【问题标题】:Char Pointer Segmentation Fault字符指针分段错误
【发布时间】:2012-04-17 05:39:03
【问题描述】:

好的,这里是正在发生的事情的要点:

我将一个字符数组(char[x]) 传递给一个函数,该函数的参数被定义为一个字符指针(char *)。一旦在函数内部,我分配了另一个字符指针(这是我拥有的结构的一部分)。一旦我将传入参数分配给结构的字符指针,就会出现分段错误;像这样。

temp->name = (char*)malloc(sizeof(char));
temp->name = name;

这就是我之前使用该功能的方式:

char *name = "HEY";
Function(name);

以下是我在错误中使用它的方式:

char name[3] = "HEY";
Function(name);

使用上面相同的语句,它工作正常。通过将名称更改为常量“嘿”,使用相同的输入,一切顺利,我确保它不是其他任何东西。

如果有人能想到一个原因,我将非常感谢您的帮助。谢谢!

完整的功能如下:

  • openList 是指向结构链表开头的指针
  • tempOpen 是一个临时指针,我们可以利用它来搜索列表,而无需更改 openList 的位置
  • findOpenSetSID/findItem -> 通过 SID/key 在链表中查找结构
  • answerOpen/answerItem -> 1 == 第一个节点,2 == 任何其他节点,0 = 未找到

以下是所涉及结构的简要摘要。 开放结构是指向另一个结构(称为集合结构)的指针的链表 集合结构是名称、sid 和项结构的链表 项目结构是数据和键的链表

Error_t WRITE(Sid_t sid, char *key, char *data){

 Open_p_t tempOpen = openList;      //setting a pointer to a struct 
 int answerOpen = findOpenSetSID(sid, &tempOpen);   
 if(answerOpen > 0){
    Set_p_t targetNode;             
    if(answerOpen == 1){
        targetNode = tempOpen->file;        
    }
    else{
        targetNode= tempOpen->next->file;   
    }
    Item_p_t tempItem = targetNode->items;      
    int answerItem = findItem(key, &tempItem);  
    Item_p_t targetItem;                
    targetItem = (Item_p_t)malloc(sizeof(Item_t));
    if(answerItem > 0){
        if(answerItem == 1){
            targetItem = targetNode->items;
        }
        else{
            targetItem = targetNode->items->next;
        }
        targetItem->data = data;        
    }
    else{
        **targetItem->data = data;**      <<<The problem line.
                                                      basically I am just adding   
                                                      items to my sets. But this line 
                                                      freaks out when the input changes 
                                                      from char* to char[]
        targetItem->key = key;

        targetItem->next = targetNode->items;
        targetNode->items = targetItem;
    }
    return 0;
}
return 1;
}

这是输入段:

char key[32], data[64]; // reads in data using fscanf(fd, "%s %s", key data) then calls WRITE(setId, key, data);

【问题讨论】:

  • 请显示演示问题的代码。描述往往令人困惑或误导。
  • 我不知道您为什么会遇到段错误(您没有显示足够的代码来猜测),但是您对 temp-&gt;name 的双重分配揭示了对 C 内存管理的根本误解。
  • 看起来你应该使用 strcpy 而不是 temp-&gt;name = name; (尽管 1 个字符没有意义),除非你实际上是在尝试复制指针而不是字符串(在这种情况下你'可能造成了内存泄漏)。
  • 永远不要对 malloc 的结果进行类型转换。阅读thisthis

标签: c arrays string pointers


【解决方案1】:

首先,这两行:

temp->name = (char*)malloc(sizeof(char));
temp->name = name;

顶行没用,会导致内存泄漏。

其次,这两行:

char *name = "HEY";
char name[3] = "HEY";

接近但不相同。第一个结果是 name 指向一个 4 字节的数据块,最后是字符串 "HEY" 和一个空终止符(值 0'\0')。第二个导致 name 指向一个 3 字节的内存块,其中字节为 "HEY" 并且没有空终止符。

如果您的函数假定它正在获取一个以 null 结尾的字符串(很可能),那么第二个变体可能会导致段错误。

【讨论】:

  • 知道了,谢谢,我不确定何时使用 malloc。特别是对于其中包含 char* 的结构。如何将 char* 分配给 char name[x]?有没有办法在数组中追加一个 NULL 字节?
  • 实际上想要实现什么? malloc 从堆中分配内存。你需要free 记忆你malloc'd。像“abc”这样的字符串文字位于只读内存中,就像代码一样。您可以使用const char * 阅读它们,但不能修改它们的内容。 char c[100] 是数组,而不是指针。您可以使用指针into 这样的数组。这种类型的自动变量驻留在堆栈中,而这种类型的结构字段驻留在结构中,无论它在哪里:堆栈或堆(malloc'd 内存)或只读内存(静态 const 变量)。告诉我们你真正想要什么。
  • 我想我需要为任何指针分配分配内存空间。我想我正在混淆如何为指向指针的指针分配内存......我真的只想将参数中的这个输入(char [x])(定义为char *)分配给我结构中的另一个变量(char *)
  • 您不能在 C 中“分配”数组。如果要将字节从只读(如字符串文字)复制到可修改的内存中,则需要使用 strcpy 或 memcpy(取决于您是否知道长度)。必须已分配目标以保存您希望复制到其中的数据。
  • 如果你有一个struct string_t { const char *c; int length; }; 并且你有一个struct string_t 类型的变量s,这个变量有足够的空间容纳一个指针和一个int。您可以分配一个指向s.c 的指针:struct string_t s; s.c = "abc"; s 在 32 位架构上占用 8 个字节,驻留在堆栈上,可修改并且在超出范围时不再存在。 c指向的内容是只读的。
【解决方案2】:

从片段中不确定temp-&gt;name 是如何声明的,但问题可能就在这里;

 name = (char*)malloc(sizeof(char));

由于 char 的大小只有一个字节,并且根据您要存储的内容,您需要一个完整指针(4 或 8 个字节)的空间,或者如果您希望将内容复制到一个字符序列中分配空间;

所以

 name = (char*)malloc(sizeof(char *));

或者

 name = (char*)malloc(sizeof(char) * 80 ); // for 80 bytes char array

【讨论】:

    【解决方案3】:

    所以让我们从头开始。您从文件中读取字符串的键、数据对。您按照阅读它们的顺序构建这些对的链接列表。然后呢?

    /*
    compile with:
    gcc -std=c89 -pedantic -Wall -O2 -o so_10185705 so_10185705.c
    test with:
    valgrind ./so_10185705
    */
    #include <stdlib.h>
    #include <string.h>
    #include <stdio.h>
    
    struct node_t {
      char key[32];
      char value[64];
      struct node_t *next;
    };
    
    static void print_list(const struct node_t *curr)
    {
      while (curr) {
        printf("%s, %s\n", curr->key, curr->value);
        curr = curr->next;
      }
    }
    
    static void free_list(struct node_t **currp)
    {
      struct node_t *curr = *currp;
      *currp = NULL; /* don't leave dangling pointers */
      while (curr) {
        struct node_t *next = curr->next;
        curr->next = NULL;
        free((void *)curr);
        curr = next;
      }
    }
    
    /* O(n) but useful */
    static const struct node_t *
    find_in_list(const struct node_t *curr, const char *key)
    {
      while (curr) {
        if (!strncmp(curr->key, key, sizeof(curr->key)))
          return curr;
        curr = curr->next;
      }
      return NULL;
    }
    
    /* Same as find_in_list but less useful */
    static int is_in_list(const struct node_t *curr, const char *key)
    {
      while (curr) {
        if (!strncmp(curr->key, key, sizeof(curr->key)))
          return 1;
        curr = curr->next;
      }
      return 0;
    }
    
    int main()
    {
      struct node_t *head = NULL;
      FILE *f = fopen("foo", "r");
      if (!f) exit(1);
      while (!feof(f)) {
        struct node_t *new_node = malloc(sizeof(struct node_t));
        fscanf(f, "%s %s", new_node->key, new_node->value);
        new_node->next = head;
        head = new_node;
      }
      fclose(f);
      print_list(head);
      const struct node_t *node = find_in_list(head, "abcd2");
      if (node) {
        printf("found! key = %s, value = %s\n", node->key, node->value);
      } else {
        printf("not found!\n");
      }
      if (is_in_list(head, "abcd3")) {
        printf("found key in list but now I don't know the value associated with this key.\n");
      } else {
        printf("not found!\n");
      }
      free_list(&head);
      return 0;
    }
    
    /* explanation of bugs in OP's code */
    
    struct node_t_2 {
      char *key;
      char *value;
      struct node_t_2 *next;
    };
    
    void build_list(FILE *f, struct node_t_2 *curr)
    {
      while (!feof(f)) {
        /*
        These variable are allocated on the stack.
        Their lifetime is limited to the current scope.
        At the closing curly brace of the block in which they are declared,
        they die, the information in them is lost and pointer to them become
        invalid garbage.
        */
        key char[32];
        value char[64];
        /*
        Of course, you can only read keys up to 31 bytes long and values up to 63 bytes long.
        Because you need an extra byte for the string's NUL terminator.
        fscanf puts that NUL terminator for you.
        If it didn't, you would not be able to use the data:
         you would not know the lenth of the string.
        If you need 32-byte long keys, declare the variable key to be 33 bytes long.
        */
        fscanf(f, "%s %s", key, value);
        /* You can use key and value here */
        struct node_t_2 bad_new_node;
        /*
        You cannot add bad_new_node to the list, because as soon as you
        reach '}' it will die.
        You need a place for the node that will not disappear
         (be reused on the next iteration of the loop).
        So it must be on the heap.
        How many bytes do you need for the node? Enough to hold the three pointers:
         12 bytes on 32bit, 24 bytes on 64bit.
        The compiler knows how many bytes a struct needs.
        */
        struct node_t_2 *new_node = malloc(sizeof(struct node_t_2));
        /*
        You can add new_node to the list, because it is on the heap and will
         exist until either passed to free() or the process (program) exits.
        */
        new_node->key = key;
        new_node->value = value;
        /*
        That was a bug, because we stored pointers to garbage.
        Now new_node has a pointer to a place that will cease to exist
        as soon as we reach '}'.
        */
        new_node->key = strdup(key);
        new_node->value = strdup(value);
        /*
        strdup is a standard function that can be implemented like this:
        char * strdup(const char *s)
        {
          int len = strlen(s)
          char *p = malloc(len);
          memcpy(p, s, len);
          return p;
        }
        Now new_node has pointers to memory on the heap that will continue to
        exist until passed to free or the process terminates.
        */
        new_node->next = curr;
        curr = new_node;
        /*
        At the next line, memory for key and value is disappears and
         is re-used if we re-enter the loop.
        */
      }
    }
    
    /*
    If your list nodes have pointers instead of arrays, you need to free
    the strings pointed to by those pointers manually, becuause freeing
    the list node wont free stuff it points to.
    */
    free_list(struct node_t_2 **currp)
    {
      struct node_t_2 *curr = *currp;
      *currp = NULL;
      while (curr) {
        struct node_t_2 *next = curr->next;
        free((void *)curr->key);
        curr->key = NULL;
        free((void *)curr->value);
        curr->value = NULL;
        curr->next = NULL;
        free((void *)curr);
        curr = next;
      }
    }
    

    【讨论】:

    • 我遇到的问题是部分阅读和列出清单。 fscanf(f, "%s %s", new_node->key, new_node->value); //就在这里而不是这个,它更像是 char key[32], data[64]; fscanf(f, "%s %s", 键, 数据); //然后在其他地方的函数中,我通过分配 func(char key, char *data){ new_node->key = key; 创建节点但是我按照建议尝试了 strcpy,现在正在这样做。输入仍然是字符键[32]; func(char key, char* data) new_node->key = (char*)malloc(sizeof(char)); strcpy(new_node->key, key);
    • 你做错了,因为你不明白什么是内存管理,什么是栈和堆。将此问题标记为作业。
    • malloc(sizeof(char)) 几乎没有任何理由。为什么要分配一个字节的内存?您从文件中读取 32 个字节,然后将 32 个字节复制到可以容纳 1 个字节的空间中。为什么你认为这会奏效? “给我一个能装 1 升水的瓶子。现在把 32 升水倒进这个瓶子里”。你明白吗?你把超过 31 升的水洒在地板上。它溢出了单个瓶子周围的东西。然后你的程序崩溃了,因为你用垃圾覆盖了指针。
    猜你喜欢
    • 1970-01-01
    • 2011-07-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多