C 这段代码是否违反了严格的别名规则?
我有一个大的缓冲区,它表示从硬盘加载和解压缩的三维模型文件,该文件后面有一个头和一些顶点、索引和子集数据。起初,我认为我可以计算每个顶点/索引/子集数据开始处的字节偏移量,并简单地将其转换为兼容的指针类型并使用它,但这将打破严格的别名规则。所以一个解决方案是使用memcpy字节来分离顶点/索引/子集数据数组(每个数组都有不同类型的ofc)C 这段代码是否违反了严格的别名规则?,c,type-punning,C,Type Punning,我有一个大的缓冲区,它表示从硬盘加载和解压缩的三维模型文件,该文件后面有一个头和一些顶点、索引和子集数据。起初,我认为我可以计算每个顶点/索引/子集数据开始处的字节偏移量,并简单地将其转换为兼容的指针类型并使用它,但这将打破严格的别名规则。所以一个解决方案是使用memcpy字节来分离顶点/索引/子集数据数组(每个数组都有不同类型的ofc) 您从错误的角度攻击了严格的别名规则。将char数组强制转换到您的结构实际上是一种错误。这不仅是因为别名,还因为路线特性可能不同。不要那样做 你必须反过来做:声
您从错误的角度攻击了严格的别名规则。将
char
数组强制转换到您的结构实际上是一种错误。这不仅是因为别名,还因为路线特性可能不同。不要那样做
你必须反过来做:声明你想要的真正类型的结构,然后使用指向该结构的void*
或char*
指针来读取或复制你的数据
这始终保证有效:
- 字符类型不受严格的别名规则限制
- 将对象指针传递给函数(
或其他)总是确保编译器在调用后不能对该对象的状态做出任何假设,因此他必须重新加载整个对象memcpy
编辑:也许有些困惑来自奇怪的gcc关于“别名规则”的警告。这只是通过指针强制转换的类型双关所产生的问题的一个方面。通常,通过错误类型(字符类型除外)的指针访问对象可能具有未定义的行为。别名只是其中一个可能会出错的问题。避免它。
您从错误的角度攻击了严格的别名规则。将
char
数组强制转换到您的结构实际上是一种错误。这不仅是因为别名,还因为路线特性可能不同。不要那样做
你必须反过来做:声明你想要的真正类型的结构,然后使用指向该结构的void*
或char*
指针来读取或复制你的数据
这始终保证有效:
- 字符类型不受严格的别名规则限制
- 将对象指针传递给函数(
或其他)总是确保编译器在调用后不能对该对象的状态做出任何假设,因此他必须重新加载整个对象memcpy
编辑:也许有些困惑来自奇怪的gcc关于“别名规则”的警告。这只是通过指针强制转换的类型双关所产生的问题的一个方面。通常,通过错误类型(字符类型除外)的指针访问对象可能具有未定义的行为。别名只是其中一个可能会出错的问题。只要避免它。
将
char*
强制转换为指向结构的指针的问题不是严格的别名规则:char
类型不受严格的别名规则约束。也就是说,您可以将写为char
data的数据作为任何其他类型读取,也可以相反地将任何数据作为char数据读取
铸件的问题是对齐。除非您直接从内存分配函数获得char*
(该函数保证为任何数据生成一个充分对齐的指针),否则可能会出现未对齐的风险,从而导致程序崩溃。使用memcpy()
可以解决这个问题。但是,如果您可以确定您的char*
完全对齐,则无需复制
为了避免混淆,这是一个完美的法律规范:
typedef struct Foo {
...
} Foo;
void bar() {
char* buffer = malloc(sizeof(Foo));
fillBuffer(buffer);
Foo* header = (Foo*)buffer; //Ok, buffer is a perfectly aligned pointer.
readHeader(header); //Ok, reading data written as char data does not violate strict aliasing rules.
}
将
char*
强制转换为指向结构的指针的问题不是严格的别名规则:char
类型不受严格的别名规则限制。也就是说,您可以将写为char
data的数据作为任何其他类型读取,也可以相反地将任何数据作为char数据读取
铸件的问题是对齐。除非您直接从内存分配函数获得char*
(该函数保证为任何数据生成一个充分对齐的指针),否则可能会出现未对齐的风险,从而导致程序崩溃。使用memcpy()
可以解决这个问题。但是,如果您可以确定您的char*
完全对齐,则无需复制
为了避免混淆,这是一个完美的法律规范:
typedef struct Foo {
...
} Foo;
void bar() {
char* buffer = malloc(sizeof(Foo));
fillBuffer(buffer);
Foo* header = (Foo*)buffer; //Ok, buffer is a perfectly aligned pointer.
readHeader(header); //Ok, reading data written as char data does not violate strict aliasing rules.
}
是什么让您认为它可能会破坏别名规则?如果指针类型为,则严格别名不适用char@ouah我真的不知道,我不知道如何让这个模型加载器在不调用未定义行为的情况下工作。别名与通过函数调用执行的修改无关,包括
memcpy
。在这里,编译器必须始终假定对象可能通过调用发生了更改。@bolov只要字节有效(例如,4个字节构成有意义的浮点),我就安全了,对吗?你凭什么认为它会破坏别名规则?如果指针类型为char@ouah我真的不知道,我不知道如何在不调用未定义行为的情况下使此模型加载器工作。别名与通过函数调用执行的修改无关,包括memcpy
。在这里,编译器必须始终假定对象可能通过调用发生了更改。@bolov只要字节有效(例如,4个字节构成有意义的浮点),我是安全的,对吗?您在OP问题中做出了这样的评论:别名与通过函数调用进行的修改无关。有任何标准参考来支持这一点吗?例如,void f(float*a){*a=0;}void g(void){int x=0;f((float*)&x)}
从您的注释来看,f
调用不会破坏别名规则?@ouah,访问