|
|
1
5
如前所述,在C中,代码具有未定义的行为,因为
一个更有趣的问题是,如果我们更改代码,使其仅在realloc未更改块的情况下尝试继续,会发生什么情况:
对于C2X,有: a proposal N2090 指明 指针出处 当通过整数类型传递时。 在当前的C标准中,有一些与指针来源相关的规则,但它没有说明当指针通过整数类型并返回时会发生什么。
根据该提议,我的代码仍然是未定义的行为:
该建议旨在避免指针出处被中间操作“入侵”
指针来源背景“指针出处”是指指针值与其指向的内存块之间的关联。如果指针值指向对象,则从该值导出的其他指针值(例如,通过指针算术)必须保持在该对象的边界内。 (当然,指针 变量 可能被重新分配以指向不同的对象-从而获得新的来源-这不是我们所说的)。
这不是出现在已编译的可执行文件中的内容,而是编译器在编译期间可以跟踪的内容,以便执行优化。具有不同来源的两个指针可以具有相同的存储器表示(例如,
指针来源提供有用优化机会的简单示例如下:
起源的概念允许优化器在代码上注册未定义的行为
关于指针边界检查的现有规则(C11 6.5.6/8)已经涵盖了这种情况,但在更复杂的情况下,它们不清楚,因此N2090建议。例如
|
|
|
2
1
原始问题中给出的代码调用了未定义的行为,因此编译器有权做任何它想做的事情。下面给出了这种未定义行为形式的一些背景。 然而,给定与您的代码类似但不调用未定义行为的代码,Clang的行为会很奇怪。除非人们认为标准中的某些语言毫无意义,否则clang在这方面似乎是不一致的。有些人希望改变标准,使以下内容引用UB,从而为clang的行为辩护,但我认为这些建议根本上是错误的。
在godbolt上使用选项调用Clang 3.9.0
将无效指针与有效指针进行比较的背景 在某些处理器上,尝试将指针加载到寄存器将导致处理器对其有效性进行一些验证。例如,在80286上,每个指针都包括一个段选择器和一个偏移量,加载段选择器将导致处理器从有效段表中获取一些信息。 有些C实现会在任何时候将指针加载到寄存器中 任何东西 存在许多实现方式,其中将指针加载到寄存器中将是安全的,或者在指针不被解引用的情况下避免“可捕获”寄存器加载(将指针加载到通用寄存器中进行比较比将其加载到段寄存器并将其传输到通用寄存器进行比较便宜),我认为没有理由相信,本标准的作者意图让代码专门针对上述任何一种不应利用以下技术的实现:
因为区块正在缩小)和可能的位置——但是 昂贵——如果出现以下情况,则重新生成指向分配存储中的内容的指针: 物体最终被移动了。尽管如此,因为标准没有 要求任何实现支持此类技术,即使在以下情况下: 这样做不需要任何成本,在一些实现中(甚至是那些 支持将不花费任何费用),他们会不遗余力地不提供支持。 |
|
|
3
0
|
|
|
4
-1
由于与原始代码相同的原因,优化器知道
查看原始代码和代码的IR
这是你的
除了尾部调用和比特广播的顺序不同之外,它们几乎完全相同。这是你的。
这是他们的。
其他一切都一样。
正如谈话中提到的
如果打印出来,您可以看到优化器正在工作
指针很好。这就是问题所在。 |