为什么1,2和3在4生成分段错误时工作? (见下文。)字符与数组c中的数组
char c[10];
char* d;
1.
scanf("%s", &c);
printf("%s\n", &c);
2.
scanf("%s", c);
printf("%s\n", c);
3.
scanf("%s", &d);
printf("%s\n", &d);
4.
scanf("%s", d);
printf("%s\n", d);
为什么1,2和3在4生成分段错误时工作? (见下文。)字符与数组c中的数组
char c[10];
char* d;
1.
scanf("%s", &c);
printf("%s\n", &c);
2.
scanf("%s", c);
printf("%s\n", c);
3.
scanf("%s", &d);
printf("%s\n", &d);
4.
scanf("%s", d);
printf("%s\n", d);
重复在问题的代码:
char c[10];
char* d;
1.
scanf("%s", &c);
printf("%s\n", &c);
这很可能按预期运行,但实际上的行为是不确定的。
scanf
与"%s"
格式需要参数类型char*
。 &c
是类型char (*)[10]
,即,它是指向char[10]
阵列的指针。它指向内存中与c
的第0个元素的地址相同的位置,但它是不同的类型。 printf
发生同样的事情:"%s"
格式告诉它期望char*
参数,但是您传递了char(*)[10]
参数。
由于scanf
是一个可变参数函数,因此没有必要的类型检查格式字符串以外的参数。假设它可以处理它,编译器将(可能)愉快地将char (*)[10]
的值传递给scanf
。在所有指针具有相同大小,表示和自变量传递机制的实现上,它可能是可能是。但是,例如,针对异国情调体系结构的C编译器很容易使指针大于指向较大类型的指针。假设一个CPU的本地地址指向一个64位的字;一个char*
指针可能由一个字指针加上一个字节偏移量组成。
2.
scanf("%s", c);
printf("%s\n", c);
这是更好的。 c
是一个数组,但在这种情况下,数组表达式“decay”指向数组的第一个元素 - 这正是格式所需的scanf
。同样的事情发生通过c
至printf
。 (但仍存在一些问题;另一个例子后,我会到达那个
3.
scanf("%s", &d);
printf("%s\n", &d);
由于d
是一个char*
说法,&d
是char**
型的,并再次,你。如果所有指针具有相同的表示形式(以及相同的参数传递机制),并且输入为scanf
的时间足够短,则这可能会发生在“工作”上,它将char*
对象视为如果它是一个数组char
。如果char*
是4个字节,并且输入字符串不再是t如果你使用了一个char[4]
并且正确地写入了这些呼叫,但是它极其不利于将字符串直接存储到指针对象中,并且存在写入对象末尾的巨大风险,结果不可预知。 (这些不可预知的结果包括写入到一个没有被用于其他任何记忆,这可能出现工作;这样是不确定的行为的性质)
(C标准给予治疗任何物体特别许可作为一组字符,但在这种情况下,这是一个非常糟糕的主意。)
4。
scanf("%s", d);
printf("%s\n", d);
这里的类型是正确的,但除非你初始化d
指向一个足够大阵的char
,它可能壮观(失败,或者更糟,出现工作“正确”,这意味着你有一个稍后可能会出现的微妙的错误)。
现在我们得到我上面提到的其他问题。
例如4,我提到d
需要指向一个“足够大”的数组。 “足够大”有多大?没有答案。 scanf("%s", ...)
读取一个空白分隔的字符序列,其长度没有上限。例如,如果我运行程序并按住x
键,则可以提供比您提供的任何缓冲区更长的输入字符串,并带有不可预知的结果(再次未定义行为)。
的scanf
功能的"%s"
格式不能安全使用(除非你的程序的环境下,你可以控制将出现什么样的标准输入流中运行)。
读取文本输入的一种好方法是使用fgets
一次读取一行,然后使用其他函数分析结果。 fgets
要求您指定输入的最大长度;如果实际输入超出限制,它将被截断并留待稍后调用读取。这不像scanf
那么方便,但它可以安全地完成。 (和从未使用gets
功能;像scanf("%s", ...)
,不能安全使用。)
推荐阅读:
的comp.lang.c FAQ的第6节做了解释C数组和指针的表现非常出色,以及它们如何'相关(而不是相关)。第12节讨论了C标准I/O。
(对不起,这个答案太长了,我没有时间让它缩短。)
你进来的情况下,3 未定义行为和4
d
未初始化。3作品(在许多平台上,并用一个警告,如果你把这些上,技术上是未定义的行为),因为你滥用指针(治疗&d
,这是(char **)
类型,作为(char *)
并存储用于指针的内存中的字符)。 4因为未初始化的指针指向一个随机地址而死亡。
这里的一个重要问题是是否有空间存储结果。
scanf("%s", &c);
printf("%s\n", &c);
是否有存储?是的,你采用的地址是数组的第一个元素。数组存在,所以你可以把结果放在那里。
scanf("%s", c);
printf("%s\n", c);
是否有存储?是。像这样使用,数组折叠成一个指针,与上面相同。
scanf("%s", &d);
printf("%s\n", &d);
是否有存储?是。它不是合适的类型(char **
,应该是char *
),但它不应该与将char转换为指针类型并将其存储在声明为指针的变量中不同。 (其他答案说这是未定义的行为,我不认为这是,将char
或任何其他整数类型投射到char *
或其他指针类型是明确的,如果不明智的话;告诉我标准说这是什么未定义。)
scanf("%s", d);
printf("%s\n", d);
是否有存储?不是你已经分配。从技术上来说,无论发生在d
中的什么都指向不会出现段错误的内存中的某个位置。即使这样做,这不是你的记忆,你可能会覆盖重要的东西,或者它可能会意外地改变。你还没有告诉d
哪里可以找到有效的记忆指向,所以你玩的指针俄罗斯轮盘赌。
第一个例子有未定义的行为,因为'scanf'需要'char *'参数,但它被赋予了一个'char(*)[10]'。 – 2012-04-15 01:43:37
@KeithThompson并不是保证崩溃的第一个元素的地址?有没有编译器? – Kevin 2012-04-15 01:45:22
没有从char(*)[10]'到'char *'的隐式转换;它保证*不*要“折叠”到第一个元素的地址。如果两个指针类型碰巧具有相同的大小,表示形式和参数传递行为(虽然优化编译器仍然可以破坏它),但它的行为仍然没有定义。尝试使用gcc编译代码。它发出警告。 – 2012-04-15 01:48:43
你觉得呢?现在看起来你试图让我们为你做功课。 – templatetypedef 2012-04-15 00:37:39
标记为功课;如果我错了,随时删除标签。 – mc10 2012-04-15 00:39:23
这不是作业。我只是准备面试,遇到这个问题时,试图制作一个产生排列的程序。 – Ivan 2012-04-15 01:04:39