0

在这个程序中,存在分段错误。程序可以成功打印出“loop end”,并且在“loop end”之后出现segmentation fault,说明read_name函数没有错误。但我无法找出我的 free_memory 函数中的任何错误。谁能帮我弄清楚?谢谢你。

输入文件:

9
Clinton, Hillary R.
Bonds, Bobby S.
Bonds, Barry L.
Clinton, William I.
Clinton, Chelsea T.
Bush, Laura M.
Bush, George W.
Bush, Jenna F.
Bush, Barbara G.

程序:

#include <stdio.h> 
#include <malloc.h>
#include<string.h>

void alloc(char ***surname, char ***first, char **mid_init, int num);
void read_names(FILE *inp, char **surname, char **first, char *mid_init, int num );
void free_memory(char **surname, char **first, char *mid_init, int num);

int main(int argc, char *argv[])
{
  int num = 0;
  char **surname, **first, *mid_init;
  FILE *inp = fopen(argv[1], "r");  
  FILE *outp = fopen(argv[2], "w");
  char array[79];

  fscanf(inp, "%d", &num);
  printf("%d\n", num);

  fgets(array, 79, inp);

  alloc(&surname, &first, &mid_init, num);
  read_names(inp, surname, first, mid_init, num);
  free_memory(surname, first, mid_init, num);

  fclose(inp);
  fclose(outp);

  return 0;
}

void alloc(char ***surname, char ***first, char **mid_init, int num)
{
  int i;

  *surname = (char**)malloc(num * sizeof(char*));
  *first = (char**)malloc(num * sizeof(char*));
  *mid_init = (char*)malloc(num * sizeof(char));

  for(i=0; i<num; i++)
  {
    (*surname)[i] = (char*)malloc(15*sizeof(char));
    (*first)[i] = (char*)malloc(10*sizeof(char));
  }
}

void read_names(FILE *inp, char **surname, char **first, char *mid_init, int num )
{
  char *token, array[79];
  char delim[6] = ", .\n";
  int i=0;

  fgets(array, 79, inp);
  printf("loop begins\n");

  for(i=0; i<num; i++)
  {
      fgets(array, 79, inp);
      printf("%s", array);

       token = strtok(array, delim);
    strcpy( (surname[i]), token);
    printf("%s   ", (surname[i]));

    token = strtok(NULL, delim);    
    strcpy( (first[i]), token);
    printf("%s  ", (first[i]));

    token = strtok(NULL, delim);
    *mid_init = token[0];
    printf("%s\n", mid_init);

    printf("\n\n");

  }
     printf("\nloop ends\n");
}

void free_memory(char **surname, char **first, char *mid_init, int num)
{
  int i;

  for(i=0;i<num;i++)
  {
    free((surname)[i]);
    free((first)[i]);
  }

  free(surname);
  free(first);
  free((mid_init));
}
4

4 回答 4

3

首先,您将自己限制为 14 个字符的名字和 9 个字符的姓氏,所以这是我要检查的第一件事,您的名字不会比这个长。

如果是这样,您在复制它们时可能会损坏内存区域。

token检查这一点的一种方法是简单地打印每次设置的长度,例如:

token = strtok(array, delim);
printf ("DEBUG: token length is %d\n", strlen (token));

请记住,腐败不一定立即或什至永远可见。在这种情况下,最有可能发生的情况是,您已经覆盖了内存领域中的重要内联控制信息,例如内存块大小或指向另一个内存块的指针。

但是,当您写入内存时,没有代码主动检查它,因此它可能仅在您下次尝试进行内存分配或取消分配调用时才被发现。

在腐败之后你的下一个电话就是你的free电话,几乎可以肯定它被发现的地方,因为竞技场是腐败的。

底线,超出分配的内存末尾写入是未定义的行为。这意味着你不应该这样做。


如果事实证明你的名字不是太长(正如你在评论中所说的那样),那么你需要问自己为什么你fgets(array, 79, inp);的代码中有多余的。我理解为什么需要在main输入行数并调用fscanf. 而那个人做得很好。

但是,您在开始时还有另一个read_names有效地丢弃了列表中的第一个名字。这会导致问题,因为虽然您的代码认为文件中有X名称,但您已经丢弃了第一个名称,这意味着只剩下X - 1剩下的了。您可以这样说,因为当您开始打印名称时,文件中的第一个名称似乎丢失了。

如果你去掉fgets开头的read_names,你应该会发现没问题。

顺便说一句,我还要对代码进行一些其他更改。首先,您确实应该检查所有这些malloc调用,以防其中一个调用失败。这是当您以后依赖它们没有失败时所有可能失败的功能的一般规则。

其次,我不太喜欢乘以sizeof(char)- 标准保证永远是1,因此乘以它会阻塞代码并使其可读性降低。

于 2013-11-13T05:48:14.823 回答
2

尝试更换

token = strtok(NULL, delim);
*mid_init = token[0];
printf("%s\n", mid_init);

token = strtok(NULL, delim);
mid_init[i] = token[0];
printf("%c\n", mid_init[i]);

当 mid_init 内存块被没有任何 null 的垃圾填充时,'printf("%s\n", mid_init);' 可能读取超出数据段导致分段错误。但是@paxdiablo 的回答更有可能成为这种情况。

@Bruce,分段错误并不总是出现在它发生的确切位置。

于 2013-11-13T06:09:29.223 回答
0

我不知道你为什么会遇到分段错误,但如果我正在写这篇文章,我会尝试让它更简单一些(我不认为你在帮自己任何忙) - 传递是没有意义的类似 char *** 姓氏。

这只是我个人的意见,但我会做这样的事情:

#include <stdio.h>
#include <malloc.h>

typedef struct {
    char **data;
    unsigned int count;
    unsigned int actualSize;
} StringArray;

void StringArray_init(StringArray *array)
{
    array->count = 0;
    array->actualSize = 0;
}

void StringArray_add(StringArray *array, char* value)
{
    if (array->actualSize == 0)
    {
        array->data = (char**)malloc(sizeof(char*)* 4);
        array->actualSize = 4;
    }
    else 
    {
        if (array->count >= array->actualSize)
        {
            array->actualSize *= 2;

            array->data = (char**)realloc(array->data,sizeof(char*) * array->actualSize);
        }
    }
    array->data[array->count] = value;
    array->count++;
}

char* StringArray_get(StringArray *array, unsigned int position)
{
    if (position < array->count)
        return array->data[position];
    else
        return 0;
}

void StringArray_clear(StringArray *array)
{
    if (array->count >0) free(array->data);
    array->count = 0;
    array->actualSize = 0;
}

int main ()
{
    StringArray surname;
    StringArray_init(&surname);
    StringArray_add(&surname, "Smith");
    StringArray_add(&surname, "Jones");

    for(int i=0;i<surname.count;i++)
    {
        printf("%s\n", StringArray_get(&surname,i));
    }

    StringArray_clear(&surname);

}

上面的代码所做的是在添加值时分配内存,但不是为一项分配空间,而是为四个添加足够的空间。如果达到添加五分之一的地步,它将使空间增加一倍,达到 8 个项目。这种方法应该有助于内存碎片。我还使用了一个结构,它只是让传递这个数组变得更容易一些。

如果我想为字符串分配内存(只包括 string.h 标头),我也可以这样做:

int main ()
{
    StringArray surname;
    StringArray_init(&surname);

    char *name = (char*)malloc(sizeof(char) * 6);
    strcpy(name,"Smith");       
    StringArray_add(&surname, name);

    name = (char*)malloc(sizeof(char) * 6);
    strcpy(name,"Jones");       
    StringArray_add(&surname, name);

    for(int i=0;i<surname.count;i++)
    {
        printf("%s\n", StringArray_get(&surname,i));
    }

    // Free memory
    for(int i=0;i<surname.count;i++)
    {
        char *name = StringArray_get(&surname,i);
        if (name != NULL) free(name);
    }
    StringArray_clear(&surname);
}

每个名称的大小为 6,因为有 5 个字符,另外一个为 0,即字符串终止符。

抱歉,如果这不能直接回答您的问题,但希望对您有所帮助。

于 2013-11-13T10:25:50.190 回答
0

布鲁斯,

在您的数据文件中,由于 read_names() 开头的第一个 fgets() 占用了第二行,因此在名称的数量和列表之间需要一个空行。因为程序跳过了第二行,所以只能读取8个名字,而最后一行读取的是空行,导致strtok返回null,下一个strcpy尝试从地址0读取,当然是一个段过错。在我的机器中,故障发生在打印“循环结束”之前。

您需要检查函数调用(在本例中为 strtok)的返回值是否存在可能的错误。否则,您将浪费大量时间进行这样的调试。

于 2013-11-13T19:39:25.473 回答