字符串数组访问错误

时间:2012-11-11 06:50:43

标签: c arrays string memory allocation

当我首先打印char **姓和char **时,我得到一些奇怪的输出。我不确定我是否正确地使用了malloc,或者我是否正在做其他不正确的事情。

输入 - > names1.txt

The outputs

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

int main ()
{
  int size, i;
  char **surname, **first, *middle_init, dummy, str[80];
  FILE *fp_input = fopen("names1.txt", "r");

  fscanf(fp_input, "%d%c", &size, &dummy); // gets size of array from file

  /* dynamic memory allocation */

  middle_init = (char*)malloc(size * sizeof(char));
  surname = (char**)malloc(size * sizeof(char*));
  first = (char**)malloc(size * sizeof(char*));

  for (i = 0; i < size; i++)
  {
    surname[i] = (char*)malloc(17 * sizeof(char));
    first[i] = (char*)malloc(17 * sizeof(char));  
  } // for

  /* reads from file and assigns value to arrays */

  i = 0;
  strcpy(middle_init, "");

  while (fgets(str, 80, fp_input) != NULL)
  { 
    surname[i] = strtok(str, ", \n");
    first[i] = strtok(NULL, ". ");
    strcat(middle_init, strtok(NULL, ". "));
    i++;
  } // while

  /* prints arrays */      

  for (i = 0; i < size; i++)
        printf("%s %s\n", surname[i], first[i]);

  return 0;
} // main

2 个答案:

答案 0 :(得分:1)

随便看看代码表明:

  • 您必须在主题上使用strcpy()或变体将strtok()找到的字符串复制到姓氏等中

  • 你写的方式,你扔掉你分配的记忆。

  • 您得到重复输出,因为您存储指向用于保存surnamefirst数组中的行的字符串的指针。执行打印时,该字符串仅保留最后一行。这和前一点是第一点的推论。

  • 您只为中间首字母分配一个字符。然后使用strcat()将它们视为字符串。我建议将中间名首字母作为字符串处理,就像其他名字一样。或者,由于您不需要打印它们,您可能会决定完全忽略中间名首字母。

  • 使用17代替enum { NAME_LENGTH = 17 };或同等代码不是一个好主意。

毫无疑问还有其他问题。

我猜你还没有达到你学习课程的结构。如果您有覆盖结构,则应该使用结构类型来表示完整名称,并使用单个数组而不是并行数组。这也可能会简化内存管理;你在结构中使用固定大小的数组元素,所以你只需要为每个名称分配一个。

以下代码生成输出:

Ryan Elizabeth
McIntyre O
Cauble-Chantrenne Kristin
Larson Lois
Thorpe Trinity
Ruiz Pedro

在此代码中,err_exit()函数非常有价值,因为它将错误报告转换为单行调用,而不是4行段落,这意味着您更有可能进行错误检查。它是可变长度参数列表的基本用法,您可能还不了解它,但它非常方便和强大。唯一可以进行错误检查但不是fclose()printf()的函数。如果您正在阅读文件,那么检查fclose()几乎没有什么好处;如果您正在编写并且fclose()失败,则可能是磁盘空间不足或类似的情况,并且可能适合报告错误。如果您希望更多地改进错误报告,可以将<errno.h>添加到标题列表中,并在errnostrerror(errno)上进行报告。代码释放分配的内存; valgrind给它一个清洁的健康状况。

#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static void err_exit(const char *fmt, ...);

int main(void)
{
    enum { NAME_SIZE = 25 };
    const char *file = "names1.txt";
    int size, i;
    char **surname, **first, str[80];
    FILE *fp_input = fopen(file, "r");

    if (fp_input == NULL)
        err_exit("Failed to open file %s\n", file);
    if (fgets(str, sizeof(str), fp_input) == 0)
        err_exit("Unexpected EOF on file %s\n", file);
    if (sscanf(str, "%d", &size) != 1)
        err_exit("Did not find integer in line: %s\n", str);
    if (size <= 0 || size > 1000)
        err_exit("Integer %d out of range 1..1000\n", size);

    if ((surname = (char**)malloc(size * sizeof(char*))) == 0 ||
        (first = (char**)malloc(size * sizeof(char*))) == 0)
        err_exit("Memory allocation failure\n");
    for (i = 0; i < size; i++)
    {
        if ((surname[i] = (char*)malloc(NAME_SIZE * sizeof(char))) == 0 ||
            (first[i] = (char*)malloc(NAME_SIZE * sizeof(char))) == 0)
            err_exit("Memory allocation failure\n");
    }

    for (i = 0; i < size && fgets(str, sizeof(str), fp_input) != NULL; i++)
    { 
        char *tok_s = strtok(str, ",. \n");
        char *tok_f = strtok(NULL, ". ");
        if (tok_s == 0 || tok_f == 0)
            err_exit("Failed to read surname and first name from: %s\n", str);
        if (strlen(tok_s) >= NAME_SIZE || strlen(tok_f) >= NAME_SIZE)
            err_exit("Name(s) %s and %s are too long (max %d)\n", tok_s, tok_f, NAME_SIZE-1);
        strcpy(surname[i], tok_s);
        strcpy(first[i], tok_f);
    }
    if (i != size)
        err_exit("Only read %d names\n", i);

    fclose(fp_input);

    /* prints arrays */      
    for (i = 0; i < size; i++)
        printf("%s %s\n", surname[i], first[i]);

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

    return 0;
}

static void err_exit(const char *fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    vfprintf(stderr, fmt, args);
    va_end(args);
    exit(1);
}

答案 1 :(得分:0)

这里:

surname[i] = (char*)malloc(17 * sizeof(char));
first[i] = (char*)malloc(17 * sizeof(char));  
..
surname[i] = strtok(str, ", \n");
first[i] = strtok(NULL, ". ");

你为surnamefirst分配内存而你没有使用那个内存,因为你为它分配了strtok返回的字符串,你应该无论如何,因为它指向函数用于解析的静态缓冲区,您可以使用strdup代替:

  while (fgets(str, 80, fp_input) != NULL)   { 
    surname[i] = strdup(strtok(str, ", \n"));
    first[i] = strdup(strtok(NULL, ". "));
    middle_init[i] = strtok(NULL, ". ")[0];
    i++;  
  } // while

  /* prints arrays */          
  for (i = 0; i < size; i++)
        printf("%s %s %c\n", surname[i], first[i], middle_init[i]);

strdup将分配内存并复制字符串,这样你就可以避免对字符串长度进行硬编码,你应该在完成后释放内存,同时注意middile_init是一个char数组,所以我只分配1 char。