首先,对不起标题。我真的不太确定如何措辞。
在 C 中,我有一个 2D 字符串数组,声明和分配如下:
char ** args = malloc(50*(num_args+1));
for (int i = 0; i < num_args+1; i++){
args[i] = malloc(50);
我正在一种"基本的外壳"类型的程序中使用它,模仿 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] 之前和之后的地址是有效的。这一个地址怎么可能越界?
使用此代码的整个函数如下所示:
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);
}
您的问题的答案在于它不是 2D 数组。 相反,args
包含一个指向 1D 指针数组的第一个元素的指针,并且每个元素本身都可以指向 1D 数组char
的一个元素(这通常称为"参差不齐的数组",因为这些 1D 数组可以具有不同的长度)。
因此,即使args[3]
和args[5]
不是,一个地址args[4]
也可以越界的原因是,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_MAX
。 calloc()
应该为您处理)。
malloc()
参数是要分配的字节数。我猜num_args
不足以容纳 char *
类型的所有指针,50
考虑到您的字符串长度也是不够的。我还没有详细查看您的完整代码,但您可能需要malloc(sizeof(char *) * num_args)
将所有指针分配给参数字符串。然后使用 malloc(sizeof(char) * len)
遍历并为每个字符串(如果它们正在复制)分配足够的空间,其中 len
是您需要存储的字符串的最大长度。
您可能在以下行中存在内存分配错误和性能错误:
arg_str = malloc(strlen(command));
for (int i = 0; i < strlen(command); i++){
通常有一个函数strdup()
可用于复制字符串。 当它不可用时,您可以使用:
char *arg_str = malloc(strlen(command) + 1);
也为终端空' '
留出足够的空间。
性能错误是,如果字符串很长,则在循环的每次迭代中评估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] = ' ';
缺少空终止可能解释了您的其他问题。 如有疑问,请随时打印内容,但要小心,不要以字符串 null 结尾。