char 数组可以用于任何数据类型吗?
Can a char array be used with any data type?
malloc()
函数returns一个void*
类型的指针。它根据作为参数传递给它的 size_t
值以字节为单位分配内存。生成的分配是原始字节,可用于 C 中的任何数据类型(无需强制转换)。
在函数 returns void *
中声明类型为 char
的数组是否可以与任何数据类型一起使用,例如 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;
}
不,由于可能存在对齐问题,您不能将任意字节数组用于任意类型。标准在 6.3.2.3 Conversions/Pointers(强调我的)中说:
A pointer to an object or incomplete type may be converted to a pointer to a different
object or incomplete type. If the resulting pointer is not correctly aligned for the
pointed-to type, the behavior is undefined. Otherwise, when converted back again, the
result shall compare equal to the original pointer.
作为最小对齐要求的 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 中定义的严格别名规则:
An object shall have its stored value accessed only by an lvalue expression that has one of
the following types:
- a type compatible with the effective type of the object,
- a qualified version of a type compatible with the effective type of the object,
- a type that is the signed or unsigned type corresponding to the effective type of the
object,
- a type that is the signed or unsigned type corresponding to a qualified version of the
effective type of the object,
- an aggregate or union type that includes one of the aforementioned types among its
members (including, recursively, a member of a subaggregate or contained union), or
- a character type.
所以恕我直言,不使用 malloc
.
构建自定义 allocator 没有可移植且符合标准的方法
静态对象 Array
的声明类型是 char
。该对象的有效类型是它声明的类型。静态对象的有效类型无法更改,因此对于程序的其余部分,Array
的有效类型是 char
.
如果您尝试访问类型不兼容或不在此列表1 上的对象的值,则行为未定义。
您的代码尝试使用类型 int
访问 Array
的存储值。此类型与类型 char
不兼容,不在异常列表中,因此当您使用 int
指针读取数组时行为未定义 p
:
printf("%d ", p[n]);
1 (引自:ISO:IEC 9899:201X 6.5 表达式 7 )
对象的存储值只能由左值访问
具有以下类型之一的表达式:
- 一种
兼容对象的有效类型,
— 合格的
与对象的有效类型兼容的类型版本,
— 一个类型,它是对应于
对象的有效类型,
— 有符号或无符号的类型
对应于有效类型的合格版本的类型
对象,
— 包含以下之一的聚合或联合类型
其成员中的上述类型(递归地包括
子聚合或包含联合的成员),或
— 一种字符类型。
如果阅读 C89 标准的基本原理,类型别名规则存在的唯一原因是避免要求编译器生成 "worst-case aliasing assumptions"。给定的例子是:
int a;
void f( double * b )
{
a = 1;
*b = 2.0;
g(a);
}
如果程序在联合中创建一个 "char" 数组,其中包含其对齐方式适用于任何类型的内容,获取其地址,并且除了通过结果指针之外从不访问该结构的存储,则应该别名规则没有理由造成任何困难。
值得注意的是,该标准的作者认识到一个实现可能同时合规但无用;查看 C89 2.2.4.1 的基本原理:
While a deficient implementation could probably contrive a program that meets this requirement, yet still succeed in being useless, the Committee felt that such ingenuity would probably require more work than making something useful. The sense of the Committee is that implementors should not construe the translation limits as the values of hard-wired parameters, but rather as a set of criteria by which an implementation will be judged.
虽然该特定声明是关于实施限制的,但将 C89 解释为与之前的 C 方言甚至远程兼容的唯一方法是将其视为更广泛的应用:标准不' 尝试详尽地指定程序应该能够做的所有事情,但依赖于编译器编写者运用一些常识。
使用字符类型数组作为任何类型的后备存储,假设确保对齐问题得到处理,不应该导致编写不笨拙的编译器出现任何问题。标准没有强制编译器作者允许这样的事情,因为他们认为没有理由期望他们做其他事情。不幸的是,他们未能预见到该语言在 21 世纪所走的道路。
malloc()
函数returns一个void*
类型的指针。它根据作为参数传递给它的 size_t
值以字节为单位分配内存。生成的分配是原始字节,可用于 C 中的任何数据类型(无需强制转换)。
在函数 returns void *
中声明类型为 char
的数组是否可以与任何数据类型一起使用,例如 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;
}
不,由于可能存在对齐问题,您不能将任意字节数组用于任意类型。标准在 6.3.2.3 Conversions/Pointers(强调我的)中说:
A pointer to an object or incomplete type may be converted to a pointer to a different object or incomplete type. If the resulting pointer is not correctly aligned for the pointed-to type, the behavior is undefined. Otherwise, when converted back again, the result shall compare equal to the original pointer.
作为最小对齐要求的 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 中定义的严格别名规则:
An object shall have its stored value accessed only by an lvalue expression that has one of the following types:
- a type compatible with the effective type of the object,
- a qualified version of a type compatible with the effective type of the object,
- a type that is the signed or unsigned type corresponding to the effective type of the object,
- a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object,
- an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), or
- a character type.
所以恕我直言,不使用 malloc
.
静态对象 Array
的声明类型是 char
。该对象的有效类型是它声明的类型。静态对象的有效类型无法更改,因此对于程序的其余部分,Array
的有效类型是 char
.
如果您尝试访问类型不兼容或不在此列表1 上的对象的值,则行为未定义。
您的代码尝试使用类型 int
访问 Array
的存储值。此类型与类型 char
不兼容,不在异常列表中,因此当您使用 int
指针读取数组时行为未定义 p
:
printf("%d ", p[n]);
1 (引自:ISO:IEC 9899:201X 6.5 表达式 7 )
对象的存储值只能由左值访问
具有以下类型之一的表达式:
- 一种
兼容对象的有效类型,
— 合格的
与对象的有效类型兼容的类型版本,
— 一个类型,它是对应于
对象的有效类型,
— 有符号或无符号的类型
对应于有效类型的合格版本的类型
对象,
— 包含以下之一的聚合或联合类型
其成员中的上述类型(递归地包括
子聚合或包含联合的成员),或
— 一种字符类型。
如果阅读 C89 标准的基本原理,类型别名规则存在的唯一原因是避免要求编译器生成 "worst-case aliasing assumptions"。给定的例子是:
int a;
void f( double * b )
{
a = 1;
*b = 2.0;
g(a);
}
如果程序在联合中创建一个 "char" 数组,其中包含其对齐方式适用于任何类型的内容,获取其地址,并且除了通过结果指针之外从不访问该结构的存储,则应该别名规则没有理由造成任何困难。
值得注意的是,该标准的作者认识到一个实现可能同时合规但无用;查看 C89 2.2.4.1 的基本原理:
While a deficient implementation could probably contrive a program that meets this requirement, yet still succeed in being useless, the Committee felt that such ingenuity would probably require more work than making something useful. The sense of the Committee is that implementors should not construe the translation limits as the values of hard-wired parameters, but rather as a set of criteria by which an implementation will be judged.
虽然该特定声明是关于实施限制的,但将 C89 解释为与之前的 C 方言甚至远程兼容的唯一方法是将其视为更广泛的应用:标准不' 尝试详尽地指定程序应该能够做的所有事情,但依赖于编译器编写者运用一些常识。
使用字符类型数组作为任何类型的后备存储,假设确保对齐问题得到处理,不应该导致编写不笨拙的编译器出现任何问题。标准没有强制编译器作者允许这样的事情,因为他们认为没有理由期望他们做其他事情。不幸的是,他们未能预见到该语言在 21 世纪所走的道路。