C 动态分配和复制数组
我有时会看到这样的代码:C 动态分配和复制数组,c,malloc,dynamic-arrays,C,Malloc,Dynamic Arrays,我有时会看到这样的代码: char* copyStr(char* input) { int inputLength; char *answer; inputLength = strlen(input); answer = malloc(inputLength + 1); answer = input; return answer; } 人们经常说这种代码不起作用,而且这种模式 answer = malloc(inputLength + 1); answer = i
char* copyStr(char* input) {
int inputLength;
char *answer;
inputLength = strlen(input);
answer = malloc(inputLength + 1);
answer = input;
return answer;
}
人们经常说这种代码不起作用,而且这种模式
answer = malloc(inputLength + 1);
answer = input;
毫无意义。为什么会这样?在我看来,代码还可以。它为答案分配适当的内存量,然后将输入复制到答案。例如,它似乎在我的测试中起作用
int main()
{
printf ("%s\n", copyStr("Hello world!"));
}
做我期望它做的事。那么它有什么问题呢?为了回答这个问题,让我们先看一段稍微简单的代码片段
int answer;
answer = 42;
answer = 0;
即使是最草率的观察者也会注意到第一次任务
answer = 42;
没用。它将42
的值放入answer
,只会在下一瞬间被丢弃并替换为0
。因此,这行代码可以完全丢弃
让我们通过查看来验证这一点。如我们所见,行answer=42代码>实际上对生成的机器代码没有任何影响
现在将其与所讨论的代码进行比较
answer = malloc(inputLength + 1);
answer = input;
如果类比推理在这种情况下是有效的,那么我们必须得出结论,第一个赋值是无用的,可以省略。我们将一些东西(malloc的结果)放在答案中,但不久之后就被扔掉,取而代之的是其他东西
当然,如果没有进一步的研究,我们不能说它是否适用,但我们可以通过再次查看生成的程序集来确认我们的怀疑。编译器甚至不生成对malloc
和strlen
的任何调用!它们确实是无用的
那么这种直觉在哪里呢
它为答案分配适当的内存量,然后将输入复制到答案
崩溃了
问题在于指针和数组之间永远的混淆
人们可能经常看到这样的说法:在C语言中,数组是指针,指针是数组,数组和指针是可互换的,或者任意数量的变体。这些说法都是虚假和误导性的。指针和数组是完全不同的东西。他们经常在一起工作,但这远非一成不变。让我们在代码示例中分解指针和数组
输入
是指针变量
input
(大概)指向一个字符串,该字符串是char
answer
是另一个指针变量
malloc(…)
动态分配一个新的char数组
,并返回指向该数组的指针
answer=malloc(…)
将指向answer
的指针复制到malloc
分配的数组中
answer=input
将另一个指针(我们已经在上面看到)复制到answer
- 现在
answer
和input
指向同一个字符串,malloc
的结果被遗忘并丢弃
这就解释了为什么你的代码会做你期望它做的事情。与“Hello world!”字符串的两个相同副本不同,您只有一个字符串和两个不同的指针。这看起来就像是医生的命令,但一旦我们做了一些稍微复杂的事情,它就会崩溃。例如,像这样的代码
char *lineArray[MAX_LINES];
char buffer[BUF_LEN];
int i = 0;
while (i < MAX_LINES && fgets(buffer, BUF_LEN, stdin)) {
lineArray[i++] = copyStr(buffer);
}
对于其他阵列,我们可以使用memcpy
。主要区别在于我们必须传递数组长度
memcpy(answer, input, inputLength + 1);
这两种变体都适用于我们的情况,但首选第一种变体,因为它重申了我们正在处理字符串。以下是完整性的固定copyStr
:
char* copyStr(char* input) {
int inputLength;
char *answer;
inputLength = strlen(input);
answer = malloc(inputLength + 1);
strcpy(answer, input);
return answer;
}
顺便说一句,它的工作原理与非标准但广泛可用的函数几乎相同(strdup具有更好的签名和工作错误检查,我们在这里省略了这一点)。简单地说。此代码:
var = foo();
var = bar();
在所有1种情况下100%等同于此:
foo();
var = bar();
此外,如果foo()
没有副作用,那么它100%等同于最后一行:
// foo();
var = bar();
这适用于任何函数,包括malloc
。如果我们暂时忘记了malloc的所作所为,只关注刚刚说过的话,我们可以很快意识到在这段代码的注释中写了什么:
answer = malloc(inputLength + 1);
// Here, the variable answer contains the return value from the call to malloc
answer = input;
// Here, it contains the value of input. The old value is overwritten, and
// is - unless you saved it in another variable - permanently lost.
malloc
所做的事情非常简单。它返回一个指向内存块的指针,如果分配失败,则返回一个空指针。2就是这样。对于像ptr=malloc(size)
这样的调用,绝对没有什么比将地址存储在指针变量ptr
中更有趣的了。指针变量与其他变量(如int
或float
)一样,也没有什么特别之处。int
存储整数。指针存储内存地址。这里没有魔法
1它是100%等价的,除了你正在做一些非常奇特的事情,比如用外部程序读取变量var
2malloc(0)
可以返回一个非空指针,但在实践中没有区别,因为取消引用它是未定义的行为,分配零字节是一个非常无意义的(哈哈,点)操作。这个问题旨在为那些具有x=malloc(y);x=z代码>模式。迂腐的细节:“如果分配失败,返回…一个NULL
指针。”更像是“失败时返回一个NULL
”<代码> MALOC(0)可以返回<代码> null <代码>或不成功。实现细节。@ CuxReNeSimeMeNICA,我会认为这是一个更哲学的问题。如果你试图分配零字节,失败和成功之间真的有区别吗?“非常没有意义”-->UV@chux,当然你是对的,修复了
answer = malloc(inputLength + 1);
// Here, the variable answer contains the return value from the call to malloc
answer = input;
// Here, it contains the value of input. The old value is overwritten, and
// is - unless you saved it in another variable - permanently lost.