将TYPE*转换为unsigned char*是允许的吗?

7
C99 -- 特别是第6.2.6.1节第4段 -- 规定将对象表示复制到无符号字符数组中是允许的:
struct {
    int foo;
    double bar;
} baz;
unsigned char bytes[sizeof baz];

// Do things with the baz structure.

memcpy(bytes, &baz, sizeof bytes);

// Do things with the bytes array.

我的问题是:我们是否可以通过简单的强制类型转换来避免额外的内存分配和拷贝操作?例如:

struct {
    int foo;
    double bar;
} baz;
unsigned char *bytes = (void *)&baz;

// Do stuff with the baz structure.

// Do things with the bytes array.

当然,需要跟踪大小,但这首先是否合法,或者它属于实现定义或未定义的行为范畴?
我提出这个问题是因为我正在实现类似qsort的算法,并且希望它适用于任何类型的数组,就像qsort一样。

据我所知,char 别名一切,所以这应该没问题。 - EOF
这得看情况。通常情况下是没问题的,但我知道在GCC中有一个标志可以通过假设指向不同类型的指针不能在同一内存空间来进行更好的优化。像这样进行强制转换,您将无法利用它。除非/直到这成为性能瓶颈,否则我会避免使用它。 - Dave
@Dave -fstrict-aliasing?这对char没有影响。 - EOF
1
另一种方式会很糟糕。这种方式可以接受。 - Deduplicator
@EOF 就是我想到的那个。所以对于 char 来说没问题了吗?我找不到任何文档来确认这一点... - Dave
只有当底层对象是匿名内存或struct是与声明类型兼容的类型时,才能成立。 - Deduplicator
1个回答

6

6.5 表达式

[...]
6 对于访问其存储值的对象,如果有声明类型,则该对象的有效类型为其声明类型。87)如果通过具有非字符类型的lvalue存储到没有声明类型的对象中,则该lvalue的类型成为该访问和不修改存储值的后续访问的对象的有效类型。如果使用memcpy或memmove将值复制到没有声明类型的对象中,或者将其作为字符类型的数组复制,则对于该访问和不修改值的后续访问,已修改对象的有效类型是从复制值的对象的有效类型,如果它有一个,则为该对象的有效类型。对于所有其他访问没有声明类型的对象的情况,用于访问的lvalue的有效类型仅为该对象的类型。
7 对象的存储值只能由以下类型之一的lvalue表达式访问:88)

  • 与对象的有效类型兼容的类型
  • 与对象的有效类型兼容的类型的限定版本
  • 与对象的有效类型相应的带符号或无符号类型
  • 与对象的有效类型的限定版本相应的带符号或无符号类型
  • 包含上述类型之一的聚合体或联合体类型(包括子聚合体或包含联合体的递归成员),或
  • 字符类型

强调是我的。因此,您可以将任何类型视为字符数组(unsigned char[]char[]signed char[])。

我还引用了第6段,因为它使反向不适用。


换句话说,只要我能确保缓冲区中的数据与所转换的对象类型的对象表示完全匹配,就不需要进行复制操作。非常感谢您指出了标准中相关的部分;您确实为我澄清了这一点。 - user539810
1
不完全正确。如果您将数据放入一个声明为char[]类型的数组中,您不能像引用结构体那样引用它。但是,如果您使用匿名内存,就可以这样做。 - Deduplicator
匿名内存是通过malloc或其他类似函数分配的内存,对吗?因此,struct_ptr = (void *)char_buffer;是不正确的,因为char_buffer的_effective type_是字符类型,而这不是与结构体类型兼容的类型。或者我错过了什么? - user539810
1
@ChronoKitsune:没错,就是这个。不过如果你使用匿名内存,请注意误对齐问题(由malloc等返回的任何块的开头都适当地对齐于任何类型)。 - Deduplicator

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接