在C中为字符串分配内存

wos*_*hen 0 c string malloc char

我刚刚开始C编程,我有一个初学者的问题:

int main(int argc, char *argv[])
{ 
char *a=malloc(1*sizeof(char));
a[0]='a';
a[1]='b';
a[2]='c';
printf("%c\n",a[0]);
printf("%c\n",a[1]);
printf("%c\n",a[2]);
printf("%s\n",a);
return 0;
}
Run Code Online (Sandbox Code Playgroud)

所以我想通过逐个输入字符来创建一个由未知长度字组成的字符串.因为我不知道单词的长度所以我只使用malloc.我计划先为一次性字符分配内存,然后在输入新字符时使用reallocate为下一个字符添加新空间.然而,在我malloc(1*sizeof(char))之后,我应该发现我可以在字符串中添加多个字符,为什么会发生这种情况?这是正确的方法吗?

感谢大家花时间阅读我的长问题:)

LSe*_*rni 10

通过"为什么会发生这种情况"你的意思是,

  • 为什么允许
  • 为什么不"受到惩罚"(即立即崩溃)?

这是允许的,因为C直接访问内存; 这是它的力量的一部分.在让你这么做之前,很少检查你要做的事情.这就是你需要小心的原因.

为什么它不是因为崩溃而受到"惩罚",而不是立即,也许永远不会?因为在该区域并不总是禁止写入(内存保护是面向页面的).假设当你分配一个内存区域时,它被分成1000字节的页面.然后,如果您分配50个字节,底层硬件将解锁1000个字节.它无法解锁较小的区域.所以你"可以"写入所有这1000个字节而不会导致保护错误.

现在,内存管理器必须跟踪数据的位置,因此它有自己的结构,并且它通常也"记录"内存.因此,当您请求50个字节时,软件内存管理器实际上可能会分配256个.然后,如果您将这50个字节重新分配()到100,您将看到指针没有改变.如果你realloc()那些257字节,指针确实改变 - 内存管理器不能将该块扩大到257字节,所以它标记它是空闲的,并从硬件分配512块在其他地方.如果然后alloc()42个字节,您可能会发现它的指针与之前指向100字节缓冲区的地址相同.

有时,一些调试库不仅会分配一个区域,还会用金丝雀"保护"它.你问50个字节,库分配66并在66个内部返回一个8字节的指针.它用已知值填充前8个字节和后8个字节.时不时会检查价值是否仍然存在; 如果不是,则会发出软崩溃,警告您溢出(或下溢)缓冲区.

在您的示例中,没有此类保护,您可以在超出分配范围的额外区域中书写.但是很有可能该区域将在以后使用,并被覆盖:也许,如果你这样做的话

foo = malloc(20);
strcpy(foo, "string ... 30 bytes long");
bar = malloc(20); //     ^20th byte
strcpy(bar, "hello world");
Run Code Online (Sandbox Code Playgroud)

然后打印foo,你得到"string ... 3hello world".或者"字符串... [垃圾]你好世界".通过编写跟随foo的bar,您覆盖了存储数据的区域.

再说一次,如果你从未在栏中写过任何东西,该程序可能会起作用而且永远不会抱怨.

然后你在不同的平台或不同的库上编译,一个已经工作多年的程序突然崩溃了.欢迎来到未定义行为的世界.

有几个库和工具可用于解决这类问题 - 一个非常好的工具是valgrind.

"删除"字符串和/或释放其内存

// I initialize the pointer to NULL. If I just declared the pointer,
// its initial value might be anything. This way, I reduce the random
// element in my program. Makes no difference... except that one time
// when it does, and will save your bacon.
char *pwd = NULL;

// Every malloc and realloc MUST check that it did not return NULL,
// meaning an error occurred. Even for small memory blocks.
if (NULL === (pwd = malloc(200))) {
     // Handle out of memory error
}
strcpy(pwd, "Squeamish Ossifrage"):

// ... do something with pwd

// ...we're done. If we just freed this area, its contents would remain
// available *and* the pointer would still point to it. so this works:
/*
      free(pwd);
      printf("The secret word is %s\n", pwd);
      ...but might explode at any moment.
*/
// pwd contains sensitive data, so we first zero it, and this requires
// remembering the actual size of the allocated block. Here, 200.
memset(pwd, 0, 200);

// Now we free the area pointed to by the pointer. Then we also
// erase the pointer.
free(pwd); pwd = NULL;
Run Code Online (Sandbox Code Playgroud)

通过在同一行写入free和NULL,我可以运行

grep 'free\\s*(' | grep -v "NULL;"
Run Code Online (Sandbox Code Playgroud)

并找到free()没有NULL赋值的所有行,并将这些行标记为可能需要改进.

现在,如果我在释放后使用pwd,它将永远不会工作,这将从执行中删除进一步的随机性.


Rya*_*yan 8

发生这种情况是因为你被允许这样做,但它是未定义的行为.这称为缓冲区溢出,这是一种危险的编程情况.您应该分配超过1个字节的内存,并跟踪字符串的长度.到达已分配空间的末尾后,您可以调用realloc以将内存块重新分配到更大的大小.