#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <string.h>
struct BOOK{
char name[15];
char author[33];
int year;
};
struct BOOK *books;
int main(){
int i,noBooks;
noBooks=2;
books=malloc(sizeof(struct BOOK)*noBooks);
books[0].year=1986;
strcpy(books[0].name,"MartinEden");
strcpy(books[0].author,"JackLondon");
//asking user to give values
scanf("%d",&books[1].year);
scanf("%s",&books[1].name);
scanf("%s",books[1].author);
printf("%d %s %s\n",books[0].year,books[0].author,books[0].name);
printf("%d %s %s\n",books[1].year,books[1].author,books[1].name);
getch();
return 0;
}
我提供1988 theidiot
和dostoyevski
输出
1986 JackLondon MartinEden
1988 dostoyevski theidiot
在scanf
,books[].name
我使用&
,在books[].author
我没有使用,但它仍然使用year
。对于&
,它不起作用。 scanf("%d",&books[1].year);
scanf("%s",&books[1].name);
scanf("%s",books[1].author); //no & operator
char name[15];
char author[33];
在结构上没用?
我的意思是
char *name[15];
char *author[33];
在这里,我可以使用
{{1}}没有什么变化。为什么我看不出差异?
答案 0 :(得分:1)
name
结构的BOOK
成员是一个大小为15的char
数组。当在表达式中使用数组的名称时,其值是数组的地址初始元素。
但是,当您从name
获取struct BOOK
成员的地址时,编译器将返回struct
的基址以及name
成员的偏移量,这与name
的初始元素的地址完全相同。这就是&books[1].name
和books[1].name
表达式评估为相同值的原因。
注意:您应该指定要读取字符串的缓冲区的大小;这样可以防止潜在的缓冲区溢出:
scanf("%14s", books[1].name);
scanf("%32s", books[1].author);
答案 1 :(得分:0)
此表格有效:
scanf("%s",books[1].author);
此表单无效:
scanf("%s", &books[1].author);
s
转换说明符期望在char
函数中指向scanf
的类型指针的参数,在第一个语句中为true,在第二个语句中为false。如果不满足此要求,则会调用函数调用未定义的行为。
在第一个语句中,尾随参数(转换后)是指向char
的类型指针,在第二个语句中,参数是指向33
的数组char
的类型指针}。
答案 2 :(得分:0)
除非它是sizeof
或一元&
运算符的操作数,或者是用于在声明中初始化另一个数组的字符串文字,否则表达式为“N-element array of T
“将被转换(”衰减“)为”指向T
的指针“类型的表达式,表达式的值将是数组中第一个元素的地址。
写作时
scanf("%s", books[1].author);
表达式 books[i].author
的类型为“{-1}}的33个元素数组”。根据上面的规则,它将被转换为“指向char
”(char
)类型的表达式,表达式的值将是数组的第一个元素的地址。
写作时
char *
表达式scanf("%s", &books[1].name);
是一元books[1].name
运算符的操作数,因此转换不会发生;相反,表达式&
的类型具有类型“指向&books[1].name
的15个元素数组的指针”(char
),其值是数组的地址。
在C中,数组的地址和数组的第一个元素的地址是相同的,因此两个表达式都会产生相同的值;但是,这两个表达式的类型是不同的,类型总是很重要。 char (*)[15]
期望与scanf
转换说明符对应的参数具有类型%s
;通过传递类型char *
的参数,您可以调用未定义的行为,这意味着编译器不需要警告您类型不匹配,也不需要处理任何有意义的表达式办法。在这种特殊情况下,代码“有效”(给出您期望的结果),但不需要;它可能很容易导致崩溃,或导致数据损坏,具体取决于char (*)[15]
的具体实现。
两次调用都应写为
scanf
修改强>
在回答您的评论时,图片可能有所帮助。这是您的scanf("%s", books[1].name);
scanf("%s", books[1].author);
数组的样子:
books
+---+ +---+
| | | name[0]
| +---+
| | | name[1]
| +---+
| ...
| +---+
| | | name[14]
| +---+
books[0] | | author[0]
| +---+
| | | author[1]
| +---+
| ...
| +---+
| | | author[33]
| +---+
| | | year
+---+ +---+
| | | name[0] <------ books[1].name
| +---+
| | | name[1]
| +---+
| ...
| +---+
| | | name[14]
| +---+
books[1] | | author[0] <------ books[1].author
| +---+
| | | author[1]
| +---+
| ...
| +---+
| | | author[33]
| +---+
| | | year
+---+ +---+
数组的每个元素都包含两个数组和一个整数。 books
评估books[1].name
内name
数组的第一个元素的地址;类似地,表达式books[1]
计算books[1].author
内author
数组的第一个元素的地址。