分配指向结构的指针,但不是实际的结构
Allocate pointer to struct, but not actual struct
我使用 malloc
分配了指向 struct
的指针的内存,但我没有为实际结构分配任何内容。但是我可以 access/use struct
.
typedef struct A
{
int w;
int x;
int y;
int z;
}A;
int main(void)
{
A* a = malloc(sizeof(A*)); //Here I allocate memory just to pointer to A
//Why can I do this than?:
a->z = 10;
a->w = 456;
return 0;
}
为什么这样做?
这只是巧合还是它总是应该以这种方式工作?
一些注意事项:
我无法通过非动态分配做到这一点
测试于repl.it
malloc
调用分配请求的字节数和 returns 一个指针
到分配的内存块的开头。
A* a = malloc(sizeof(A*));
您在这里请求 sizeof(A*)
字节,但是您请求的字节数
请求是指向 A
的指针。但是,您需要一个字节的数量
A
对象,可能不一样。这似乎是一个微小的差异,但它
是一个巨大的。你应该做的是:
A *a = malloc(sizeof(A));
甚至更好
A *a = malloc(sizeof *a);
第二个更好,因为你不能犯错误,它总是会传递给
malloc 正确的字节数。当使用 sizeof(<type>)
时很容易制作
错误并在不需要时添加 *
,就像您对 sizeof(A*)
.
所做的那样
I couldn't do that with non-dynamic allocation
好吧,你可以。对于一个简单的结构,您可以在不从中请求内存的情况下对其进行初始化
堆(通过 malloc
):
A a;
a.z = 10;
a.w = 456;
或
A a = { .z = 10, .w = 456 };
也会这样做。
我们通常选择对结构体使用malloc
的原因是结构体
可能非常大,可能需要很多字节的内存。您拥有的 space 数量
与变量函数相比,变量函数的堆栈框架非常小
您在堆部分中拥有的内存。所以最好从
堆使用 malloc
。它还允许您将该指针传递给其他人
函数,即使在请求内存的函数结束之后。
不过你忘记了两件事:
- 经常查看
malloc
的return,可能是NULL
.
最后释放内存。当你不需要内存时:
free(a);
没有人说你应该做什么。这样做:
int main(void)
{
A* a = malloc(sizeof(A)); // Here I allocate an ins6tance of A and set a pointer to it
a->z = 10;
a->w = 456;
return 0;
}
当你这样做时:
A* a = malloc(sizeof(A*));
您没有为 A
的实例分配足够的内存。因此,如果您读取/写入的任何成员恰好超出了实际分配的范围,您将调用 undefined behavior.
对于未定义的行为,您无法准确预测程序的输出。它可能会崩溃,可能会输出奇怪的结果,或者(在您的情况下)它可能看起来工作正常。如何通过进行看似无关的修改(例如额外的局部变量、调用 printf
进行调试或通过使用不同的选项重新编译)来改变未定义的行为清单。
仅仅因为一个程序可能崩溃并不意味着它会。
如果您使用 valgrind 等工具,它可以在您不正确地使用内存时捕获,例如在您的示例中。
实际上这将取决于实际的 CPU 体系结构和操作系统来判断这是否有效:
第一个问题是:什么是sizeof(A)
,什么是sizeof(A *)
?
我不知道 CPU 不等式 sizeof(A) <= sizeof(A *)
是否成立,但 CPUs(或具有外部存储器电路的计算机)至少是可能的.
在这种情况下,您的代码可以正常工作,因为 malloc
分配了足够的内存来保存结构 A
。
如果不给出此条件,则取决于malloc
的实际执行情况。许多实现会舍入 malloc
的参数,因此 malloc(5)
实际上会分配 16 个字节,而不是 5 个字节。
如果 malloc
将分配多达或超过 sizeof(A)
字节,一切都很好。
如果malloc
分配少于sizeof(A)
字节,则取决于分配的内存之后的内存是否使用以及是否存在的问题:
如果分配内存后的内存不存在(例如,在使用 MMU 时未映射),您将收到错误消息,具体取决于系统 - 在大多数现代计算机上,会有一个 "exception" 和您的程序会崩溃。
如果该内存之后的内存被其他数据占用,您将覆盖该数据,这可能会导致错误。
如果该内存之后的内存存在但未使用,您的程序将正常运行。
我使用 malloc
分配了指向 struct
的指针的内存,但我没有为实际结构分配任何内容。但是我可以 access/use struct
.
typedef struct A
{
int w;
int x;
int y;
int z;
}A;
int main(void)
{
A* a = malloc(sizeof(A*)); //Here I allocate memory just to pointer to A
//Why can I do this than?:
a->z = 10;
a->w = 456;
return 0;
}
为什么这样做? 这只是巧合还是它总是应该以这种方式工作? 一些注意事项:
我无法通过非动态分配做到这一点
测试于repl.it
malloc
调用分配请求的字节数和 returns 一个指针
到分配的内存块的开头。
A* a = malloc(sizeof(A*));
您在这里请求 sizeof(A*)
字节,但是您请求的字节数
请求是指向 A
的指针。但是,您需要一个字节的数量
A
对象,可能不一样。这似乎是一个微小的差异,但它
是一个巨大的。你应该做的是:
A *a = malloc(sizeof(A));
甚至更好
A *a = malloc(sizeof *a);
第二个更好,因为你不能犯错误,它总是会传递给
malloc 正确的字节数。当使用 sizeof(<type>)
时很容易制作
错误并在不需要时添加 *
,就像您对 sizeof(A*)
.
I couldn't do that with non-dynamic allocation
好吧,你可以。对于一个简单的结构,您可以在不从中请求内存的情况下对其进行初始化
堆(通过 malloc
):
A a;
a.z = 10;
a.w = 456;
或
A a = { .z = 10, .w = 456 };
也会这样做。
我们通常选择对结构体使用malloc
的原因是结构体
可能非常大,可能需要很多字节的内存。您拥有的 space 数量
与变量函数相比,变量函数的堆栈框架非常小
您在堆部分中拥有的内存。所以最好从
堆使用 malloc
。它还允许您将该指针传递给其他人
函数,即使在请求内存的函数结束之后。
不过你忘记了两件事:
- 经常查看
malloc
的return,可能是NULL
. 最后释放内存。当你不需要内存时:
free(a);
没有人说你应该做什么。这样做:
int main(void)
{
A* a = malloc(sizeof(A)); // Here I allocate an ins6tance of A and set a pointer to it
a->z = 10;
a->w = 456;
return 0;
}
当你这样做时:
A* a = malloc(sizeof(A*));
您没有为 A
的实例分配足够的内存。因此,如果您读取/写入的任何成员恰好超出了实际分配的范围,您将调用 undefined behavior.
对于未定义的行为,您无法准确预测程序的输出。它可能会崩溃,可能会输出奇怪的结果,或者(在您的情况下)它可能看起来工作正常。如何通过进行看似无关的修改(例如额外的局部变量、调用 printf
进行调试或通过使用不同的选项重新编译)来改变未定义的行为清单。
仅仅因为一个程序可能崩溃并不意味着它会。
如果您使用 valgrind 等工具,它可以在您不正确地使用内存时捕获,例如在您的示例中。
实际上这将取决于实际的 CPU 体系结构和操作系统来判断这是否有效:
第一个问题是:什么是sizeof(A)
,什么是sizeof(A *)
?
我不知道 CPU 不等式 sizeof(A) <= sizeof(A *)
是否成立,但 CPUs(或具有外部存储器电路的计算机)至少是可能的.
在这种情况下,您的代码可以正常工作,因为 malloc
分配了足够的内存来保存结构 A
。
如果不给出此条件,则取决于malloc
的实际执行情况。许多实现会舍入 malloc
的参数,因此 malloc(5)
实际上会分配 16 个字节,而不是 5 个字节。
如果 malloc
将分配多达或超过 sizeof(A)
字节,一切都很好。
如果malloc
分配少于sizeof(A)
字节,则取决于分配的内存之后的内存是否使用以及是否存在的问题:
如果分配内存后的内存不存在(例如,在使用 MMU 时未映射),您将收到错误消息,具体取决于系统 - 在大多数现代计算机上,会有一个 "exception" 和您的程序会崩溃。
如果该内存之后的内存被其他数据占用,您将覆盖该数据,这可能会导致错误。
如果该内存之后的内存存在但未使用,您的程序将正常运行。