超出2D阵列中间的内存地址?

时间:2012-06-07 03:32:52

标签: c memory malloc

首先,对标题感到抱歉。我真的不太确定如何表达它。

在C中,我有一个2D字符串数组,声明和分配如下:

char ** args = malloc(50*(num_args+1));
for (int i = 0; i < num_args+1; i++){
    args[i] = malloc(50);

我使用的是一种“基本shell”类型的程序,模仿bash的一些功能,因此是num_args变量。

在多台机器上编译并运行,args [4]的地址总是超出范围。这是相关的gdb输出:

(gdb) print args[0]
$2 = 0x609140 "gcc"
(gdb) print args[1]
$3 = 0x609180 ""
(gdb) print args[2]
$4 = 0x6091c0 ""
(gdb) print args[3]
$5 = 0x609200 ""
(gdb) print args[4]
$6 = 0x636367 <Address 0x636367 out of bounds>
(gdb) print args[5]
$7 = 0x609280 ""

如您所见,args [4]之前和之后的地址有效。这个地址怎么能超出范围?

使用此代码的整个函数是here及以下:

void parse(const char * command){
    // first parse built-ins (ie, not a call to the OS)
    if (strcmp(command, "history") == 0){
        show_history();
        return;
    }
    if (strcmp(command, "exit") == 0){
        exit(0);
    }

    hist_add(command);

    // copy 'command' into arg_string, while ignoring any possible comments
    char * arg_str;
    int num_args = 1;
    arg_str = malloc(strlen(command));
    for (int i = 0; i < strlen(command); i++){
        if (command[i] == '#' || command[i] == '\n') break;
        if (command[i] == ' ') num_args++;
        arg_str[i] = command[i];
    }

    // split arg_str into a string array where each string is an argument
    // to the command
    char ** args = malloc(num_args+1);
    for (int i = 0; i < num_args+1; i++){
        args[i] = malloc(50);
    }
    int tokens = 0;
    const char token = ' ';
    char * next = strtok(arg_str, &token);
    while (next != NULL){
        strcpy(args[tokens++], next);
        next = strtok(NULL, &token);
        if (next == NULL)
            args[tokens] = (char *)NULL;
    }

    exec_command(args);
}

3 个答案:

答案 0 :(得分:4)

你的问题的答案在于它不是 2D数组。相反,args包含指向一维指针数组的第一个元素的指针,并且这些元素中的每一个本身都可以指向char的一维数组的元素(这通常称为“衣衫褴褛”数组“,因为那些1D数组可以有不同的长度。”

因此即使args[4]args[3]不是,一个地址args[5]可能超出界限的原因是三个指针args[3]args[4]args[5]是完全独立的值。

args[4]很可能被错误的值覆盖,因为它实际上位于您分配的区域之外 - 您没有为args指向的数组分配足够的空间。您的malloc()来电请求num_args + 1 字节,但您希望有足够的空间用于num_args + 1 指针,每个指针占用多个字节。我建议您将malloc()电话改为:

char ** args = calloc(num_args + 1, sizeof args[0]);

(而不是使用calloc(),您当然可以将num_args + 1乘以sizeof args[0]并调用malloc(),但如果您这样做,那么您需要检查以确保乘法不会溢出SIZE_MAXcalloc()应该为你处理。)

答案 1 :(得分:1)

malloc()的参数是要分配的字节数。我猜num_args不足以容纳char *类型的所有指针,50也不足以给出字符串长度。我没有仔细查看完整代码,但您可能需要malloc(sizeof(char *) * num_args)来分配参数字符串的所有指针。然后循环并为malloc(sizeof(char) * len)为每个字符串分配足够的空间(如果它们被复制),其中len是您需要存储的字符串的最大长度。

答案 2 :(得分:0)

您可能存在以下行中的内存分配错误和性能错误:

arg_str = malloc(strlen(command));
for (int i = 0; i < strlen(command); i++){

通常有一个函数strdup()可用于复制字符串。如果不可用,请使用:

char *arg_str = malloc(strlen(command) + 1);

为终端'\0'提供足够的空间。

性能错误是,如果字符串很长,那么在循环的每次迭代中评估strlen()代价很高。计算一次长度并重复使用 - 除非字符串的长度在每次迭代时变化。

你不是null终止你的字符串;你这样做至关重要。

int len = strlen(command);
int i;  // Must be in scope after the loop ends
for (i = 0; i < len; i++){
    if (command[i] == '#' || command[i] == '\n') break;
    if (command[i] == ' ') num_args++;
    arg_str[i] = command[i];
}

// i is out of scope if you use for (int i = 0;...
arg_str[i] = '\0';

缺少空终止可能会解决您的其他问题。如有疑问,请随时打印,但在没有字符串空终止时要小心。