malloc()
函数返回类型为void*
的指针。它根据作为参数传递的size_t
值以字节为单位分配内存。生成的分配是原始字节,可以与C中的任何数据类型一起使用(无需转换)。
在返回char
的函数中声明类型为void *
的数组是否可以与任何数据类型一起使用,例如malloc
的结果分配?
例如,
#include <stdio.h>
void *Stat_Mem();
int main(void)
{
//size : 10 * sizeof(int)
int buf[] = { 1,2,3,4,5,6,7,8,9,10 };
int *p = Stat_Mem();
memcpy(p, buf, sizeof(buf));
for (int n = 0; n < 10; n++) {
printf("%d ", p[n]);
}
putchar('\n');
return 0;
}
void *Stat_Mem()
{
static char Array[128];
return Array;
}
答案 0 :(得分:5)
静态对象Array
的声明类型为char
。该对象的有效类型是它的声明类型。无法更改静态对象的有效类型,因此对于程序的其余部分,Array
的有效类型为char
。
如果您尝试访问类型与此列表 1 不兼容的对象的值,则行为未定义。
您的代码尝试使用Array
类型访问int
的存储值。此类型与类型char
不兼容,并且不在异常列表中,因此当您使用int
指针p
读取数组时,行为未定义:
printf("%d ", p[n]);
1 (引自:ISO:IEC 9899:201X 6.5表达式7)
对象的存储值只能由左值访问
具有以下类型之一的表达式:
- 一种
与对象的有效类型兼容,
- 合格的
与对象的有效类型兼容的类型的版本,
- 对应于的有符号或无符号类型的类型
有效的对象类型,
- 签名或未签名的类型
类型对应于有效类型的限定版本
对象,
- 包含其中一个的聚合或联合类型
其成员之间的上述类型(包括,递归,a
分包或联合的成员)或
- 字符类型。
答案 1 :(得分:3)
不可以因为可能的对齐问题而无法将任意字节数组用于任意类型。标准在6.3.2.3转换/指针(强调我的)中说:
指向对象或不完整类型的指针可能会转换为指向其他对象的指针 对象或不完整的类型。 如果生成的指针未正确对齐 指向类型,行为未定义。否则,当再次转换回来时, 结果应与原始指针进行比较。
作为最小对齐要求的char,您无法确保您的char数组将正确对齐任何其他类型。这就是为什么malloc保证malloc获得的缓冲区(即使它是void *
)具有最大可能的对齐要求,以便能够接受任何其他类型。
我认为
union {
char buf[128];
long long i;
void * p;
long double f;
};
应该对任何类型都有正确的对齐方式,因为它与最大的基本类型兼容(如6.2.5类型中所定义)。我很确定它适用于所有常见的实现(gcc,clang,msvc,...)但遗憾的是我无法找到标准允许的任何确认。基本上是因为6.5表达式§7中定义的严格别名规则:
对象的存储值只能由具有其中一个的左值表达式访问 以下类型:
- 与对象的有效类型兼容的类型
- 与对象的有效类型兼容的类型的限定版本,
- 对应于有效类型的有符号或无符号类型的类型 对象,
- 对应于合格版本的有符号或无符号类型的类型 有效的对象类型,
- 聚合或联合类型,其中包含上述类型之一 成员(包括,递归地,子集合或包含的联合的成员),或
- 字符类型。
所以恕我直言,没有使用malloc
构建自定义分配器的可移植和标准符合方式。
答案 2 :(得分:-1)
如果读取C89标准的基本原理,存在类型别名规则的唯一原因是避免要求编译器做出“最坏情况的混叠假设”。给出的例子是:
int a;
void f( double * b )
{
a = 1;
*b = 2.0;
g(a);
}
如果程序在一个联合内创建一个“char”数组,其中包含哪些符号适合任何类型,取其地址,并且永远不会访问该结构的存储,除非通过结果指针,应该没有理由别名规则应该会造成任何困难。
值得注意的是,标准的作者认识到实施可以同时兼容但无用;见C89 2.2.4.1的基本原理:
虽然缺乏执行可能会设法满足这一要求的计划,但仍然没有成功,但委员会认为,这种聪明才智可能需要做更多的工作而不是做一些有用的工作。委员会的意义在于,实施者不应将翻译限制解释为硬连线参数的值,而应将其作为判断实施的一套标准。
虽然该特定声明是关于实施限制的,但将C89解释为与之前的C语言甚至远程兼容的唯一方法是将其视为更广泛地应用:标准不尝试详尽地指出程序应该能够做的所有事情,但依赖于编译器编写者的一些常识。
使用字符类型数组作为任何类型的后备存储,假设有一个确保对齐问题得到处理,不应该导致任何非钝写编译器的问题。标准没有要求编译器编写者允许这样的事情,因为他们认为没有理由期望他们这样做。不幸的是,他们未能预见到21世纪语言的发展方向。