2012-11-11 47 views
2

当我首先打印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 
+1

该代码泄漏surname [i]的初始for循环中的所有动态分配,并在处理第二个for循环时泄漏第一个for循环,然后在退出main()之前泄漏剩余部分。 – WhozCraig

+2

您为'surname [i]'分配内存,然后将'surname [i]'设置为不同的指针,从而失去了分配的内存块。您应该将'str'的​​标记化部分*复制到'surname [i]'中。 – DCoder

+0

你从不检查'strtok'函数的失败。 –

回答

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); 
} 
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。