【问题标题】:Why this program that performs invalid pointer initialization compiles fine in C?为什么这个执行无效指针初始化的程序在 C 中编译得很好?
【发布时间】:2014-11-01 17:33:28
【问题描述】:

我写了一个简单的 C 程序,我期待它在编译时会失败,但不幸的是它在 C 中编译并运行良好,但在 C++ 中编译失败。 考虑下面的程序:

#include <stdio.h>
int main()
{
    char *c=333;
    int *i=333;
    long *l=333;
    float *f=333;
    double *d=333;
    printf("c = %u, c+1 = %u",c,c+1);
    return 0;
}

访问此链接:http://ideone.com/vnKZnx

我认为由于 C++ 的强类型检查,这个程序肯定不能在 C++ 中编译。为什么这个程序用 C 编译?这是编译器也显示警告的事实。我正在使用 Orwell Dev C++ IDE(gcc 4.8.1 编译器)。我还在其他编译器(Borland Turbo C++ 4.5)上尝试了相同的程序,通过扩展名 .c 保存它,但在这个编译器上它无法编译。

【问题讨论】:

  • @hacks 程序的哪一部分调用了未定义的行为?只要您不取消引用它,将随机值分配给指针变量是完全有效的。
  • @5gon12eder;这部分:printf("c = %u, c+1 = %u",c,c+1);。需要解释吗?
  • @hacks 没错,%u 应该改为%p。但这与 meet 的问题无关,也没有解释程序编译的原因。
  • @JensGustedt;我承认我的评论具有误导性,因此我将其删除。但我认为有UB,虽然可执行文件无效。
  • @davmac 未定义的行为包括“终止翻译或执行”。

标签: c pointers variable-initialization


【解决方案1】:

这段代码既不是合法的 C 也不是合法的 C++。

N1570 §6.7.9/p11:

标量的初始值设定项应为单个表达式,可选 括在大括号中。对象的初始值是 表达式(转换后);相同的类型约束和 与简单赋值一样的转换适用,采用 标量是其声明类型的非限定版本。

§6.5.16.1/p1 为简单分配提供了:

应满足以下条件之一:

  • 左操作数具有原子、合格或非限定算术类型,右操作数具有算术类型;
  • 左操作数具有与右操作数兼容的结构或联合类型的原子、限定或非限定版本;
  • 左操作数具有原子、限定或非限定指针类型,并且(考虑到左操作数在左值之后的类型 转换)两个操作数都是指向合格或不合格的指针 兼容类型的版本,左边指向的类型有 右边指向的类型的所有限定符;
  • 左操作数具有原子、限定或非限定指针类型,并且(考虑到左操作数在左值之后的类型 转换)一个操作数是指向对象类型的指针,而另一个 是指向void 的合格或不合格版本的指针,并且 左侧指向的类型具有所指向类型的所有限定符 向右;
  • 左边的操作数是一个原子的、合格的或不合格的指针,右边是一个空指针常量;或
  • 左侧操作数的类型为 atomic、合格或不合格 _Bool,右侧是指针。

它们都不匹配左侧的指针和右侧的333。 §6.5.16.1/p1 是一个约束,并且需要符合要求的实现来在违反约束时产生诊断(§5.1.1.3/p1):

符合要求的实现应至少产生一个诊断 消息(以实现定义的方式标识)如果 预处理翻译单元或翻译单元包含一个 违反任何语法规则或约束,即使行为是 也明确指定为未定义或实现定义。

碰巧GCC决定在C模式下产生警告而不是错误并继续编译它,但它不是必须的。

【讨论】:

    【解决方案2】:

    从种族上讲,您的代码没有任何问题。您正在使用整数常量值 333 初始化指针。然后打印地址。它显示的警告可能是因为整数值类型转换为地址类型。

    当您尝试取消引用指针时,问题将开始。它会给出分段错误。

    【讨论】:

    • echnically there is nothing wrong with your code. -1.
    • 至少你的编译器应该发出警告:[Warning] format '%u' expects argument of type 'unsigned int', but argument 2 has type 'char *' [-Wformat]
    • @hacks 对于 gcc 为 true,但其他一些编译器不检查 printf() 参数
    • “种族上没有错” - “种族上”根本不是你的意思。也许是“技术上”。
    • 我一开始写的是“技术上”,因此被否决了.. :(
    【解决方案3】:

    C 可以将数字转换为指针。 char* c = 123 将设置 c 指向内存中的第 123 个字节。

    虽然这在桌面编程中几乎没有用,而且几乎可以肯定是一个错误,但在嵌入式系统中,必须与硬件接口,这可能会在某些硬编码的内存地址中查找值。

    【讨论】:

    • 虽然这是答案的一部分,但它并没有说明为什么以及如何该代码实际上是违反约束的,因此 每个 C 编译器必须至少给出诊断,而且确实 OP 在某处承认它编译得不好,但会发出警告。
    猜你喜欢
    • 2015-08-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-10
    • 2013-02-17
    • 1970-01-01
    • 2015-05-13
    • 1970-01-01
    相关资源
    最近更新 更多