登录 立即注册

找到11526个回复

无名啊 27楼回复 无名啊如何理解 C/C++ 中的 指针别名(pointer alias)、restrict、const 的关系呢? (2023-01-27//)

@老虎会游泳,比如,(long *)&y,编译器认为结果是 NULL。。。

老虎会游泳 26楼回复 无名啊如何理解 C/C++ 中的 指针别名(pointer alias)、restrict、const 的关系呢? (2023-01-27//)

@无名啊,如果特别担心,就用联合吧。

#include <stdio.h>

float Q_rsqrt( float number ) {
  union { long l; float f; } i;
  float x2, y;
  const float threehalfs = 1.5F;

  x2 = number * 0.5F;
  y  = number;
  i.f = y;                       // evil floating point bit level hacking
  i.l  = 0x5f3759df - ( i.l >> 1 );               // what the fuck?
  y  = i.f;
  y  = y * ( threehalfs - ( x2 * y * y ) );   // 1st iteration
  y  = y * ( threehalfs - ( x2 * y * y ) );   // 2nd iteration, this can be removed

  return y;
}

int main() {
  printf("%0.7f\n", Q_rsqrt(3.14));
  printf("%0.7f\n", Q_rsqrt(1024.0));
  printf("%0.7f\n", Q_rsqrt(10086.0));
  printf("%0.7f\n", Q_rsqrt(2147483647.0));

  printf("%0.14f\n", Q_rsqrt(3.14));
  printf("%0.14f\n", Q_rsqrt(1024.0));
  printf("%0.14f\n", Q_rsqrt(10086.0));
  printf("%0.14f\n", Q_rsqrt(2147483647.0));

  return 0;
}

代码实际上变简单了。

不过有趣的是,使用联合的版本给出的都是负值(虽然也是正确解,平方根有两个解),不知道符号位的处理和非联合版本有什么不同。

Screenshot_20230127_164309.jpg(144.41 KB)

无名啊 25楼回复 无名啊如何理解 C/C++ 中的 指针别名(pointer alias)、restrict、const 的关系呢? (2023-01-27//)

@老虎会游泳,万一哪天,编译器改成,看到不兼容类型的指针转换,后续都不管对这个对象的任何操作了,咋办?

反正也符合标准。。

无名啊 24楼回复 无名啊如何理解 C/C++ 中的 指针别名(pointer alias)、restrict、const 的关系呢? (2023-01-27//)

@老虎会游泳( long * ) &y 这个是左值吗?解引用后,类型是 long,不是字符类型,与 float 也不是兼容类型,所以应该是未定义行为

我 C/C++ 写得不多,现在看到那篇文章,就更怕编译器没能正确实现 UB 了。。

老虎会游泳 23楼回复 无名啊如何理解 C/C++ 中的 指针别名(pointer alias)、restrict、const 的关系呢? (2023-01-27//)

@无名啊,经过一番思考之后,我还是认为i = * ( long * ) &y没有问题,因为没有生成新的别名,整个表达式应该被视为一个右值。

相反,把它分开的操作反而是有问题的,这违反了严格别名规则。

long i;  long *p_i;
float y; float *p_y;

p_y = &y;
p_i = (long *) p_y;
i = *p_i;

但它应该也没有副作用,因为不涉及对*p_i的写入。


最重要的是,i = * ( long * ) &y的目标是读取y的值,它根本没有任何优化空间。&y意味着y一定得在内存,所以无论怎么优化,结果应该都是正确的。

无名啊 22楼回复 无名啊如何理解 C/C++ 中的 指针别名(pointer alias)、restrict、const 的关系呢? (2023-01-27//)

@老虎会游泳,我没能力修改 gcc,所以就认了。要么加 -fno-strict-aliasing,要么用 memcpy,来防止结果出错。

但这还会影响到性能低下问题(特别是修改了 char * 后,编译器会认为一大堆对象有可能被修改了,所以缓存失效,需要重新读取),所以我要搞懂 restrict

老虎会游泳 21楼回复 无名啊如何理解 C/C++ 中的 指针别名(pointer alias)、restrict、const 的关系呢? (2023-01-27//)
层主 @老虎会游泳 于 2023-01-27 16:16 删除了该楼层。
无名啊 20楼回复 无名啊如何理解 C/C++ 中的 指针别名(pointer alias)、restrict、const 的关系呢? (2023-01-27//)

@老虎会游泳,cppreference 说,这是 UB:

image.png(64.78 KB)

老虎会游泳 18楼回复 无名啊如何理解 C/C++ 中的 指针别名(pointer alias)、restrict、const 的关系呢? (2023-01-27//)

@无名啊,这里没有未定义行为,因为取地址操作会阻止优化。因为&a,所以a必须在内存,不能优化到寄存器。所以该代码没有未定义行为,但存在出现编程错误的风险(如果float和long长度不同)。

float a = 1.0;
long * b = (long *)&a;

*b = 1;
return a;
无名啊 19楼回复 无名啊如何理解 C/C++ 中的 指针别名(pointer alias)、restrict、const 的关系呢? (2023-01-27//)

@老虎会游泳,你看下知乎上的 一个回答

无名啊 17楼回复 无名啊如何理解 C/C++ 中的 指针别名(pointer alias)、restrict、const 的关系呢? (2023-01-27//)

@老虎会游泳,按照标准,可能根本不会发生解引用指向float值的long指针,因为可能已经被优化掉了。。也就没有你后面说的浮点定义如何如何……

无名啊 15楼回复 无名啊如何理解 C/C++ 中的 指针别名(pointer alias)、restrict、const 的关系呢? (2023-01-27//)

@老虎会游泳,如果按 cppreference 所说,应该是未定义行为

转换指针类型没问题,只要不访问就行。(但不访问,转了也没用。可认为不能转)

比如:

float a = 1.0;
long * b = (long *)&a;

*b = 1;
return a;

按照标准,编译器可认为,a 未被修改(因为 *b 不是 a 的兼容类型,所以修改 *b 不应该污染 a),所以优化掉 b,直接返回 1.0。。。

老虎会游泳 16楼回复 无名啊如何理解 C/C++ 中的 指针别名(pointer alias)、restrict、const 的关系呢? (2023-01-27//)

@无名啊,我还是要说,错误行为不是未定义行为。

解引用指向float值的long指针具有明确的定义,因为float的内存表示在IEEE754定义,long的内存表示在C中定义。在特定的实现中,两者的长度可能相同,也可能不同,但当两者长度不同时,错误一定会以规定好的方式发生:float及其后不属于它的4字节会被访问。这只是编程错误,不是未定义行为。

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1N1bkNoZXJyeURyZWFt,size_16,color_FFFFFF,t_70.jpg(11.22 KB)

老虎会游泳 14楼回复 无名啊如何理解 C/C++ 中的 指针别名(pointer alias)、restrict、const 的关系呢? (2023-01-27//)

需要注意的是,错误行为不是未定义行为

char c;
long i;

// 这个行为非常不恰当,会导致紧接着`c`后面的3个字节被访问,这3个字节不属于`c`。
// 但它只是错误行为,不是未定义行为。
// 这个行为会发生什么具有明确的定义,就是`c`所指向的内存地址及其后方3个字节一同被赋值给`i`,在所有平台上都会发生同样的事情。
// 所以,这里不含未定义行为,只含编程错误。
i = * ( long * ) &c;
无名啊 13楼回复 无名啊如何理解 C/C++ 中的 指针别名(pointer alias)、restrict、const 的关系呢? (2023-01-27//)

@老虎会游泳,你看下 cppreference - 指针 - 注解 说的:

尽管任何指向对象的指针能被转型成指向其他类型对象的指针,解引用指向类型异于对象声明类型的指针几乎总是未定义行为。细节见严格别名使用。

老虎会游泳 12楼回复 无名啊如何理解 C/C++ 中的 指针别名(pointer alias)、restrict、const 的关系呢? (2023-01-27//)

@无名啊,我已经对上述问题进行了回答。

老虎会游泳 10楼回复 无名啊如何理解 C/C++ 中的 指针别名(pointer alias)、restrict、const 的关系呢? (2023-01-27//)

@无名啊,把这段代码拆分成多个部分,应该有助于理解为什么没有未定义行为:

long i;  long *p_i;
float y; float *p_y;

p_y = &y; // 只是一个简单的取地址操作,不是未定义行为
p_i = (long *) p_y; // 对指针进行类型转换不是未定义行为,所有指针类型都是互相兼容的
i = *p_i; // i 和 *p_i 类型一致,没有未定义行为

操作的每一步都不含未定义行为,所以整体不含未定义行为。

无名啊 11楼回复 无名啊如何理解 C/C++ 中的 指针别名(pointer alias)、restrict、const 的关系呢? (2023-01-27//)

@老虎会游泳,我比较怕的是未定义行为导致的结果错误,所以想弄清楚别名规则。

看到知乎那篇文章中的第三个例子,我又觉得 restrict 有助于减少性能损耗(修改 char * 导致编译器认为 this 可能被修改,进而没法重复利用缓存好的 this),所以顺便想弄清楚 restrict

老虎会游泳 9楼回复 无名啊如何理解 C/C++ 中的 指针别名(pointer alias)、restrict、const 的关系呢? (2023-01-27//)

@无名啊i = * ( long * ) &y不含未定义行为,因为long i,所以* ( long * )long显然是它的兼容类型。当赋值发生时,类型已经是long了。而把一个float指针转换为long指针显然也不是未定义行为,因为实际上只是绕过了编译器的类型检查,对于代码生成来说相当于什么也没有发生,指针的值没有任何变化。

无名啊 8楼回复 无名啊如何理解 C/C++ 中的 指针别名(pointer alias)、restrict、const 的关系呢? (2023-01-27//)

@老虎会游泳,回复好多啊,我刚看到第一条:

此外,Q_rsqrt()函数中没有未定义行为

未定义行为是:* ( long * ) &y

cppreference - 严格别名 说:

某左值表达式,是某个对象的[cvr修饰][有/无符号]兼容类型/含有第一项的结构体或联合体/字符类型,才能赋值访问,否则为未定义行为。

long 不是 float 的兼容类型,也不是字符类型,所以是未定义行为。

严重时,会产生结果错误/性能低下等后果(见 知乎 - 严格别名(Strict Aliasing)规则是什么? - 严格别名(strict aliasing)为什么讨厌 中的三个例子)

下一页 上一页 (282 / 577页)

11月12日 10:59 星期三

本站由hu60wap6驱动

备案号: 京ICP备18041936号-1