2014-09-27 64 views
0

我想了解C编程中的指针和字符数组。 我有一个名为球员结构定义为这样:C解引用指针中的分段错误

typedef struct player{ 
    char* name; 
    float ppm; 
} player; 

而且在主要我有以下几点:

int main() 
{ 
    player* head = (player*) malloc(sizeof(player)); 
    char* meName = &(head->name); 
    (*head).name = "potato"; 
    (*head).name = "Paul"; 
    (*head).ppg =7.6; 
    //printf("player is named %s\n", *meName); //First print 
    printf("player is named %s\n", meName); //second print 
    printf("player is named %s\n", (*head).name); //third print 
    return 0; 
} 

为什么我的第一次印刷导致段错误,为什么做我的其他两个印记输出如下:

player is named [email protected] 
player is named Paul 

如果我没有记错meName应指向,然后由使用另一个指针改变的存储地址到相同的内存地址。事实上,如果我将其打印在第二张照片中,则会显示该地址。当我清楚地将变量设置为指向刚刚被修改的内存空间时,为什么取消引用会导致segFault?为什么它不会更改为Paul?为什么它会首先抛出段错误?

此外,我会很感激解释箭头 - >与*用于取消引用指针。

+0

[不投出malloc(和朋友)](http://stackoverflow.com/q/605845)的结果。另外,'(* head).name'等价于更好的'head-> name'。这个表达式中的括号是混乱的:'&(head-> name)'。无论如何,'meName'具有'char *'类型,而不是'char **'。我的建议:用完整的警告编译你的代码,并全部留意。 – Deduplicator 2014-09-27 15:53:48

回答

1

首先关闭所有你用printfs使用%S码。这意味着第二个参数将被解释为指向表示字符的字节(即地址)的指针。 Printf将打印该字节的值(作为字符),然后打印存储器中下一个字节的值,依此类推,直到遇到一个值为0的字节。

我会将您的打印语句一次。

printf("player is named %s\n", *meName); 

*meName指在位置meName的字符。这个字符将是一个介于-128和+127之间的整数。 (实际上它是107--见下文)现在你使用这个数字107,就好像它是一个以空字符结尾的字符串(的第一个字符)的地址一样。机会是这个号码不是有效地址,所以当printf试图读取该地址(107)处的字符时,它不在你的进程的内存范围内,因此你会得到分段错误。 (通常是分段错误意味着你已经尝试过在你的进程不允许访问的地址读取或写入内存。)

printf("player is named %s\n", meName); 

这是更好,因为meName至少是一个指针。那么它指向什么。初始化

meName = &(head->name); 

意味着meName是(即指向)的字段head->name地址。请注意,这实际上并不是一个字符,它是一个指向字符的指针。那么会发生什么printf打印head->name的第一个字节就好像它是一个字符。它恰巧打印为k,因此第一个字节是107(k的ascii号码)。然后它会打印指针的第二个字节,依此类推,直到遇到一个字节为00000000的字节为止。为什么编译器接受初始化并不清楚,因为左侧的类型是“指向字符的指针”,而在右边的类型是“指向字符的指针的指针”。它应该产生至少一个警告错误。

三printf的

printf("player is named %s\n", (*head).name); 

这看起来在地址head和发现的结构。该结构的name字段的值是五字节序列的第一个字节的地址'P''a''u''l''l' 0. printf在该指针值后面找到'P'(它将打印),然后它增加指针并遵循它再次找到'a',依此类推,直到找到0字节,并停止。

请注意,因为printf有点特殊,它的参数不是由编译器检查类型。这就是为什么即使第二个参数的类型不是char*,因为%s指令指示它应该是第一个printf。

+0

我忘了解决'head-> name'与'(* head).name'的用法。他们的意思是*完全一样的东西*。大多数人更喜欢'head-> name',因为它稍微容易阅读,并且输入起来稍微容易一些。 – 2014-09-27 16:40:01

+0

这很有道理。所以当我想要声明一个新的“字符串”时,等号左边的项应该是_pointer_,而右边的项应该是实际的字符集?所以如果我说char数组[] =“保罗”那么数组现在是指向字符'P'的内存地址的指针。是? – farid99 2014-09-27 16:44:42

+0

不完全。在大多数情况下,像“Paul”这样的字符串就代表了一个字符数组。但是在C中,一个字符数组很容易转换为一个char指针。在某种意义上,你的赋值'(* head).name =“Paul”对于'(* head).name =&(“Paul”)[0]'来说是简短的,这意味着第一项那5个字符数组被分配给指针'head-> name'。但是有一个例外;字符串文字在用于初始化数组时具有完全不同的含义:当您声明'char array [] =“Paul”;'时,这是'char array [5] = {'P','a','的u”, 'L',0};'。 – 2014-09-27 17:56:53

3

这是不正确的,你应该得到一个警告:

char* meName = &(head->name); 

meName是一个指针的指针char,不是简单的一个指针char。这里是你如何解决这个问题:

当然
char** meName = &(head->name); 

印刷应该是这样的 - 即你已经注释掉的方式:

printf("player is named %s\n", *meName); 
+0

所以,当我做_ **'head-> name' ** _时,这是返回一个指向字符串“name”的指针,当我通过使用&符号来获取地址时,我得到一个指向指针的指针。这是怎么回事?在那种情况下,为什么不_ **'char * meName = head-> name;'** _ compile要么?在这种情况下,_ **'meName'** _是一个指向char的指针,并且_ **'head-> name' ** _也是一个指向_ **'char' ** _的指针,如果我我没有错。 – farid99 2014-09-27 16:07:30

+0

我并不是故意要用双星号。评论的格式不在我身上。 – farid99 2014-09-27 16:13:32

+0

@ mufasa56' - >'运算符引用'head',给你一个指向'char'的指针。 “接收地址”&'操作符使其成为指向char的指针。 – dasblinkenlight 2014-09-27 16:18:36

0
int main() 
{ 
    player* head = (player*) malloc(sizeof(player)); 
    char* meName = &(head->name); 
    (*head).name = "potato"; 
    (*head).name = "Paul"; 
    (*head).ppg =7.6; 
    //printf("player is named %s\n", *meName); //First print 
    printf("player is named %s\n", meName); //second print 
    printf("player is named %s\n", (*head).name); //third print 
    return 0; 
} 

此代码有严重的错误类型(其中有些会被编译器,如果你使用适当的警告级别被抓),并且不使用的最佳实践(和也因错别字不会编译)。对比它

int main(void) 
{ 
    player* head = malloc(sizeof *head); 
    char** pname = &head->name; 

    head->name = "potato"; 
    head->name = "Paul"; 
    head->ppm = 7.6; 

    printf("address of player name is %p\n", pname); 
    printf("player is named %s\n", *pname); 
    printf("ditto: %s\n", head->name); 

    return 0; 
}