动态分配和复制数组

问题描述 投票:3回答:2

我有时会看到这样的代码:

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!"));
}

做了我期望的事情。那怎么了?

c malloc dynamic-arrays
2个回答
2
投票

简单地说。此代码:

var = foo();
var = bar();

在所有1情况下均等于此值:

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
// - unless you saved it in another variable - permanently lost.

malloc确实很简单。它返回指向内存块的指针,如果分配失败,则返回NULL指针。2就是这样。与ptr = malloc(size)之类的调用相比,您所做的工作绝对不过是将地址存储在指针变量ptr中而已。指针变量的使用方式与intfloat等其他变量相同。 int存储一个整数。指针存储一个存储器地址。这里没有魔术。

1 这是100%的等效项,除了您正在做一些花哨的事情,例如使用外部程序读取变量var之外2 malloc(0)可以返回非空指针,但实际上并没有什么不同,因为取消引用将是未定义的行为,并且分配零字节是毫无意义的(哈哈,指向)操作。


1
投票

为了回答这个问题,让我们先来看一个稍微简单一些的代码片段。

int answer;

answer = 42;
answer = 0;

即使是最粗略的观察者也会注意到第一个作业

answer = 42;

没用。它将42的值放入answer,仅在第二个下一个瞬间被丢弃并替换为0。这样那行代码可以完全扔掉。

让我们通过查看optimised assembly code generated by a C compiler进行验证。如我们所见,answer = 42;行实际上对生成的机器代码没有任何影响。

现在将其与相关代码进行比较

answer = malloc(inputLength + 1);
answer = input;

如果类推推理在这种情况下有效,那么我们必须得出结论,第一个分配是无用的,可以省略。我们将某些内容(malloc的结果)放置在answer中,稍后将其丢弃并替换为其他内容。

当然,如果没有进一步的研究,我们不能说它是否适用,但是可以通过再次查看生成的程序集来证实我们的怀疑。 And it is confirmed。编译器甚至不会生成对mallocstrlen的任何调用!他们确实是没用的。


那么,这种直觉在哪里?>

它为答案分配了适当的内存量,然后将输入复制到答案中

分解?

问题在于指针和数组之间的永恒混乱。

人们可能经常看到这样的说法,在C语言中,数组是指针,或者指针是数组,或者数组和指针是可互换的,或其任何数量的变体。这些主张都是虚假和误导的。指针和数组是完全不同的东西。他们经常在一起工作,但这远非相同。让我们在代码示例中分解指针和数组。

  • input是指针变量
  • input(大概)指向
  • 字符串,它是char的数组]
  • answer是另一个指针变量
  • malloc(...)动态分配char的新数组并返回一个指针,该指针指向
  • 所述数组
  • [answer = malloc(...)将指针复制到answer,现在是answer 指向malloc分配的数组
  • answer = input另一个指针(我们已经在上面看到)复制到answer] >>
  • 现在answerinput 指向同一字符串
  • ,并且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);
}

将以stringArray的每个元素指向相同的字符串,而不是指向从stdin提取的一堆不同的行结束。

[确定,所以现在我们确定answer = input复制一个指针。但是我们要复制一个数组,我们刚刚为其分配了空间!我们该怎么做?

由于我们的数组大概是NUL终止的字符串,所以我们可以使用设计用于复制NUL终止的字符串的标准库函数。

strcpy(answer, input);

对于其他数组,我们可以使用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函数相同(strdup具有更好的签名和有效的错误检查,在此我们省略了。)>

© www.soinside.com 2019 - 2024. All rights reserved.