如果在scanf函数中对字符串使用“&”会怎样?

时间:2018-11-16 05:54:17

标签: c string scanf

我刚刚在博客中看到了一些代码。 它用

std::merge

但是我们知道,我们不应该在字符串中使用&符,因为它会自动分配该字符串的第一个地址。我确实运行了该代码,并且令人惊讶的是它正在运行,所以我想知道当我在字符串中使用scanf("%s",&T); 时会发生什么?

&

2 个答案:

答案 0 :(得分:4)

从技术上讲,这是类型不匹配,导致undefined behavior。对于扫描 string ,期望的参数是指向字符数组初始元素的指针。

当您说类型为t的数组char[somevalue]时,

scanf("%s",t);

t衰减到指向第一个元素的指针,所以没关系。

另一方面,当您说&t时,它的类型为char (*)[somevalue]-指向数组(整个数组)的指针,而不是指向数组初始元素的指针。

现在,由于数组的地址和数组的第一个元素的地址相同(内存位置),因此,将扫描的值写入提供的地址可能不会导致任何结果。问题并按预期工作-但这既未定义也未建议。

答案 1 :(得分:3)

代码段的相关部分是:

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指向的位置。大多数编译器会分析诸如printfscanf之类的函数的格式字符串,因此会发出错误消息。

请注意,char T[2]仅可容纳两个字符,并且在字符串末尾包括空字节。因此scanf("%s", T)仅具有读取单个字符的空间。如果此时输入包含多个非空白字符,则程序将使缓冲区溢出。要读取单个字符并使其成为一个单字符字符串,请使用

char T[2];
scanf("%c", T);
T[1] = 0;

scanf("%s", T)不同,这会读取任何字符,甚至是空格。要读取具有长度限制的字符串,请在%s规范中添加一个限制。您永远不要在%s中使用无限制的scanf,因为这将读取尽可能多的输入,而不管将此输入存储在内存中的空间如何。

char T[2];
scanf("%1s", T); // one less than the array size