因为我在讨论中被告知"通过不兼容的指针类型进行别名是未定义的行为"(例如,double d; int *p = (int *)&d;以下问题:
它是否允许转换(double *)为(double **),例如double *d1; double **d2 = &d2 和使用类似于d2[0][y]期望的语法d1[y]?
我知道它不是通过不兼容的指针类型完全混淆,但是我不确定.背景是我想要一个在二维数组(=图像)上运行的函数,但我希望能够只传递图像的一行或一列.
struct test
{
char member1;
char member2;
};
int main(void)
{
struct test structure[] = {'h', 'i'};
static void* p = &structure;
printf("%i", *((int*)p));
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我们都知道structure应该指向struct中第一个元素的地址.为什么通过这样取消引用它,它会返回地址本身呢?
我发现这个 "快速strlen函数"实现:
// for x86 only
size_t my_strlen(const char *s) {
size_t len = 0;
for(;;) {
unsigned x = *(unsigned*)s;
if((x & 0xFF) == 0) return len;
if((x & 0xFF00) == 0) return len + 1;
if((x & 0xFF0000) == 0) return len + 2;
if((x & 0xFF000000) == 0) return len + 3;
s += 4, len += 4;
}
}
Run Code Online (Sandbox Code Playgroud)
这里使用的优化技术显然很简单:通过自然CPU字读取内存(代码是旧的并假定为x32 CPU),而不是简单的字节.
但是这段代码违反了别名规则,因此会导致未定义的行为,这些行为可以由编译器自由优化(那些使代码更快,但是更多).
我现在也看到它不可移植,因为它与little-endian endianness有关.
或者可能是我完全错了,上面的代码是正确的?这对C来说是否正确?对于C++?
我想我真的在问:别名是"传递性的"吗?如果编译器知道A可能是别名B,而B可能是别名C,那么肯定它应该记住A可能因此别名C.也许这个"明显的"传递逻辑不是必需的吗?
一个例子,为了清楚起见.对我来说,最有趣的例子是严格别名问题:
// g++ -fstrict-aliasing -std=c++11 -O2
#include <iostream>
union
{
int i;
short s;
} u;
int * i = &u.i;
int main()
{
u.i = 1; // line 1
*i += 1; // line 2
short & s = u.s;
s += 100; // line 3
std::cout
<< " *i\t" << *i << std::endl // prints 2
<< "u.i\t" << u.i << std::endl // prints 101
;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
克++ 5.3.0,在x86_64(但不铛3.5.0)给出上面的输出,其中*i和u.i给予不同的号码.但是它们应该给出完全相同的数字,因为它i被定义为 …
我的理解是在Basic.lval 11中定义了C++中的严格别名:
(11)如果程序试图通过以下类型之一以外的glvalue访问对象的存储值,则行为未定义:
- (11.1)对象的动态类型,
- (11.2)对象的动态类型的cv限定版本,
- (11.3)与对象的动态类型类似的类型(在conv.qual中定义),
- (11.4)与对象的动态类型对应的有符号或无符号类型的类型,
- (11.5)一种类型,它是有符号或无符号类型,对应于对象动态类型的cv限定版本,
- (11.6)聚合或联合类型,包括其元素或非静态数据成员中的上述类型之一(递归地,包括子聚合或包含联合的元素或非静态数据成员),
- (11.7)一种类型,它是对象的动态类型的(可能是cv限定的)基类类型,
- (11.8)一个
char,unsigned char或std?::?byte类型.
通过我的阅读,每11.8,这总是合法的,因为程序x通过类型的glvalue 访问存储的值unsigned char:
int x = 0xdeadbeef;
auto y = reinterpret_cast<unsigned char*>(&x);
std::cout << y[1];
Run Code Online (Sandbox Code Playgroud)
我很好奇使用指向别名的指针unsigned char:
alignas(int) unsigned char[4] x;
auto y = reinterpret_cast<int*>(x);
*y = 0xdeadbeef;
Run Code Online (Sandbox Code Playgroud)
这是违反严格别名的吗?我的阅读是它不是,但我只是在另一个线程上被告知它是.仅对于basic.lval,在我看来没有UB,因为程序不会尝试访问存储的值:它存储一个新的而不读取它,只要后续读取使用x,就不会发生违规.
我的问题是由于需要double通过void *函数参数正确传递变量。我问,因为在我的机器sizeof(void *)是4,和sizeof(double)是8,铸造一个到另一个好像它应该产生一个问题,但我的编译器(CLANG,对所有的警告)没有给出一个问题的指示,和代码似乎工作美好的。
请注意,我已经看到了这个和这个。他们的标题中有相似的组成词,但没有回答这个特定的问题。
以下是否会导致严格的别名违规错误?或未定义的行为?
// some calling function
double a = 0.000234423;
func1(&a);
...
void func1(void *var)
{
double a = *(double *)(var);
}
Run Code Online (Sandbox Code Playgroud) 如何避免严格别名规则违规,试图修改char*sha256函数的结果.
计算哈希值:
std::string sha = sha256("some text");
const char* sha_result = sha.c_str();
unsigned long* mod_args = reinterpret_cast<unsigned long*>(sha_result);
Run Code Online (Sandbox Code Playgroud)
得到2个64位:
unsigned long a = mod_args[1] ^ mod_args[3] ^ mod_args[5] ^ mod_args[7];
unsigned long b = mod_args[0] ^ mod_args[2] ^ mod_args[4] ^ mod_args[6];
Run Code Online (Sandbox Code Playgroud)
而不是通过concat获得结果两件:
unsigned long long result = (((unsigned long long)a) << 32) | b;
Run Code Online (Sandbox Code Playgroud) 我知道违反严格别名规则是根据C标准的未定义行为.请不要告诉我这是UB,没有什么可谈的.
我想知道下面的代码是否有编译器没有预期的行为(由我在下面定义).
假设的大小float和int为4个字节,并且大端机.
float f = 1234.567; /* Any value here */
unsigned int u = *(unsigned int *)&f;
Run Code Online (Sandbox Code Playgroud)
我在英语单词中的预期行为是"获取float存储的四个字节并按int 原样放入".在代码中它将是这个(我认为这里没有UB):
float f = 1234.567; /* Any value here */
unsigned char *p = (unsigned char *)&f;
unsigned int u = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
Run Code Online (Sandbox Code Playgroud)
我也欢迎实际和具体的例子,为什么,除了按照标准的UB,编译器会有我认为是意想不到的行为.
关于下面的示例,在中f1,不会发生别名,因为p(void*)无法访问,并且p1是访问内存的唯一指针。但是,p1(float*)和p2(int*)之间的指针别名在outside之外f1。
我的问题是,此别名是否合法,也就是说,严格混叠规则是否适用于函数调用?
如果此示例有效,那么如果f1内联了该怎么办?
void f1(void *p)
{
auto* p1 = static_cast<float*>(p);
*p1 = 1.f;
}
int f2()
{
int x = 1;
auto* p2 = &x;
f1(&x);
*p2 = 1;
return *p2;
}
Run Code Online (Sandbox Code Playgroud) 我使用STB库将图像加载到内存中.特定函数stbi_load返回指向a的指针unsigned char,该指针是一个数组.
我很想将新的C++ 17 API用于原始数据,std::byte这将使我更具表现力,让我逐个像素地对原始数据进行别名,或者通过将其转换为不同的数据类型来逐个颜色(不同大小的整数).
现在我尝试了这个:
std::unique_ptr<std::byte[], stbi_deleter>(stbi_load(...));
Run Code Online (Sandbox Code Playgroud)
当然,由于缺乏隐式转换,它不起作用.
然后我尝试了:
std::unique_ptr<std::byte[], stbi_deleter>(
static_cast<std::byte*>(stbi_load(...))
);
Run Code Online (Sandbox Code Playgroud)
同样,它仍然没有奏效.我不得不决定使用reinterpret_cast.并让我怀疑这种转换是否合法.我unsigned char*可以std::byte* 根据严格的别名规则合法地转换为?然后我可以将数据转换为另一种数据类型std::uint32_t*并进行变异吗?这会破坏别名规则吗?