阵列未正确填充

时间:2015-02-23 18:47:43

标签: c arrays

我试图将文档解构为各自的段落,并将每个段落作为字符串输入到数组中。但是,每次添加新值时,它都会覆盖数组中的所有先前值。读取的最后一个“段落”(由换行符表示)是数组的每个非空值的值。

以下是代码:

char buffer[MAX_SIZE];
char **paragraphs = (char**)malloc(MAX_SIZE * sizeof(char*));
int pp = 0;
int i;

FILE *doc;
doc = fopen(argv[1], "r+");
assert(doc);

while((i = fgets(buffer, sizeof(buffer), doc) != NULL)) {
    if(strncmp(buffer, "\n", sizeof(buffer))) {
        paragraphs[pp++] = (char*)buffer;
    }
}

printf("pp: %d\n", pp);

for(i = 0; i < MAX_SIZE && paragraphs[i] != NULL; i++) {
    printf("paragraphs[%d]: %s", i, paragraphs[i]);
}

我收到的输出是:

pp: 4
paragraphs[0]: paragraph four
paragraphs[1]: paragraph four
paragraphs[2]: paragraph four
paragraphs[3]: paragraph four

程序运行时如下:./prog.out doc.txt,其中doc.txt为:

paragraph one
paragraph two
paragraph three

paragraph four

另外需要程序的行为。段落计数正常工作,忽略仅包含换行符的行(第4行)。

我认为问题发生在while循环中,但我不确定如何解决问题。

2 个答案:

答案 0 :(得分:0)

分配必要内存的正确方法是什么?第2行的malloc不够用吗?

不,您需要为您创建的2D字符串数组分配内存。以下内容不能用于编码。

char **paragraphs = (char**)malloc(MAX_SIZE * sizeof(char*));   

如果你有: (简单解释)
    char ** array = {0}; //在内存分配之前的C字符串数组

然后你可以像这样为它创建内存:

int main(void) 
{
    int numStrings = 10;// for example, change as necessary
    int maxLen = MAX_SIZE; //for example, change as necessary
    char **array  {0};
    array = allocMemory(array, numStrings, maxLen);
    //use the array, then free it
    freeMemory(array, numStrings);
    return 0;
}

char ** allocMemory(char ** a, int numStrings, int maxStrLen)
{
    int i;
    a = calloc(sizeof(char*)*(numStrings+1), sizeof(char*));
    for(i=0;i<numStrings; i++)
    {
      a[i] = calloc(sizeof(char)*maxStrLen + 1, sizeof(char));
    }
    return a;
}

void freeMemory(char ** a, int numStrings)
{
    int i;
    for(i=0;i<numStrings; i++)
        if(a[i]) free(a[i]);
    free(a);
}

注意: 您可以通过多种方式确定文件中的行数,例如,通过FILE * fp = fopen(filepath,&#34; r&#34;);然后在循环中调用ret = fgets(lineBuf, lineLen, fp)直到ret == EOF,保持每个循环的索引值的计数。然后是fclose()。 (你没有这样做)这个必要的步骤是包含在上面的代码示例中,但如果这是你想要使用的方法,你可以添加它。

分配内存后 ,请在代码中更改以下内容:

paragraphs[pp++] = (char*)buffer;  

要:

strcpy(paragraphs[pp++], buffer);//no need to cast buffer, it is already char *    

此外,请勿忘记在完成打开文件后致电fclose()

答案 1 :(得分:0)

你的解决方案非常合理。您的Paragraph数组应该包含每个段落,并且由于每个段落元素只是一个小的4字节指针,因此您可以定义合理的最大数量。但是,由于此最大数字是常量,因此动态分配数组几乎没有用处 动态分配的唯一有意义的用途是读取整个文本一次以计算段落的实际数量,相应地分配数组并重新读取整个文件,但我怀疑这是值得的。 使用固定大小的段落数组的缺点是,一旦达到最大数量的元素,就必须停止填充它 如果你绝对想要处理整本圣经,你可以重新分配一个更大的数组,但是对于一个教育练习,我认为停止记录段落是合理的(从而产生一个可以存储和计算段落的代码达到最大数量)。

您的代码确实存在问题,您不会将段落内容存储在任何位置。当你读取实际的行时,它总是在同一个缓冲区内,所以每个段落都会指向相同的字符串,它将包含最后一段读取。

解决方案是制作缓冲区的唯一副本,并将当前段落指向该位置。

C已经非常混乱了,我建议使用strdup()函数, dup 获取 str ing(基本上计算字符串长度,分配足够的内存,将字符串复制到其中并返回保存新副本的新内存块)。一旦你完成了它的使用,你只需要记住释放这个新副本(在你的程序结束时)。

这是最节省时间的解决方案,因为每个字符串都需要strlenmalloc内部执行strdump为所有段落预先分配了一个大缓冲区,但它肯定更简单并且可能更高效(仅为每个字符串分配最少量的内存,尽管每个malloc为内部分配器消耗一些额外的字节持家)。

血腥笨拙的fgets也会在行尾添加尾随\n,所以你可能想要摆脱它。

如果您只是使用pp作为限制,而不是检查未初始化的段落,那么您的上一个显示循环将更简单,更强大且更高效。

最后,您最好为最大行大小和最大段落数定义两个不同的常量。使用相同的值对两者都没有意义,除非你正在处理完美的方形文本:)。

#define MAX_LINE_SIZE   82 // max nr of characters in a line (including trailing \n and \0)
#define MAX_PARAGRAPHS 100 // max number of paragraphs in a file

void main (void)
{
    char buffer[MAX_LINE_SIZE];
    char * paragraphs[MAX_PARAGRAPHS];
    int pp = 0;
    int i;

    FILE *doc;
    doc = fopen(argv[1], "r+");
    assert(doc != NULL);

    while((fgets(buffer, sizeof(buffer), doc) != NULL)) {
        if  (pp != MAX_PARAGRAPHS  // make sure we don't overflow our paragraphs array
          && strcmp(buffer, "\n")) {
            // fgets awkwardly collects the ending \n, so get rid of it
            if (buffer[strlen(buffer)-1] == '\n') buffer[strlen(buffer)-1] = '\0';
            // current paragraph references a unique copy of the actual text
            paragraphs[pp++] = strdup (buffer);
        }
    }

    printf("pp: %d\n", pp);

    for(i = 0; i != pp; i++) {
        printf("paragraphs[%d]: %s", i, paragraphs[i]);
        free(paragraphs[i]); // release memory allocated by strdup
    }
}