分配2D数组后,字符串被截断(已编辑)

时间:2017-04-18 15:44:55

标签: c shell multidimensional-array malloc

我正在动态分配2D数组:

char ** inputs;
inputs = (char **) malloc(4 * sizeof(char));

执行此操作后,我开始遇到字符串问题。我在分配2D数组之前和之后打印了字符串:

printf("%s\n", str);
char ** inputs;
inputs = (char **) malloc(4 * sizeof(char));
printf("%s\n", str);

但我得到了奇怪的输出:

before: input aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa with len  34
after: input aaaaaaaaaaaaaaaaaaaaaaaaaaaa with len  29

为什么长度会改变?我已经搜索了stackoverflow和其他网站,但无法找到合理的答案。

这是我的所有函数调用:

int main(int argc, char const *argv[])
{
    /* code */

    mainProcess();
    printf("\nEnd of the program\n");
    return 0;
}

//  Reading the input from the user
char * getInput(){
    printf("Inside of the getInput\n");
    char * result;
    char * st;
    char c;
    result = malloc(4 * sizeof(char));
    st = malloc(4 * sizeof(char));
    // code goes here 
    printf("$ ");
    while(3){
        c = fgetc(stdin);
        if(c == 10){
            break;
        }
        printf("%c", c);
        result[length] = c;
        length++;
    }   

    result[length] = '\0'; 

    return result;
}

void mainProcess(){
    char * input;

    printf("Inside of Main process\n");

    input = getInput();
    printf("\nthis is input %s with len  %d\n", input, strlen(input));

    splitInput(input);

    printf("\nthis is input %s with len  %d\n", input, strlen(input));
}



char ** splitInput(const char * str){
    char ** inputs;
    inputs = NULL;
    printf("inside split\n");
    printf("%s\n", str);

    inputs = (char **) malloc( sizeof(char));


    // free(inputs);
    printf("------\n"); // for testing
    printf("%s\n", str);
    if(!inputs){
        printf("Error in initializing the 2D array!\n");
        exit(EXIT_FAILURE);
    }

    return NULL;
}

2 个答案:

答案 0 :(得分:3)

您要完成的内容并不完全清楚,但似乎您尝试使用getInput阅读一行文字,然后您打算将输入分为splitInput中的单个字词,但不清楚如何去做。将一行文本分成单词的过程称为标记一个字符串。标准库提供strtok(恰当命名)和strsep(如果您有可能分隔字段,则主要有用)。

我已经在上面的评论中解释了2D数组与你使用指针指向字符之间的区别。

首先,请查看getInputc必须是int类型,或者您无法检测EOF,这个问题不会让您感到悲伤。此外,您只需将指针(类型size_t)作为参数传递,并在result中保留字符数,并避免strlen获取返回字符串的长度。无论如何你必须使用一个计数器来确保你不要在result的末尾开始写作,所以你也可以在调用函数中重新计算,例如。

char *getInput (size_t *n)
{
    printf ("Inside of the getInput\n");
    char *result = NULL;
    int c = 0;      /* c must be type 'int' or you cannot detect EOF */

    /* validate ALL allocations */
    if ((result = malloc (MAXC * sizeof *result)) == NULL) {
        fprintf (stderr, "error: virtual memory exhausted.\n");
        return result;
    }

    printf ("$ ");
    fflush (stdout);    /* output is buffered, flush buffer to show prompt */

    while (*n + 1 < MAXC && (c = fgetc (stdin)) != '\n' && c != EOF) {
        printf ("%c", c);
        result[(*n)++] = c;
    }
    putchar ('\n');     /* tidy up with newline */

    result[*n] = 0; 

    return result;
}

接下来,如上所示,您似乎想要使用result中的文本行并使用splitInput填充指向指针的指针使用单个单词(您会混淆为2D数组)。要做到这一点,您必须牢记strtok修改其操作的字符串,因此您必须复制str作为const char *传递的内容避免尝试修改常量字符串(和segfault)。

您对如何分配指针指向char对象感到困惑。首先,您必须为足够数量的指针分配空间,例如(使用#define MAXW 32)你需要这样的东西:

    /* allocate MAXW pointers */
    if ((inputs = malloc (MAXW * sizeof *inputs)) == NULL) {
        fprintf (stderr, "error: memory exhausted - inputs.\n");
        return inputs;
    }

然后,当您对输入字符串进行标记时,必须为每个单独的单词分配(每个单词本身都是一个单独的字符串),例如

        if ((inputs[*n] = malloc ((len + 1) * sizeof *inputs[*n])) == NULL) {
            fprintf (stderr, "error: memory exhausted - word %zu.\n", *n);
            break;
        }
        strcpy (inputs[*n], p);
        (*n)++;

注意: 'n'是一个指向size_t的指针,可以在调用者中恢复字数。

要标记输入字符串,您可以将上面的分配包装在:

    for (char *p = strtok (cpy, delim); p; p = strtok (NULL, delim))
    {
        size_t len = strlen (p);
        ...
        if (*n == MAXW)     /* check if limit reached */
            break;
    }

在整个代码中,您还应验证所有内存分配,并为每个分配的函数提供有效的返回,以允许调用者验证被调用函数是成功还是失败。

将所有部分组合在一起,您可以执行以下操作:

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

#define MAXC 256    /* constant for maximum characters of user input */
#define MAXW 32     /* constant for maximum words in line */

void mainProcess();

int main (void)
{
    mainProcess();
    printf ("End of the program\n");

    return 0;
}

char *getInput (size_t *n)
{
    printf ("Inside of the getInput\n");
    char *result = NULL;
    int c = 0;      /* c must be type 'int' or you cannot detect EOF */

    /* validate ALL allocations */
    if ((result = malloc (MAXC * sizeof *result)) == NULL) {
        fprintf (stderr, "error: virtual memory exhausted.\n");
        return result;
    }

    printf ("$ ");
    fflush (stdout);    /* output is buffered, flush buffer to show prompt */

    while (*n + 1 < MAXC && (c = fgetc (stdin)) != '\n' && c != EOF) {
        printf ("%c", c);
        result[(*n)++] = c;
    }
    putchar ('\n');     /* tidy up with newline */

    result[*n] = 0; 

    return result;
}

/* split str into tokens, return pointer to array of char *
 * update pointer 'n' to contain number of words
 */
char **splitInput (const char *str, size_t *n)
{
    char **inputs = NULL,
        *delim = " \t\n",   /* split on 'space', 'tab' or 'newline' */
        *cpy = strdup (str);

    printf ("inside split\n");
    printf ("%s\n", str);

    /* allocate MAXW pointers */
    if ((inputs = malloc (MAXW * sizeof *inputs)) == NULL) {
        fprintf (stderr, "error: memory exhausted - inputs.\n");
        return inputs;
    }

    /* split cpy into tokens (words) max of MAXW words allowed */
    for (char *p = strtok (cpy, delim); p; p = strtok (NULL, delim))
    {
        size_t len = strlen (p);

        if ((inputs[*n] = malloc ((len + 1) * sizeof *inputs[*n])) == NULL) {
            fprintf (stderr, "error: memory exhausted - word %zu.\n", *n);
            break;
        }
        strcpy (inputs[*n], p);
        (*n)++;

        if (*n == MAXW)     /* check if limit reached */
            break;
    }

    free (cpy);     /* free copy */

    return inputs;
}

void mainProcess()
{    
    char *input = NULL,
        **words = NULL;
    size_t len = 0, nwords = 0;

    printf ("Inside of Main process\n\n");

    input = getInput (&len);

    if (!input || !*input) {
        fprintf (stderr, "error: input is empty or NULL.\n");
        return;
    }

    printf ("this is input '%s' with len: %zu (before split)\n", input, len);

    words = splitInput (input, &nwords);

    printf ("this is input '%s' with len: %zu (after split)\n", input, len);

    free (input);  /* done with input, free it! */

    printf ("the words in input are:\n");

    for (size_t i = 0; i < nwords; i++) {
        printf ("  word[%2zu]: '%s'\n", i, words[i]);
        free (words[i]);    /* free each word */
    }
    free (words);           /* free pointers */

    putchar ('\n');     /* tidy up with newline */
}

示例使用/输出

$ ./bin/mainprocess
Inside of Main process

Inside of the getInput
$ my dog has fleas
my dog has fleas
this is input 'my dog has fleas' with len: 16 (before split)
inside split
my dog has fleas
this is input 'my dog has fleas' with len: 16 (after split)
the words in input are:
  word[ 0]: 'my'
  word[ 1]: 'dog'
  word[ 2]: 'has'
  word[ 3]: 'fleas'

End of the program

内存错误检查

在您编写的任何动态分配内存的代码中,您需要通过内存/错误检查程序运行代码。在Linux上,valgrind是正常选择。只需通过它运行代码,例如

$ valgrind ./bin/mainprocess
==15900== Memcheck, a memory error detector
==15900== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==15900== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==15900== Command: ./bin/mainprocess
==15900==
Inside of Main process

Inside of the getInput
$ my dog has fleas
my dog has fleas
this is input 'my dog has fleas' with len: 16 (before split)
inside split
my dog has fleas
this is input 'my dog has fleas' with len: 16 (after split)
the words in input are:
  word[ 0]: 'my'
  word[ 1]: 'dog'
  word[ 2]: 'has'
  word[ 3]: 'fleas'

End of the program
==15900==
==15900== HEAP SUMMARY:
==15900==     in use at exit: 0 bytes in 0 blocks
==15900==   total heap usage: 7 allocs, 7 frees, 546 bytes allocated
==15900==
==15900== All heap blocks were freed -- no leaks are possible
==15900==
==15900== For counts of detected and suppressed errors, rerun with: -v
==15900== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

始终确认您已释放所分配的内存,并且没有内存错误。

仔细看看,如果您有任何疑问,请告诉我。如果我猜错了你的意图,那么MCVE帮助:)

答案 1 :(得分:1)

此代码在没有警告的情况下编译(gcc -Wall),并且不会更改大小 它还试图强调分配足够空间和/或不要在分配的内存之外写入的需要 注意例如

  • malloc((MaxInputLength+1) * sizeof(char))
  • while(length<MaxInputLength)
  • inputs[i]=malloc((MaxLengthOfSplitString+1) * sizeof(char));

    #include <stdio.h>  
    #include <stdlib.h>
    #include <string.h>
    
    // the length which was used in your MCVE, probably accidentally  
    #define MaxInputLength 3           // you will probably want to increase this
    #define MaxLengthOfSplitString  1  // and this
    #define MaxNumberOfSplitStrings 3  // and this
    
    //  Reading the input from the user
    char * getInput(){
        printf("Inside of the getInput\n");
        char * result;
        char c;
        int length=0;
        result = malloc((MaxInputLength+1) * sizeof(char));
    
        // code goes here 
        printf("$ ");
        while(length<MaxInputLength){
            c = fgetc(stdin);
            if(c == 10){
                break;
            }
            printf("%c", c);
            result[length] = c;
            length++;
        }   
    
        result[length] = '\0'; 
    
        return result;
    }
    
    char ** splitInput(const char * str){
        char ** inputs;
        inputs = NULL;
        printf("inside split\n");
        printf("%s\n", str);
    
        inputs = (char **) malloc(MaxNumberOfSplitStrings * sizeof(char*));
        {
            int i;
            for (i=0; i< MaxNumberOfSplitStrings; i++)
            {
                inputs[i]=malloc((MaxLengthOfSplitString+1) * sizeof(char));
            }
    
            // Now you have an array of MaxNumberOfSplitStrings char*.
            // Each of them points to a buffer which can hold a ero-    terminated string
            // with at most MaxLengthOfSplitString chars, ot counting the     '\0'.
        }
    
        // free(inputs);
        printf("------\n"); // for testing
        printf("%s\n", str);
        if(!inputs){
            printf("Error in initializing the 2D array!\n");
            exit(EXIT_FAILURE);
        }
    
        return NULL;
    }
    
    void mainProcess(){
        char * input;
        printf("Inside of Main process\n");
        input = getInput();
        printf("\nthis is input %s with len  %d\n", input, strlen(input));
        splitInput(input);
        printf("\nthis is input %s with len  %d\n", input, strlen(input));
    }
    
    int main(int argc, char const *argv[])
    {
        /* code */
        mainProcess();
        printf("\nEnd of the program\n");
        return 0;
    }