如何理解 C/C++ 中的 指针别名(pointer alias)、restrict、const 的关系呢?
@无名啊,对了,VC++的__thiscall调用约定始终将this指针放在ecx寄存器。
https://learn.microsoft.com/zh-cn/cpp/cpp/thiscall?view=msvc-170
__thiscall 的调用约定用于 x86 体系结构上的 C++ 类成员函数。 它是成员函数使用的默认调用约定。
在 __thiscall 下,被调用方清理堆栈,自变量将从右到左推送到堆栈中。 指针 this 通过寄存器 ECX 传递,而不是在堆栈上传递。
https://blog.csdn.net/xkdlzy/article/details/108873014
小于等于64位的整型或指针类型返回值由RAX传递。
浮点返回值由XMM0传递。
更大的返回值(比如结构体),由调用方在栈上分配空间,并有RCX持有该空间的指针并传递给被调用函数,因此整型参数使用的寄存器依次右移一格,实际只可以利用3个寄存器,其余参数入栈。函数调用结束后,RAX返回该空间的指针。
但函数没有返回值(void),所以不清楚上面的案例中RCX用于什么,x64调用约定没有明说。
编译器认为 this 没被改,但 this->target 可能被改了。。
其实这句话没说错:
因此,每次修改 target,编译器认为 this 也可能随之变化,即 target[0] = t & 0x7; 可能改变了 this 指针。
因为this
(也就是RDX寄存器的值)可能变了,所以才需要重新加载this->target
。
那为什么不需要重新加载this
呢?因为它就在寄存器,所以自然不需要重新加载,直接用就好了。
我还在过年放鞭炮,你们就开始讨论这么高深的内容了。
小米MIX2s(白)
restrict
估计很耗时间。。除下列标出者,任意 C 运算符的运算数求值顺序,包括函数调用表达式的函数参数求值顺序,及任何表达式的子表达式求值顺序都是未指定的。编译器会以任意顺序对其求值,而且在同一表达式被再度求值时可选用另一种顺序。
C 中没有从左到右或从右到左求值的概念,这不会与运算符的从左到右或从右到左结合性混淆:表达式 f1() + f2() + f3() 被分析成 (f1() + f2()) + f3() ,因为 operator+ 的从左到右结合性,但运行时对 f3 的函数调用可以最先、最后,或在 f1() 与 f2() 之间求值。
未定义行为
- 若对一个标量对象的副效应与另一个对同一标量对象的副效应相对无顺序,则行为未定义。
i = ++i + i++; // 未定义行为 i = i++ + 1; // 未定义行为 f(++i, ++i); // 未定义行为 f(i = -1, i = -1); // 未定义行为
- 若一个标量对象上的副效应与另一个使用同一标量对象之值的值计算相对无顺序,则行为未定义。
f(i, i++); // 未定义行为 a[i] = i++; // 未定义行为
- 只要至少一个子表达式的排序容许这种无顺序副效应,就应用上述规则。
我记得,C 只保证在序列点后,所有副作用都被执行。
@无名啊,我刚看到C++20的std::bit_cast,这应该是在C++中实现类型双关的唯二安全方法(另一种安全方法是memcpy)。C的话,联合应该是安全方法。
https://en.wikipedia.org/wiki/Fast_inverse_square_root 给出的安全版本:
// C
# include <stdint.h> // uint32_t
float q_rsqrt(float number)
{
union {
float f;
uint32_t i;
} conv = { .f = number };
conv.i = 0x5f3759df - (conv.i >> 1);
conv.f *= 1.5F - (number * 0.5F * conv.f * conv.f);
return conv.f;
}
// C++20
# include <bit>
# include <limits>
# include <cstdint>
constexpr float Q_rsqrt(float number) noexcept
{
static_assert(std::numeric_limits<float>::is_iec559); // (enable only on IEEE 754)
float const y = std::bit_cast<float>(
0x5f3759df - (std::bit_cast<std::uint32_t>(number) >> 1));
return y * (1.5f - (number * 0.5f * y * y));
}
@无名啊,
this
可能已经优化到了寄存器中。这里是在读取
target
指针的值,mov rdi, QWORD PTR [rdx]
,是以rdx
寄存器的值为内存首地址,连续读取8字节到rdi
寄存器。因为
target
是struct T
的第一个成员,所以它的首地址就是this
。也就是说,this
在rdx
寄存器。所以问题是程序一直在从内存加载
this->target
,而非一直在从内存加载this
。