让我先说一下,我是一个新手,我在学校的入门级C班。
我正在编写一个程序,要求我使用malloc和malloc在所有情况下分配8倍我期望的空间。即使只是对malloc(1),它是分配8个字节而不是1,我很困惑为什么。
这是我测试过的代码。这应该只允许输入一个字符加上转义字符。相反,我可以输入8,因此它分配8 bytes
而不是1
,即使我只使用malloc()
中的整数也是如此。请忽略x
变量,它在实际程序中使用,但在此测试中不使用。 :
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main (int argc ,char* argv[]){
int x = 0;
char *A = NULL;
A=(char*)malloc(sizeof(char)+1);
scanf("%s",A);
printf("%s", A);
free(A);
return 0;
}
答案 0 :(得分:6)
A=(char*)malloc(sizeof(char)+1);
将分配至少2个字节(sizeof(char)始终为1)。 我不明白你是如何确定它分配8个字节,但是malloc被允许分配比你要求的更多的内存,只是永远不会少。
您可以使用scanf将更长的字符串写入A指向的内存并不意味着您已分配了该内存。它会覆盖那里的任何内容,这可能会导致程序崩溃或产生意外结果。
答案 1 :(得分:5)
malloc
正在分配你所要求的内存。
如果你可以读取超过分配的字节数(使用scanf
),那是因为scanf
也读取你拥有的内存:这是缓冲区溢出。
您应该限制数据scanf
可以这样读取:
scanf( "%10s", ... ); // scanf will read a string no longer than 10
答案 2 :(得分:3)
我正在编写一个需要我的程序 使用malloc和malloc正在分配 8倍于我所期望的空间 案例。即使只是为了malloc(1),它 分配8个字节而不是1个, 我很困惑为什么。
从理论上讲,你在程序中做事的方式不是分配8 bytes
。
您仍然可以键入8个字节(或任意数量的字节),因为在C中没有检查,您仍然使用有效的写入位置。
你看到的是Undefined Behaviour
,其原因是你在记忆中写下你不应该写的。您的代码中没有任何内容在您分配的n
字节被使用后停止程序。
您现在或以后可能会获得Seg Fault
,或者永远不会。这是未定义的行为。仅仅因为它似乎有效,并不意味着它是正确的。
现在,您的程序确实可以分配8个字节而不是1个。
原因是Alignment
同一程序可能会在不同的计算机和/或不同的操作系统中分配不同的大小。
此外,由于您使用C
,因此您不需要强制转换。请参阅this作为开始。
答案 3 :(得分:2)
在您的代码中,使用scanf
加载的数据量没有限制,导致缓冲区溢出(安全漏洞/崩溃)。您应该使用格式字符串将读取的数据量限制为您分配的一个或两个字节。 malloc
函数可能会分配一些额外的空间来扩大规模,但你不应该依赖它。
答案 4 :(得分:1)
你在运行什么系统?如果是64位,系统可能会分配尽可能小的单位。 64位是8个字节。
编辑:只是感兴趣的一点:
char *s = malloc (1);
在iOS 4.2(Xcode 3.2.5)上分配16个字节。
答案 5 :(得分:1)
允许malloc分配比你要求更多的内存。它只需要提供至少,就像你要求的那样,或者如果不能提供则会失败。
答案 6 :(得分:1)
使用malloc或在堆栈上创建缓冲区将以单词分配内存。
在32位系统上,字大小为4字节,因此当您要求
时 A=(char*)malloc(sizeof(char)+1);
(基本上是A=(char*)malloc(2);
系统实际上会给你4个字节。在64位计算机上,您应该获得8个字节。
使用scanf
的方式存在危险,因为如果字符串大于分配的大小,它会溢出缓冲区,从而在程序中留下堆溢出漏洞。在这种情况下,scanf
将尝试将任意长度的字符串填充到该内存中,因此使用它来计算分配的大小将不起作用。
答案 7 :(得分:0)
如果您输入8
,如果只分配2个字节sizeof(char) == 1 (unless you are on some obscure platform)
,您将向该字母写入数字。然后在printf
,它将输出您存储在那里的数字。因此,如果存储数字8,它将在命令行上显示8。它与分配的字符数无关。
当然,除非你在调试器或其他地方查找它实际上是在分配8个字节。
答案 8 :(得分:0)
scanf
不知道目标缓冲区实际有多大。它只知道缓冲区的起始地址。 C没有边界检查,所以如果你传递一个大小为2个字符的缓冲区的地址,并输入一个长度为10个字符的字符串,scanf
将在结束后将这些额外的8个字符写入内存。缓冲区。这称为缓冲区溢出,这是一种常见的恶意软件漏洞。无论出于何种原因,紧跟缓冲区后的六个字节不是“重要”,因此您最多可以输入8个字符,没有明显的不良影响。
您可以通过在转化说明符中包含显式字段宽度来限制scanf
调用中读取的字符数:
scanf("%2s", A);
但是仍然需要确保目标缓冲区足够大以容纳该宽度。不幸的是,没有办法像printf
一样动态指定字段宽度:
printf("%*s", fieldWidth, string);
因为%*s
在scanf
中意味着完全不同(基本上,跳过下一个字符串)。
您可以使用sprintf
来构建格式字符串:
sprintf(format, "%%%ds", max_bytes_in_A);
scanf(format, A);
但你必须确保缓冲区format
足够宽以容纳结果等等,等等。
这就是我通常建议fgets()
进行交互式输入的原因。