2012-04-15 54 views
1

为什么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); 
+3

你觉得呢?现在看起来你试图让我们为你做功课。 – templatetypedef 2012-04-15 00:37:39

+0

标记为功课;如果我错了,随时删除标签。 – mc10 2012-04-15 00:39:23

+0

这不是作业。我只是准备面试,遇到这个问题时,试图制作一个产生排列的程序。 – Ivan 2012-04-15 01:04:39

回答

4

重复在问题的代码:

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。同样的事情发生通过cprintf。 (但仍存在一些问题;另一个例子后,我会到达那个

3.

scanf("%s", &d); 
printf("%s\n", &d); 

由于d是一个char*说法,&dchar**型的,并再次,你。如果所有指针具有相同的表示形式(以及相同的参数传递机制),并且输入为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

你进来的情况下,3 未定义行为和4

  • 情形之一的,两个是一样的,都指向数组中的第一个元素。
  • 情况3是未定义的,因为当期望指向char的指针时,您将指针指向指向char的指针。
  • 情况4未定义,因为指针d未初始化。

+0

不是情况1给出指向数组中第一个元素(即char **)而不是char *的指针吗? – Ivan 2012-04-15 01:09:32

+1

编号数组的地址('&array')是第一个元素的地址。当你刚刚写入('array')时,它会衰减到一个指针。两者都是有效的。 – MByD 2012-04-15 01:10:44

+1

@BinyaminSharet:不,两者都无效。 '&array'是整个数组的地址。它指的是与&array [0]相同的内存地址,但它是不同类型的。它可能适用于所有指针具有相同表示的系统,但行为未定义。 – 2012-04-15 01:42:10

2

3作品(在许多平台上,并用一个警告,如果你把这些上,技术上是未定义的行为),因为你滥用指针(治疗&d,这是(char **)类型,作为(char *)并存储用于指针的内存中的字符)。 4因为未初始化的指针指向一个随机地址而死亡。

0

这里的一个重要问题是是否有空间存储结果。

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哪里可以找到有效的记忆指向,所以你玩的指针俄罗斯轮盘赌。

+0

第一个例子有未定义的行为,因为'scanf'需要'char *'参数,但它被赋予了一个'char(*)[10]'。 – 2012-04-15 01:43:37

+0

@KeithThompson并不是保证崩溃的第一个元素的地址?有没有编译器? – Kevin 2012-04-15 01:45:22

+0

没有从char(*)[10]'到'char *'的隐式转换;它保证*不*要“折叠”到第一个元素的地址。如果两个指针类型碰巧具有相同的大小,表示形式和参数传递行为(虽然优化编译器仍然可以破坏它),但它的行为仍然没有定义。尝试使用gcc编译代码。它发出警告。 – 2012-04-15 01:48:43