【问题标题】:Strange behavior of c char** array assignmentc char** 数组赋值的奇怪行为
【发布时间】:2017-05-15 22:54:55
【问题描述】:

我是 C 语言的新手,希望能够很好地理解来自 Java 背景的 char 数组赋值。我使用stringTok() 将本地sentence[] 数组拆分为单独的数组字,并希望将该字分配给正在传入的char ** tokens。我收到以下输出:

value of i:4   
len:1 ,  
len:1 ,  
len:6 ,8?k  
len:0 ,  

有些有值(分配的值是乱码)有些没有。我最初认为我必须为字符数组中的每个字符分配内存。但是,这似乎也不起作用。所以,我想我一定是做错了什么。我也尝试过使用tokens[4][10] 而不是char ** tokens,也没有任何区别。所以,我被困在这很长一段时间了。

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

void  stringTok(char** tokens)
{
    int i = 0;
    char sentence[] = "jake is really cool.";
    char *word = strtok(sentence, " .");

    while (word != NULL)
    {
        //printf("%s\n", word);
        tokens[i++] = word;
        word = strtok(NULL, " .");
    }
    printf("value of i:%d\n", i);
    tokens[i] = NULL;
}

int main()
{ 
    char **cmdArr = calloc(5, sizeof(char*));

    for (int i = 0; i < 5; i++)
        cmdArr[i] = (char*)calloc(10, sizeof(char));
    stringTok(cmdArr);
    for(int i = 0; i < 4; i++)
    {
        printf("len:%ld ,", strlen(cmdArr[i]));
        printf("%s\n", cmdArr[i]);
    }

    return 0;
}

【问题讨论】:

  • 在 C 中你不强制转换 malloc - stackoverflow.com/questions/605845/…
  • 在对象生命周期结束后访问对象的未定义行为。 (另外,在这种情况下,意味着使用一个对象的值(指向该对象的指针),而它的值是不确定的)
  • char sentence[] = "jake is really cool."; 在您的 stringTok() 函数中创建一个局部变量。您的 strtok() 调用返回指向该数组的指针。当您的 stringTok() 函数返回时,您的 sentence 变量将不复存在,并且指向它的所有指针都将变为无效。因此未定义的行为。
  • @AndrewHenle 我现在明白你的意思了。我在函数 stringTok 中添加了另一个参数,而不是使用 sentence[ ] 作为局部变量。这显然修复了错误。但是,这归结为 c 中隐藏的概念性内容。您是否会说我们不应该使用或分配函数内本地指针引用的任何值来指向其范围之外的指针?
  • @NateLee 你永远不会那样做。告诉我这与计算机的工作方式相得益彰。请记住,C 是一种低级语言,具有绝对惊人的控制能力,前提是您了解如何使用它。

标签: c arrays variable-assignment


【解决方案1】:

您已经为这些值分配了存储空间。主要错误是您使用分配认为它会复制您的字符串。相反,它覆盖了指向由strtok 返回的临时对象的指针。

快速解决方法是这样的:

while (word != NULL) {
    strcpy(tokens[i++], word);
    word = strtok(NULL, " .");
}

你应该小心一点。您的字符串仅支持 10 个字符(包括空终止符)。通常,您会使用strncpy,如果源字符串对于您可用的存储量来说太长,它将停止。这有助于避免诸如缓冲区溢出之类的不良情况,这些情况会导致未定义的行为并造成经常在网络攻击中被利用的漏洞。

更安全的方法可能是让您的分词器执行分配。

int tokenize( const char *sentence, char** tokens, int max_tokens )
{
    const char * delim = " .";
    int count = 0;

    // Make copy of sentence for strtok
    int len = strlen(sentence);
    char *copy = malloc(len+1);
    if( !copy ) return 0;
    strncpy( copy, len, sentence );
    copy[len] = '\0';

    // Allocate and copy tokens
    for( char *word = strtok(copy, delim);
         word && count < max_tokens;
         word = strtok(NULL, delim) )
    {
        int len = strlen(word);
        tokens[count] = malloc(len+1);
        if( !tokens[count] ) break;
        strncpy( tokens[count], len, word );
        tokens[count][len] = '\0';
        count++;
    }

    return count;
}

现在,这有点傻。由于您已经复制了原始文件并且strtok 将插入终止符,因此无需分配和复制所有内容。您可以改为将copy 存储在tokens[0] 中,然后将word 存储在每个后续指针中。调用者只需要知道清理内存只需要free(tokens[0])从不任何其他(只是指向该块的指针)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-07
    • 1970-01-01
    • 2021-12-05
    • 1970-01-01
    • 1970-01-01
    • 2014-11-04
    相关资源
    最近更新 更多