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

2
malloc(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.