我刚刚开始C编程,我有一个初学者问题:
int main(int argc, char *argv[])
{
char *a=malloc(1*sizeof(char));
a[0]='a';
a[1]='b';
a[2]='c';
printf("%cn",a[0]);
printf("%cn",a[1]);
printf("%cn",a[2]);
printf("%sn",a);
return 0;
}
因此,我想通过一个一个一个接一个地输入字符的字符串由未知的单词组成。因为我不知道这个词的长度,所以我只使用malloc。我计划先分配一次字符的内存,然后使用Reallocation使用RealLocation为下一个字符添加新的空间,当我输入新字符时。但是,在我malloc(1*sizeof(char))之后,它应该是一个字符的大小,我发现我已经可以在字符串中添加多个字符,为什么会发生这种情况?正确的方法是什么?
感谢大家的时间阅读我的长期问题:)
by"为什么会发生"你的意思,
- 为什么允许?
- 为什么不"受到惩罚"(即立即崩溃)?
允许,因为C直接访问内存;那是其力量的一部分。在让您 do 之前,很少有关于您要做的事情的检查。这就是为什么您需要小心。
为什么不会因崩溃而"惩罚",而不是立即也许从来没有?因为并非总是禁止在该地区写作(记忆保护是面向页面的)。假设您分配内存区域时,将其分为1000个字节。然后,如果您分配50个字节,那么基础硬件将解锁1000个字节。它无法解锁较小的区域。因此,您"可以"写信给所有1000个字节中的所有,而不会引起保护故障。
现在,内存管理器必须跟踪数据的位置,因此它具有自己的结构,并且经常"页面"内存。因此,当您请求50个字节时,软件内存管理器实际上可能分配256。然后,如果您将50个字节到100,则您会发现指针没有更改。如果将这些元素to to 257字节到257个字节,则指针 dis 更改 - 内存管理器无法将该块扩大到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");
然后打印foo,您会得到"弦... 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 %sn", 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;
通过在同一行上写免费和空,我可以运行
grep 'free\s*(' | grep -v "NULL;"
并找到免费()没有零分配的所有行,并将这些行标记为可能需要改进的行。
现在,如果我在释放它后使用PWD,它将永远无法使用,这将消除执行中的进一步随机性。
这是因为您可以这样做,但这是不确定的行为。这称为缓冲区溢出,这是一种危险的编程情况。您应该分配比1个字节更多的内存,并跟踪字符串的长度。到达分配空间的末端后,您可以致电realloc
将内存块分配到更大的尺寸。
int main(/*dont write what you dont use*/)
{
char *a=malloc(4/*at least*/*sizeof(char));
a[0]='a';
a[1]='b';
a[2]='c';
a[3]=' ';
//or strcpy(a, "abc");
printf("%cn",a[0]);
printf("%cn",a[1]);
printf("%cn",a[2]);
printf("%sn",a);
free(a);
return 0;
}
记得:当您写"?"时编译器获取
{?,' 0'}
' 0'是弦乐结束。