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