指针变量只是与某些运算符的整数,还是它们的符号"?

时间:2015-08-17 08:31:35

标签: c++ pointers language-lawyer undefined-behavior

编辑:原来的单词选择令人困惑。术语"符号"比原来要好得多("神秘")。

在关于我以前的C ++问题的讨论中,我被告知指针是

听起来不错!如果没有任何符号,并且指针是其表示,那么我可以执行以下操作。我可以吗?

#include <stdio.h>
#include <string.h>

int main() {
    int a[1] = { 0 }, *pa1 = &a[0] + 1, b = 1, *pb = &b;
    if (memcmp (&pa1, &pb, sizeof pa1) == 0) {
        printf ("pa1 == pb\n");
        *pa1 = 2;
    }
    else {
        printf ("pa1 != pb\n");
        pa1 = &a[0]; // ensure well defined behaviour in printf
    }
    printf ("b = %d *pa1 = %d\n", b, *pa1);
    return 0;
 }

这是一个C和C ++问题。

使用GNU GCC v4.8.3 Compile and Execute C Online进行测试:gcc -O2 -Wall给出了

pa1 == pb                                                                                                                                                                                       
b = 1 *pa1 = 2    

使用GNU GCC v4.8.3 Compile and Execute C++ Online进行测试:g++ -O2 -Wall

pa1 == pb                                                                                                                                                                                       
b = 1 *pa1 = 2        

所以通过b修改(&a)[1]失败与C和C ++中的GCC。

当然,我想根据标准报价给出答案。

编辑:要回应&a + 1对UB的批评,现在a是一个包含1个元素的数组。

相关:Dereferencing an out of bound pointer that contains the address of an object (array of array)

附加说明:术语&#34;神秘&#34;我认为首先是由Tony Delroy here使用的。我借错了。

4 个答案:

答案 0 :(得分:8)

首先要说的是,在一个架构上生成代码的一个编译器上的一个测试样本不是得出该语言行为结论的基础。

c ++(和c)是为便携而创建的通用语言。即在一个系统上用c ++编写的格式良好的程序应该在任何其他系统上运行(禁止调用系统特定的服务)。

曾几何时,由于各种原因,包括向后兼容性和成本,内存映射在所有处理器上都不是连续的。

例如,我曾经在6809系统上编写代码,其中一半内存是通过在内存映射的非分页部分寻址的PIA分页的。我的c编译器能够解决这个问题,因为对于那个编译器来说,指针是一个神秘的&#39;知道如何写入PIA的类型。

80386系列具有寻址模式,其中地址以16字节为一组进行组织。查找FAR指针,您将看到不同的指针算法。

这是c ++中指针开发的历史。并非所有的芯片制造商都表现得很好并且#34;并且语言可以容纳所有这些(通常),而无需重写源代码。

答案 1 :(得分:4)

窃取TartanLlama的报价:

  

[expr.add] / 5&#34; [用于指针添加,]如果指针操作数和结果都指向同一个数组对象的元素,或者指向数组对象的最后一个元素,则评估不得产生溢出;否则,行为未定义。&#34;

因此,编译器可以假设您的指针指向a数组,或者指向结尾的指针。如果它指出一个结束,你不能贬低它。但正如你所做的那样,它肯定不会超过结束,所以它只能在数组内部。

现在你有了你的代码(简化)

b = 1;
*pa1 = 2;

其中pa指向数组a内部,b是一个单独的变量。当您打印它们时,您会得到12,即您为其指定的值。

优化编译器可以解决这个问题,甚至不会将12存储到内存中。它可以打印最终结果。

答案 2 :(得分:3)

如果关闭优化器,代码将按预期工作。

通过使用未定义的指针算法,您正在欺骗优化器。 优化器已经发现没有代码写入b,因此可以安全地将其存储在寄存器中。事实证明,您已经以非标准方式获取了b的地址,并以优化者无法看到的方式修改了该值。

如果您阅读C标准,它会说指针可能是神秘的。 gcc指针不是神秘的。它们存储在普通存储器中,由构成所有其他数据类型的相同类型的字节组成。您遇到的行为是由于您的代码不遵守您所选择的优化级别所规定的限制。

修改

修订后的代码仍然是UB。即使指针值恰好与另一个指针值相同,标准也不允许引用a[1]。因此,优化器 允许将b的值存储在寄存器中。

答案 3 :(得分:2)

C被认为是指针与整数密切相关的语言,其确切关系取决于目标平台。指针和整数之间的关系使得该语言非常适合于低级或系统编程。出于以下讨论的目的,我将这种语言称为“低级别C”[LLC]。

C标准委员会编写了一个不同语言的描述,其中这种关系并未明确禁止,但未以任何有用的方式得到承认,即使实现为目标生成代码也是如此和这种关系有用的应用领域。我将这种语言称为“仅限高级别C”[HLOC]。

在编写标准的时代,大多数自称为C实现的事情都处理了LLC的方言。最有用的编译器处理一种方言,该方言在比HLOC更多的情况下定义了有用的语义,但没有LLC那么多。指针表现得更像整数还是更像抽象神秘实体,取决于使用哪种精确方言。如果正在进行系统编程,那么将C视为将指针和整数视为密切相关是合理的,因为适合于该目的的LLC方言是这样做的,而不这样做的HLOC方言不适用于该目的。然而,在进行高端数字运算时,人们更常使用HLOC的方言,而这些方言无法识别这种关系。

真正的问题,以及如此争论的根源在于LLC和HLOC越来越不同,但两者都被称为C。