用指针指向C中的分段错误

时间:2015-10-16 18:17:10

标签: c segmentation-fault

为什么以下代码会终止分段错误 为什么备用版本即注释代码,不?这两个版本的代码看起来和我一样。我错过了什么?

L*     a*     b*       color
80     25     -30       NA
75     55     55        NA
30     0      25        NA
10     -20    30        NA
55     15     20        NA
60     43     18        NA
...  ...

3 个答案:

答案 0 :(得分:5)

name是一个字符数组。它的类型是char[20]

在某些情况下,数组会衰减为指针。这不是其中之一。

C标准特别提到地址运算符的参数不会衰减。将address-of运算符应用于数组名称的结果不出所料是数组的地址。

在这种情况下,&name的类型为char (*)[20]。此类型与char**非常不同。前者描述了一个指向包含20个字符的内存位置的指针。后者描述了指向存储器位置的指针,该存储器位置包含指向包含字符的另一个存储器位置的指针。你不能把一个投射到另一个并希望它能起作用。

答案 1 :(得分:1)

char **是一个指向指针的指针。当你传递数组的地址时,它的类型char (*)[20]char**类型的参数不兼容。这是你怎么做的纠正代码:

#include <stdio.h>

void get_input(char* m); // void get_input(char*);

int main(void)
{
    char name[20];
    get_input(name); //get_input(name);
    printf("%s", name);

}

void get_input(char* m)//get_input(char* m)
{
    scanf("%s", m); // scanf("%s", m);
}

答案 2 :(得分:1)

n.m.的回答是正确的。但是如果你想知道问题发生在哪里&#34;引擎盖下&#34;,请看看下面的代码:

#include <stdio.h>
 void get_input(char**);

 int main(void)
 {
   char name[20];
   char* pname = name;
   char** ppname = (char**)&name; //this is what you were passing to get_input
   printf("Address of name: %d\n", name);
   printf("Value of pname: %d\n", pname);
   printf("Value of &name: %d\n", &name); 
   printf("Value of ppname: %d\n", ppname);
   get_input(ppname); 
   printf("Input: %s\n", name);
}

 void get_input(char** ppinput)
 {
   char* pinput = *ppinput;
   printf("Value of ppinput: %d\n", ppinput);
   printf("Value of pinput: %d\n", pinput);
   // The next line of code causes SEGMENTATION FAULT because 
   //   pinput is the value of name[0], which is garbage,
   //   so you don't own the memory it points to.
   scanf("%s", pinput); 
 }

如果编译并运行它,您将看到与此类似的输出:

Address of name: 2358816
Value of pname: 2358816
Value of &name: 2358816
Value of ppname: 2358816
Value of ppinput: 2358816
Value of pinput: 1
Segmentation fault

查看name的地址,并将其与pname(指向name的指针)的值和ppname的值(已定义)进行比较作为一个char **)。 OP可能期望&name将返回指向pname的指针(即&name返回指向指向数组中第一个char的指针的指针)。但是,你可以看到pname和ppname是一样的!这是因为编译器将&name解释为指向数组的指针,它偶然与数组中的第一个字符(pname指向的地址)位于同一地址。

get_input功能实际上非常好。如果ppinput(OP称为&#34; m&#34;)确实是指向char指针的指针,则该函数将按预期工作。 char**将被char*取消引用,而scanf会毫无问题地填充它。

但是如上所示,ppname实际上是指向数组的指针,它与指向该数组的第一个元素的指针相同。因此,ppname实际上与pname相同。因此,在OP的代码中,他实际上传递的值实际上是char*get_input,而不是char**

然后,当get_input解除引用ppinput时,它会获得char[]数组中第一个字符的值(我的输出中恰好是1而是指向该值的指针,这是scanf所期望的。

OP可以通过简单地改变行(从我的代码示例)来完成他想要在他的问题中做的事情:

char** ppname = (char**)&name;

char** ppname = &pname;

现在这个ppname真正 IS 指向指针的指针,这是OP期望的&name。完成更改后,您确实会将char**传递给get_input,该功能将按预期工作。

我希望能够更清楚地说明这个问题。 n.m.已经提到了重要的教条点,但是从中可以得出一个实际的注意事项是对数组的引用返回与指向第一个元素的指针相同的值。即 当(int)&name被声明为数组时,(int)name(非直观地)与name相同。我说&#34;不直观&#34;因为如果你不熟悉c ++数组,你可能会期望&var总是返回一个不同于var的值,但是正如这个例子所示,对于数组来说,结果并非如此。

(请注意,上面我已经使用int作为指针值,同样%d用于printf。这在便携性方面是不好的做法,但是对于这个例子它应该工作并获得这一点。)