我最近遇到了一本很棒的数据结构书," 数据结构使用C "(c)1991,当地图书馆书籍只售2美元.正如本书的标题所暗示的,本书涵盖了使用C编程语言的数据结构.
我知道这本书已经过时,但可能会包含许多我在其他地方都不会遇到的高级C主题.
确实在5分钟内我找到了一些我不知道的关于C的内容.我遇到了一个关于union
关键字的部分,我意识到我从未使用它,也没见过任何代码.我很高兴能够学到一些有趣的东西并迅速买下这本书.
对于那些不了解联盟是什么的人,本书使用一个好的比喻来解释:
要完全理解联合的概念,有必要检查它的实现.结构可以被视为存储器区域的路线图.它定义了如何解释内存.联合为同一内存区域提供了几种不同的路线图,程序员有责任确定当前使用的路线图.实际上,编译器会分配足够的存储空间来包含union的最大成员.然而,路线图确定了如何解释存储.
我可以很容易地想出我会使用联盟的人为的情况或黑客.(但我对人为的情况或黑客不感兴趣...)
您是否曾使用或看过使用Union解决问题的实现**比不使用Union更优雅**?
如果你快速解释为什么使用union比不使用union更好/更容易,可以增加额外的奖励.
如果我有一个联合,C标准保证联合本身将与最大元素的大小对齐.
union U {
long l;
int i;
short s;
char c[2];
} u;
Run Code Online (Sandbox Code Playgroud)
但它对联盟内各个联盟元素的对齐有什么看法呢?以下表达式是否保证是真的?
(&u.l == &u.i) && (&u.i == &u.s) && (&u.s == &u.c[0])
Run Code Online (Sandbox Code Playgroud) typedef union epoll_data
{
void *ptr;
int fd;
__uint32_t u32;
__uint64_t u64;
} epoll_data_t;
Run Code Online (Sandbox Code Playgroud)
这里int
和__uint32_t
4个字节,而其他8个字节.
当我们设置fd
为a时int
,它是位于前4个字节还是后4个字节,还是依赖于字节序?
有些理由值得赞赏.
假设您必须实现一个static_vector<T, N>
类,它是一个完全存在于堆栈中并且永远不会分配的固定容量容器,并且公开std::vector
类似接口.(Boost提供boost::static_vector
.)
考虑到我们必须为最大实例提供未初始化的存储,在设计内部数据布局时可以有多种选择:N
T
单一成员union
:
union U { T _x; };
std::array<U, N> _data;
Run Code Online (Sandbox Code Playgroud)单身std::aligned_storage_t
:
std::aligned_storage_t<sizeof(T) * N, alignof(T)> _data;
Run Code Online (Sandbox Code Playgroud)数组std::aligned_storage_t
:
using storage = std::aligned_storage_t<sizeof(T), alignof(T)>;
std::array<storage, N> _data;
Run Code Online (Sandbox Code Playgroud)无论选择如何,创建成员都需要使用"放置new
",访问它们将需要一些东西reinterpret_cast
.
现在假设我们有两个非常小的实现static_vector<T, N>
:
with_union
:使用"单一成员union
"方法实施;
with_storage
:使用"单一std::aligned_storage_t
"方法实施.
让我们使用both g++
和clang++
with 执行以下基准测试-O3
.我使用quick-bench.com完成这项任务:
void escape(void* p) …
Run Code Online (Sandbox Code Playgroud) 有一种或多种方法的工会有什么问题吗?还是要注意什么?(我可以看到构造函数/析构函数因精神分裂症原因而存在问题)
我需要一种安全的方法来在任意POD类型之间进行别名,符合ISO-C++ 11,明确考虑n3242或更高版本的3.10/10和3.11.这里有很多关于严格别名的问题,其中大部分都是关于C而不是C++.我找到了一个使用联合的C的"解决方案",可能使用了这个部分
联合类型,包括其元素或非静态数据成员中的上述类型之一
从那我建立了这个.
#include <iostream>
template <typename T, typename U>
T& access_as(U* p)
{
union dummy_union
{
U dummy;
T destination;
};
dummy_union* u = (dummy_union*)p;
return u->destination;
}
struct test
{
short s;
int i;
};
int main()
{
int buf[2];
static_assert(sizeof(buf) >= sizeof(double), "");
static_assert(sizeof(buf) >= sizeof(test), "");
access_as<double>(buf) = 42.1337;
std::cout << access_as<double>(buf) << '\n';
access_as<test>(buf).s = 42;
access_as<test>(buf).i = 1234;
std::cout << access_as<test>(buf).s << '\n';
std::cout << access_as<test>(buf).i << '\n';
}
Run Code Online (Sandbox Code Playgroud)
我的问题是,可以肯定的是,该计划是否符合标准?*
它没有给出任何警告,并且在使用MinGW/GCC 4.6.2进行编译时工作正常: …
一个初级开发人员问我是否可以为具有POD参数的联合重载赋值运算符,以便当将union的实例分配给该类型的变量时,将写入union中的相应数据类型.我回答说我不这么认为,但后来玩了下面的代码.令我惊讶的是这段代码实际编译(在Ubuntu 12.04上使用g ++版本4.6.3)
union unMember
{
float fData;
unsigned int uiData;
unMember():uiData(0) {};
unMember(float data):fData(data) {};
unMember(unsigned int data):uiData(data) {};
operator float() {return fData;};
operator unsigned int() {return uiData;};
unMember& operator=(float data) {fData = data;return *this;};
unMember& operator=(unsigned int data) {uiData = data; return *this;};
float GetFloat() const {return fData;};
};
int main () {
float fTest = 1.0;
unsigned int uiTest = 10;
unMember data = fTest;
unMember data2 = uiTest;
unMember data3 = data2;
float f = data.GetFloat(); …
Run Code Online (Sandbox Code Playgroud) 从其他StackOverflow问题和ISO/IEC草案C++标准标准的§9.5.1中可以看出,使用联合来执行文字reinterpret_cast
数据是未定义的行为.
请考虑以下代码.目标是取整数值,0xffff
并将其解释为IEEE 754浮点中的一系列位.(二进制转换直观地显示了这是如何完成的.)
#include <iostream>
using namespace std;
union unionType {
int myInt;
float myFloat;
};
int main() {
int i = 0xffff;
unionType u;
u.myInt = i;
cout << "size of int " << sizeof(int) << endl;
cout << "size of float " << sizeof(float) << endl;
cout << "myInt " << u.myInt << endl;
cout << "myFloat " << u.myFloat << endl;
float theFloat = *reinterpret_cast<float*>(&i);
cout << …
Run Code Online (Sandbox Code Playgroud) 与C++不同,C没有概念const_cast
.也就是说,没有有效的方法将const限定指针转换为非限定指针:
void const * p;
void * q = p; // not good
Run Code Online (Sandbox Code Playgroud)
首先:这个演员实际上是未定义的行为吗?
无论如何,GCC警告这一点.要制作需要const-cast的"干净"代码(即我可以保证我不会改变内容,但我所拥有的只是一个可变指针),我看到了以下"转换"技巧:
typedef union constcaster_
{
void * mp;
void const * cp;
} constcaster;
Run Code Online (Sandbox Code Playgroud)
用法:u.cp = p; q = u.mp;
.
通过这种联盟抛弃常量的C语言规则是什么?我对C的了解只是非常不完整,但我听说C对联合访问比C++要宽松得多,所以虽然我对这个结构有一种不好的感觉,但我想从标准中得到一个论证(C99我想,虽然如果在C11中这已经改变了,那么知道它会很好.
我试图更好地理解关于联合和共同的初始序列规则的相当令人惊讶的发现.常见的初始序列规则是(class.mem 23):
在具有struct类型T1的活动成员的标准布局并集中,允许读取结构类型T2的另一个union成员的非静态数据成员m,前提是m是T1和T2的公共初始序列的一部分; 行为就像提名T1的相应成员一样.
所以,给定:
struct A {
int a;
double x;
};
struct B {
int b;
};
union U {
A a;
B b;
};
U u;
u.a = A{};
int i = u.b.b;
Run Code Online (Sandbox Code Playgroud)
这是定义的行为,i
应该具有值0
(因为A
并且B
具有第一个成员的CIS,a int
).到现在为止还挺好.令人困惑的是,如果B
被简单的int替换:
union U {
A a;
int b;
};
...
int i = u.b;
Run Code Online (Sandbox Code Playgroud)
根据常见初始序列的定义:
两种标准布局结构类型的通用初始序列是......
所以CIS只能在两个标准布局结构之间应用.反过来:
标准布局结构是使用类键结构或类键类定义的标准布局类.
所以原始类型肯定不符合条件; 也就是说它没有任何CIS,所以A
没有CIS int
.因此标准说第一个例子是定义的行为,但第二个例子是UB.这对我来说根本没有任何意义; 直觉上,编译器至少受原始类型和类的限制.如果这是故意的,是否有任何押韵或理由(可能是对齐相关的)为什么这是有意义的?这可能是一个缺陷吗?