我是一个 C 新手,我来自 C#。我一直在学习内存管理和malloc()
功能。我也遇到过这段代码:
char *a_persons_name = malloc(sizeof(char) + 2);
我不明白这是分配了多少空间a_persons_name
。它是分配2个字符(例如AB)还是其他?
我也知道您有时可以“幸运”malloc
并使用未分配的空间(这可能导致数据损坏和段错误)。那么我怎么知道我分配了多少空间以及我需要多少空间呢?
我是一个 C 新手,我来自 C#。我一直在学习内存管理和malloc()
功能。我也遇到过这段代码:
char *a_persons_name = malloc(sizeof(char) + 2);
我不明白这是分配了多少空间a_persons_name
。它是分配2个字符(例如AB)还是其他?
我也知道您有时可以“幸运”malloc
并使用未分配的空间(这可能导致数据损坏和段错误)。那么我怎么知道我分配了多少空间以及我需要多少空间呢?
该片段为 2 个字符的名称分配了足够的空间。
通常,字符串缓冲区将从某处被填充,即 I/O。如果事先不知道字符串的大小(例如从文件或键盘读取),通常使用以下三种方法之一:
为任何给定字符串定义最大大小,分配该大小 + 1(用于空终止符),最多读取那么多字符,如果提供了太多字符,则错误或盲目截断。不是非常用户友好。
分阶段重新分配(最好使用几何级数,例如加倍,以避免二次行为),并继续阅读直到到达终点。不是很容易编码。
分配一个固定大小并希望它不会被超过,并且当这个假设失败时会可怕地崩溃(或被拥有)。易于编码,易于破解。例如,参见gets
标准 C 库。(切勿使用此功能。)
好吧,首先,sizeof(char)
总是 1,所以你可以只是malloc(3)
.
您在其中分配的空间足以容纳三个字符。但请记住,您需要一个用于 C 字符串的空终止符。
您往往会找到如下内容:
#define NAME_SZ 30
: : :
char *name = malloc (NAME_SZ+1);
为名称和终止符获得足够的存储空间(请记住,字符串“xyzzy”在内存中存储为:
+---+---+---+---+---+----+
| x | y | z | z | y | \0 |
+---+---+---+---+---+----+
有时使用非基于字符的数组,您会看到:
int *intArray = malloc (sizeof (int) * 22);
这将为 22 个整数分配足够的空间。
malloc()
如果成功,将分配一块内存并返回指向该内存的指针,如果不成功,则返回 NULL。内存块的大小由malloc
's 参数指定,以字节为单位。
运算符以字节为sizeof
单位给出其参数的大小。
char *someString = malloc(sizeof(char) * 50)
这将为不包括 NULL 字符的 49 个字符的字符串(C 风格的字符串必须以 NULL ( '\0'
) 字符终止)分配足够的空间,并指向someString
该内存。
看起来您问题中的代码应该是malloc(sizeof(char) * 2);
,因为sizeof(char) + 2
没有意义。
请注意,sizeof(char)
它保证始终等于 1(字节)——但其他类型(如 long)的内存表示可能因编译器而异。
使用动态分配的内存获得(不)幸运的方式是,如果您尝试在分配的内存之外读/写。
例如,
char *someString = malloc(10);
strcpy(someString, "Hello there, world!");
printf("%s\n", someString);
第一行为 9 个字符和一个 NULL 字符分配了足够的空间。
第二行尝试将 20 个字符(19 + NULL)复制到该内存空间中。这会超出缓冲区,并可能导致一些非常机智的事情,例如覆盖相邻的内存或导致段错误。
第三行可能会起作用,例如,如果在 someString 旁边分配了内存,并且“你好,世界!” 跑进那个内存空间,它可能会打印你的字符串加上下一个内存空间中的任何内容。如果第二个空格是 NULL 终止的,它就会停止——除非它不是,在这种情况下它会漂移并最终出现段错误。
这个例子是一个非常简单的操作,但它很容易出错。C 很棘手——要小心。
第一点 - 永远不要将绝对数字放在 malloc 的参数中,始终使用 sizeof 和倍数是一个好习惯。如上所述,为某些类型分配的内存因编译器和平台而异。为了保证为“blob”类型的数组提供足够的空间,最好使用以下内容:
blob *p_data = malloc(sizeof(blob) * length_of_array);
这样,无论类型是什么,无论它在内存中看起来如何,您都会得到完全正确的数量。
其次,段错误等。C作为一种低级语言,没有边界检查。这意味着没有什么可以检查您正在查看的索引实际上不在数组中。实际上,即使它不属于您的程序,它也不会阻止您在任何地方访问内存(尽管您的操作系统可能,这就是段错误)。这就是为什么每当你在 C 中传递一个数组时,你也需要传递它的长度,以便接收数组的函数知道它有多大。不要忘记“数组”实际上只是指向第一个元素的指针。这在传递字符串时非常无用 - 每个字符串参数都会变成两个参数,因此使用了作弊。任何标准 C 字符串都以 NULL 结尾。字符串中的最后一个字符应为 ASCII 值 0。任何字符串函数都沿着数组工作,直到他们看到然后停止。这样他们就不会超出阵列,但如果由于某种原因它不存在,他们会。那个被理解
strlen("Hello")
是 5,但要存储它,您还需要一个字符。例如:
const char str1 = "Hello";
char *str2 = malloc(sizeof(char) * (strlen(str1) + 1));
strcpy(str2, str1);
是的,sizeof(char) 是不必要的,因为它被定义为 1,但我发现它更清晰,这绝对是一个好习惯。
您的调用malloc
将分配 3 个字节的内存。sizeof(char)
是 1 个字节,2 个字节被明确指出。这为您提供了足够的空间来容纳大小为 2 的字符串(以及终止字符)
这将分配三个字节;1 表示 sizeof(char),再加上 2。只是断章取义地看到那条线,我无法知道为什么它会以这种方式分配,或者它是否正确(对我来说看起来很可疑)。
您需要分配足够的内存来容纳您需要放入其中的任何内容。例如,如果您要分配内存来保存字符串,则需要分配足够的内存来保存预期的最长字符串以及一个用于终止 null 的字节。如果您正在处理 ASCII 字符串,这很简单:每个字符一个字节加一个。如果您使用的是 unicode 字符串,事情会变得更加复杂。