代码片段的相关部分是:
char T[2];
scanf("%s", &T);
&T
是一个指向两个字符数组的指针(char (*)[2]
)。这不是那种类型scanf
需要一个%s
说明符:它需要一个指向字符的指针(char *
)。所以程序的行为是未定义的。
如您所知,编写此程序的正确方法是
char T[2];
scanf("%s", T);
Since T
是一个数组,当它在大多数上下文中使用时,它会“衰减”为指向第一个字符的指针:T
相当于&(T[0])
其中有类型char *
。当您获取数组的地址时,不会发生这种衰减(&T
)或其大小(sizeof(T)
).
实际上,几乎所有平台都对指向同一地址的所有指针使用相同的表示形式。所以编译器会生成完全相同的代码T
and &T
。有一些罕见的平台可能会生成不同的代码(我听说过它们,但我无法说出其中一个)。某些平台对“字节指针”和“字指针”使用不同的编码,因为它们的处理器本身寻址字,而不是字节。在这样的平台上,int *
and a char *
指向同一地址的不同编码。这些类型之间的转换会转换值,但在变量参数列表之类的情况下误用会导致错误的地址。然而,我希望此类平台对 char 数组使用字节地址。还有一些罕见的平台,其中指针不仅编码数据的地址,还编码一些类型或大小信息。然而,在这样的平台上,类型和大小信息必须是相同的:它是一个 2 字节的块,从地址开始T
,并且可逐字节寻址。因此,这个特殊的错误不太可能产生任何实际影响。
请注意,如果您首先使用指针而不是数组,情况将完全不同:
char *T; // known to point to an array of two characters
scanf("%s", &T); // bad
Here &T
是指向内存中包含字符数组地址的位置的指针。所以scanf
将在指针所在的位置写入它读取的字符T
存储在内存中,而不是存储在T
指着。大多数编译器分析函数的格式字符串,例如printf
and scanf
所以会发出错误消息。
注意char T[2]
只有两个字符的空间,这包括字符串末尾的空字节。所以scanf("%s", T)
只有空间可以读取单个字符。如果此时输入包含多个非空白字符,程序将溢出缓冲区。要读取单个字符并使其成为单字符字符串,请使用
char T[2];
scanf("%c", T);
T[1] = 0;
Unlike scanf("%s", T)
,这会读取任何字符,甚至是空格。要读取有长度限制的字符串,请添加限制%s
规格。你永远不应该使用无限制的%s
in scanf
因为这将读取尽可能多的可用输入,无论内存中有多少空间可以存储该输入。
char T[2];
scanf("%1s", T); // one less than the array size