如果我有一段简单的代码,uint32_t那么它可以比使用uint8_t. 据我所知,这是因为 char 可以豁免严格的别名规则。考虑:
using T = uint32_t;
T *a;
T *b;
T *c;
void mult(int num)
{
for (int count = 0; count < num; count++)
{
a[count] = b[count] * c[count];
}
}
Run Code Online (Sandbox Code Playgroud)
https://godbolt.org/z/sW1xnTrhc
这有一个内部循环-01:
.LBB0_2: # =>This Inner Loop Header: Depth=1
mov r8d, dword ptr [rcx + 4*rdi]
imul r8d, dword ptr [rax + 4*rdi]
mov dword ptr [rdx + 4*rdi], r8d
inc rdi
cmp rsi, rdi
jne .LBB0_2
Run Code Online (Sandbox Code Playgroud)
请注意,在这种情况下,它只是加载一个值,进行乘法,存储结果,然后循环。这很好。但是,如果我使用uint8_t …
我读到- 以及它们char *的签名和未签名对应项 - 可以为任何类型别名,而不会违反严格的别名规则。但是,将一个char *点指向一个int变量并将其强制转换char *为 a 会double *违反规则,因为底层对象的类型为int。但如果记忆来自 呢malloc?例如:
#include <stdlib.h>
#include <stdio.h>
int main(void)
{
void *buffer = malloc(32);
unsigned char *ptr = buffer;
*ptr = 10;
*((double *)(ptr + 1)) = 3.14;
*((double *)(ptr + 9)) = 2.718;
printf("*ptr: %d\n", *ptr);
printf("*(ptr + 1): %lf\n", *((double *)(ptr + 1)));
printf("*(ptr + 9): %lf\n", *((double *)(ptr + 9)));
return 0;
} …Run Code Online (Sandbox Code Playgroud) 在这个答案的评论中,据说使用如下的联合将整数分割成它们的字节将是未定义的行为.在那个地方给出的代码是相似的,虽然与此不相同,请注意我是否更改了代码的未定义行为相关方面.
union addr {
uint8_t addr8[4];
uint32_t addr32;
};
Run Code Online (Sandbox Code Playgroud)
到目前为止,我认为这将是一个很好的做法,addr = {127, 0, 0, 1};并得到相应 uint32_t的回报.(我承认根据我的系统的字节顺序,这可能产生不同的结果.但问题仍然存在.)
这是未定义的行为吗?如果是这样,为什么?(我不知道C++中的UB是什么意思是访问非活动的联盟成员.)
C99
C++ 03
然而
结论
uint8_t[4]和uint32_t不是同一类型(我猜,一个严格的混叠的东西)(加上两个都不是POD结构/联合)上面确实是UB?C++ 11
在http://cppquiz.org上解决测试时,我发现了这段有趣的代码:
#include <iostream>
int f(int& a, int& b) {
a = 3;
b = 4;
return a + b;
}
int main () {
int a = 1;
int b = 2;
int c = f(a, a);// note a,a
std::cout << a << b << c;
}
Run Code Online (Sandbox Code Playgroud)
我的问题是这个程序是合法的C++还是不是?我担心严格的混淆.
我问自己,我可以使用严格别名的BSD套接字,而不是通过编译gcc获得未定义的行为吗?
bind(sdListen, (struct sockaddr*)&sockaddr_inIdentifier, sizeof(sockaddr_inIdentifier))
Run Code Online (Sandbox Code Playgroud)
据我所知,这行代码打破了严格的别名规则(并且gcc给了我相同的警告).那么是否有一个计划b,在O3模式下使用套接字而不转向严格的数据?当然没有违反规定?或者我是否必须运行一个可以在所有系统/编译器上运行的套接字系统?
restrict添加到C99 的关键字的主要用途之一是允许编译器将某些内容加载到寄存器中,并假设寄存器将镜像这样加载的变量的状态.特定
void foo1(int * restrict a, int * restrict b) {
(*a)++; (*b)++; (*b)+=(*a);
}
Run Code Online (Sandbox Code Playgroud)
编译器有权假设写入(*b)不会受到影响(*a),从而避免(*a)在其之后重新加载.是否restrict有混淆的任何其他影响?例如,给定:
extern void foo2a(int * restrict q);
extern void foo2b(void);
int x;
int foo2(restrict int *q) {
int z=x;
x++; *q++; x++;
foo2a(&z);
x++; *q++; z++;
foo2b();
x++; *q++; z++;
return x+(*q)+z;
}
Run Code Online (Sandbox Code Playgroud)
是否需要编译器预期增加*q,调用foo2a()和foo2b()可能都会受到干扰x,并且调用可能对" x和"的值"感兴趣" *q?编译器是否需要假设调用foo2a()可能持有其参数 - 即使它被标记restrict,这样foo2b()可以修改z …
c restrict strict-aliasing language-lawyer restrict-qualifier
我建议更改一个库,其公共API目前看起来像这样:
typedef size_t enh; /* handle */
int en_open(enh *handle)
{
struct internal *e = malloc(...);
*handle = (enh)e;
return 0;
}
int en_start(enh handle)
{
struct internal *e = (struct internal*)handle;
return do_something(e);
}
Run Code Online (Sandbox Code Playgroud)
这种用法,来回摆动以size_t打破严格的走样?
为了记录,我struct internal在公共API中提出了一个典型的不透明前向声明,如此Programmers.SE关于相同代码的问题所示.
#include <iostream>
int main(int argc, char * argv[])
{
int a = 0x3f800000;
std::cout << a << std::endl;
static_assert(sizeof(float) == sizeof(int), "Oops");
float f2 = *reinterpret_cast<float *>(&a);
std::cout << f2 << std::endl;
void * p = &a;
float * pf = static_cast<float *>(p);
float f3 = *pf;
std::cout << f3 << std::endl;
float f4 = *static_cast<float *>(static_cast<void *>(&a));
std::cout << f4 << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
我从可靠的编译器中获得以下信息:
me@Mint-VM ~/projects $ g++-5.3.0 -std=c++11 -o pun pun.cpp -fstrict-aliasing -Wall
pun.cpp: In function ‘int main(int, …Run Code Online (Sandbox Code Playgroud) struct Test {
void doAction() {}
};
// Create and save into a void*
void *ptr = new Test;
// Real use through a Test*
Test *t = static_cast<Test *>(ptr);
t->doAction();
// Delete
delete static_cast<Test *>(ptr);
Run Code Online (Sandbox Code Playgroud)
ptr仅用于保存对象的地址,并且该地址仅取消引用该对象的真实类型.
所以除非它被解除引用到一个不相关的类型,否则严格的别名规则是可以的呢?
我已经在Stack Overflow中阅读了很多关于严格别名的QA,但它们都很常见,而且讨论总是倾向于引用C++标准的深层细节,这些细节几乎总是很难理解.特别是在标准时,不要直接说话,而是用泥泞不清楚的方式描述.所以,我的问题可能是这里有大量质量保证的重复,但是,请回答一个具体的问题:
这是做"nonalias_cast"的正确方法吗?:
template<class OUT, class IN>
inline auto nonalias_cast(IN *data) {
char *tmp = reinterpret_cast<char *>(data);
return reinterpret_cast<OUT>(tmp);
}
float f = 3.14;
unsigned *u = nonalias_cast<unsigned *>(&f);
*u = 0x3f800000;
// now f should be equal 1.0
Run Code Online (Sandbox Code Playgroud)
我猜答案是否定的.但是有什么好的解决方法吗?当然,除了禁用严格别名标志.联盟也不是一个方便的选择,除非有一种方法在nonalias_cast函数体内部适合联合黑客.memcpy这里也不是一个选项 - 数据更改应该是同步的.
一个不可能的梦想或一个难以捉摸的现实?
UPD:
好的,既然我们得到了一个否定答案"是否可能?" 问题,我想问你一个困扰我的额外问题:
将如何您解决这个任务?我的意思是有很多实际的任务,更多的是要求"玩一点点"的方法.例如,假设您必须像这样编写IEEE-754浮点转换器.我更关注问题的实际方面:如何有一个解决方法来实现目标?至少在"#$ $的痛苦"方式.
strict-aliasing ×10
c++ ×6
c ×5
api-design ×1
c++11 ×1
gcc ×1
restrict ×1
simd ×1
sockets ×1
type-punning ×1
types ×1
unions ×1