我使用一个代码,我将枚举*转换为int*.像这样的东西:
enum foo { ... }
...
foo foobar;
int *pi = reinterpret_cast<int*>(&foobar);
Run Code Online (Sandbox Code Playgroud)
在编译代码(g ++ 4.1.2)时,我收到以下警告消息:
dereferencing type-punned pointer will break strict-aliasing rules
Run Code Online (Sandbox Code Playgroud)
我用Google搜索了这条消息,发现只有在严格的别名优化打开时才会发生这种情况.我有以下问题:
是的,我实际上需要这种别名.
我一直在std::memcpy用来规避严格的混叠很长一段时间.
例如,检查a float,像这样:
float f = ...;
uint32_t i;
static_assert(sizeof(f)==sizeof(i));
std::memcpy(&i, &f, sizeof(i));
// use i to extract f's sign, exponent & significand
Run Code Online (Sandbox Code Playgroud)
但是,这次,我检查了标准,我还没有找到任何可以验证这一点的东西.我所发现的是这个:
对于平凡可复制类型T的任何对象(可能重叠的子对象除外),无论对象是否保持类型T的有效值,组成对象的基础字节([intro.memory])都可以复制到char,unsigned char或std :: byte([cstddef.syn])数组.40如果将该数组的内容复制回对象,则该对象应随后保持其原始值.[例如:
Run Code Online (Sandbox Code Playgroud)#define N sizeof(T) char buf[N]; T obj; // obj initialized to its original value std::memcpy(buf, &obj, N); // between these two calls to std?::?memcpy, obj might be modified std::memcpy(&obj, buf, N); // at this point, each subobject of obj of scalar …
c++ strict-aliasing undefined-behavior language-lawyer c++17
我找到了一个相当奇怪但工作的平方根逼近floats; 我真的不明白.有人能解释一下为什么这段代码有效吗?
float sqrt(float f)
{
const int result = 0x1fbb4000 + (*(int*)&f >> 1);
return *(float*)&result;
}
Run Code Online (Sandbox Code Playgroud)
我测试了一下它输出的值std::sqrt()约为1到3%.我知道Quake III的快速反平方根,我想这里有类似的东西(没有牛顿迭代),但我真的很感激它的工作原理.
假设我有一个带有一些成员变量且没有虚函数的基类:
class Base {
int member;
};
Run Code Online (Sandbox Code Playgroud)
以及从非虚拟方式派生Base并且没有新成员变量的派生类,再次没有虚函数:
class Derived : Base {
};
Run Code Online (Sandbox Code Playgroud)
显然sizeof(Derived)不能小于sizeof(Base).
被sizeof(Derived)要求等于sizeof(Base)?
我们最近在大学里开了一个关于多种语言编程特色的讲座.
讲师写下了以下功能:
inline u64 Swap_64(u64 x)
{
u64 tmp;
(*(u32*)&tmp) = Swap_32(*(((u32*)&x)+1));
(*(((u32*)&tmp)+1)) = Swap_32(*(u32*) &x);
return tmp;
}
Run Code Online (Sandbox Code Playgroud)
虽然我完全理解这在可读性方面也是非常差的风格,但他的主要观点是这部分代码在生产代码中运行良好,直到它们实现了高优化级别.然后,代码将什么都不做.
他说,变量的所有赋值tmp都将由编译器优化.但为什么会这样呢?
我知道有些情况下变量需要声明为volatile,这样编译器就不会触及它们,即使他认为它们永远不会被读或写,但我不知道为什么会发生这种情况.
面试中有人问我这个问题,我真的不明白这里发生了什么。问题是“控制台中将显示什么?”
#include <iostream>
int main()
{
unsigned long long n = 0;
((char*)&n)[sizeof(unsigned long long)-1] = 0xFF;
n >>= 7*8;
std::cout << n;
}
Run Code Online (Sandbox Code Playgroud)
这是怎么回事,一步一步?
我试过这个:
float a = 1.4123;
a = a & (1 << 3);
Run Code Online (Sandbox Code Playgroud)
我得到一个编译错误,说操作数&不能是float类型.
当我做:
float a = 1.4123;
a = (int)a & (1 << 3);
Run Code Online (Sandbox Code Playgroud)
我让程序运行.唯一的事情是按位操作是在舍入后获得的数字的整数表示上完成的.
以下也是不允许的.
float a = 1.4123;
a = (void*)a & (1 << 3);
Run Code Online (Sandbox Code Playgroud)
我不明白为什么int可以投,void*但不是float.
我这样做是为了解决Stack Overflow问题中描述的问题如何使用遗传算法求解线性方程?.
我使用以下代码来从文件中读取数据,作为更大程序的一部分.
double data_read(FILE *stream,int code) {
char data[8];
switch(code) {
case 0x08:
return (unsigned char)fgetc(stream);
case 0x09:
return (signed char)fgetc(stream);
case 0x0b:
data[1] = fgetc(stream);
data[0] = fgetc(stream);
return *(short*)data;
case 0x0c:
for(int i=3;i>=0;i--)
data[i] = fgetc(stream);
return *(int*)data;
case 0x0d:
for(int i=3;i>=0;i--)
data[i] = fgetc(stream);
return *(float*)data;
case 0x0e:
for(int i=7;i>=0;i--)
data[i] = fgetc(stream);
return *(double*)data;
}
die("data read failed");
return 1;
}
Run Code Online (Sandbox Code Playgroud)
现在我被告知使用-O2,我得到以下gcc警告:
warning: dereferencing type-punned pointer will break strict-aliasing rules
谷歌我找到了两个正交的答案:
VS
让我们在一个示例中展示它,其中我们有一个包含主数据的 Data 类、某种指向主数据的索引,并且我们还需要公开索引const的版本。
class Data
{
public:
const std::vector<int>& getPrimaryData() const { return this->primaryData; }
const std::vector<int*>& getIndex() const { return this->index; }
private:
std::vector<int> primaryData;
std::vector<int*> index;
};
Run Code Online (Sandbox Code Playgroud)
这是错误的,因为用户可以轻松修改数据:
const Data& data = something.getData();
const std::vector<int*>& index = data.getIndex();
*index[0] = 5; // oups we are modifying data of const object, this is wrong
Run Code Online (Sandbox Code Playgroud)
原因是 Data::getIndex 应返回的正确类型是:
const std::vector<const int*>&
Run Code Online (Sandbox Code Playgroud)
但是您可以猜测当您尝试以“仅将非常量变体转换为常量变体”的方式编写方法时会发生什么:
// compiler error, can't convert std::vector<int*> to std::vector<const int*> these are unrelated types.
const std::vector<const int*>& …Run Code Online (Sandbox Code Playgroud) 我的程序通过网络接收消息.这些消息被一些中间件(即其他人无法更改的代码)反序列化.我的程序接收到如下所示的对象:
struct Message {
int msg_type;
std::vector<uint8_t> payload;
};
Run Code Online (Sandbox Code Playgroud)
通过检查,msg_type我可以确定消息有效负载实际上是,例如,uint16_t值数组.我想在没有不必要的副本的情况下读取该数组.
我的第一个想法是这样做:
const uint16_t* a = reinterpret_cast<uint16_t*>(msg.payload.data());
Run Code Online (Sandbox Code Playgroud)
但是阅读a似乎违反了标准.这是第3.10.10条:
如果程序试图通过以下类型之一以外的glvalue访问对象的存储值,则行为未定义:
- 对象的动态类型,
- 一个cv限定版本的动态类型的对象,
- 与对象的动态类型类似的类型(如4.4中所定义),
- 与对象的动态类型对应的有符号或无符号类型的类型,
- 一种类型,是有符号或无符号类型,对应于对象动态类型的cv限定版本,
- 聚合或联合类型,包括其元素或非静态数据成员中的上述类型之一(递归地,包括子聚合或包含联合的元素或非静态数据成员),
- 一个类型,它是对象动态类型的(可能是cv限定的)基类类型,
- a
char或unsigned char类型.
在这种情况下,a将是glvalue并且uint16_t*似乎不符合任何列出的标准.
那么如何在uint16_t不调用未定义的行为或执行不必要的副本的情况下将有效负载视为值数组?
c++ ×9
c ×3
optimization ×3
c++17 ×1
endianness ×1
gcc ×1
ieee-754 ×1
inheritance ×1
pointers ×1
sizeof ×1
warnings ×1