我试图理解动态内存分配的概念。我想知道分配内存后大小的变化。
我的代码如下。
#include <stdio.h>
#include <stdlib.h>
int main(){
char *s;
printf("Size before malloc: %dn", sizeof(s));
s = (char*)malloc(1024 * sizeof(char));
printf("Size after malloc: %dn", sizeof(s));
printf("Size actual: %dn", sizeof(s) * 1024);
s[0] = 'a';
s[1] = 'b';
s[2] = 'c';
s[3] = 'd';
printf("Size by calculation: %d", sizeof(s) / sizeof(char));
return 0;
}
当我执行这段代码时,我得到如下输出:
Size before malloc: 8
Size after malloc: 8
Size actual: 8192
Size by calculation: 8
那么为什么不在分配内存后返回8192作为大小呢?此外,当我在s
中添加字符时,为什么不获得4
呢?
所有变量的大小在其整个生命周期内都是恒定的。任何函数调用都不会改变变量的大小。
那么为什么不在分配内存后返回8192作为大小呢?
因为变量是类型为char *s
的对象,并且该类型的大小在您的系统中不是8192。
sizeof pointer
给出了指针本身的大小,而不是被引用对象的大小。因此,在现代PC机器上,它将是4(32位系统/构建)或8(64位系统/构建)字节。
在C语言中没有可移植的方法来获取动态分配的内存块的大小。
一些言论。
sizeof
给出size_t
类型
printf("Size by calculation: %d", sizeof(s) / sizeof(char));
调用未定义的行为。您需要使用正确的格式:
printf("Size by calculation: %zu", sizeof(s) / sizeof(char));
我总是发现图片在这种情况下很有帮助。
宣言
char *s;
创建一个char *
类型的对象:
char *
+---+
| | s
+---+
带有不确定的值(不一定是0或任何特定的值,每次程序运行时可能不同)。
声明
s = (char*)malloc(1024 * sizeof(char));
为1024个char
对象在内存中的其他位置分配空间,并将该空间中第一个对象的地址分配给s
:
char * char
+---+ +---+
| | s ----> | | s[0]
+---+ +---+
| | s[1]
+---+
| | s[2]
+---+
...
s
变量的大小不会改变,只会改变它的值。在本例中,它的值是s[0]
的地址。
那么为什么不在分配内存后返回8192作为大小呢?此外,当我在s中添加字符时,为什么不得到4呢?
sizeof(s)
给出了指针变量本身的大小,而不是它所指向的对象的大小。你想把这个调用写成
printf("Size actual: %dn", sizeof *s * 1024);
s
的类型是char *
,它在任何现实世界的系统(取决于4或8)上都将大于1。表达式*s
的类型是char
,这就是您在这里要使用的。
进一步当我添加字符到s为什么不得到作为4 ?
您没有向s
添加任何内容(或者更确切地说,它指向的缓冲区)-您只是将新值分配给已经存在的东西。您为1024个char
对象分配了空间,并且仅通过分配元素不会改变该大小。关键是,像
s[1026] = some_value;
不会自动扩展已分配的内存-您只是在内存的范围之外写入。您必须使用realloc
库调用来增加分配块的大小。
另外
printf("Size by calculation: %d", sizeof(s) / sizeof(char));
没有达到您的期望——同样,sizeof(s)
给出了指针变量本身的大小,而不是它所指向的对象的大小。sizeof(*s)
也不能工作,因为它提供的是单个char
对象的大小,而不是整个分配的空间。您必须跟踪您在单独变量中分配了多少空间:
size_t num_elements = 1024;
char *s = malloc( sizeof *s * num_elements );
就像我上面说的,如果你以后需要扩展这个空间,你需要使用realloc
函数:
/**
* Double the size of the allocated block.
*/
char *tmp = realloc( s, sizeof *s * (num_elements * 2) );
if ( tmp )
{
s = tmp;
num_elements *= 2;
}
如果realloc
不能成功扩展缓冲区,它将返回NULL
并保留原始缓冲区。因此,我们希望将结果赋值给一个临时变量——我们不想冒s
被设置为NULL
的风险,从而失去对该内存的唯一引用。同样,在我们知道realloc
调用成功之前,我们不想更新大小。
一些样式说明-
从C89开始,对malloc
(以及calloc
和realloc
)的强制转换是不必要的,通常被认为是不好的做法。更常见的做法是
T *p = malloc( sizeof *p * N ); // for any type T
由于表达式*p
的类型为T
,因此sizeof *p
等价于sizeof (T)
。这样,您就不会在单个malloc
调用中多次重复类型信息,从而减轻了维护负担(如果T发生变化,需要更新的内容更少)。在c++中不是为真,并且在malloc
,calloc
和realloc
上仍然需要强制转换,但是如果你正在编写c++,你首先不应该使用C风格的内存管理。
sizeof
是一个操作符,而不是一个函数——只有当操作数是类型名或包含算术运算符或其他优先级较低的操作符的表达式时,才需要括号。例如,sizeof x+y
解析为
(sizeof x) + y
如果你想要表达式x+y
的大小,那么你需要写
sizeof (x+y)