free()导致分段错误

时间:2013-11-13 05:42:22

标签: c

在此程序中,存在分段错误。程序可以成功打印出“循环结束”,并在“循环结束”后出现分段错误,这意味着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 个答案:

答案 0 :(得分: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保证这一点,所以乘以它会堵塞代码并使其减少可读的。

答案 1 :(得分: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内存块中充满了没有任何空值的垃圾时,'printf(“%s \ n”,mid_init);'可能会超出数据段读取导致分段错误。 但@ paxdiablo的答案在这里有更好的机会。

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

答案 2 :(得分: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);

}

上面的代码正在做的是在添加值时分配内存,但不是为一个项目分配空间,而是为4个增加了足够的空间。如果你到达添加第五个的点,它会将空间加倍到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,这是字符串终止符。

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

答案 3 :(得分:0)

布鲁斯,

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

您需要检查函数调用的返回值(在本例中为strtok)以查找可能的错误。否则你会浪费很多时间来调试这个。