Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/69.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 未定义行为的不同分类意味着什么?_C_Undefined Behavior_C11 - Fatal编程技术网

C 未定义行为的不同分类意味着什么?

C 未定义行为的不同分类意味着什么?,c,undefined-behavior,c11,C,Undefined Behavior,C11,我正在通读C11标准。根据C11标准,未定义行为分为四种不同类型。括号中的数字指的是C标准(C11)中标识未定义行为的子条款 示例1:程序尝试修改字符串文字(6.4.5)。此未定义行为分类为:未定义行为(需要信息/确认) 示例2:评估时,左值不指定对象(6.3.2.1)。此未定义行为分类为:关键未定义行为 示例3:对象的存储值不是由允许类型(6.5)的左值访问的。此未定义行为分类为:有界未定义行为 示例4:调用fopen函数时,mode参数指向的字符串与指定的字符序列之一(7.21.5.3)不完

我正在通读C11标准。根据C11标准,未定义行为分为四种不同类型。括号中的数字指的是C标准(C11)中标识未定义行为的子条款

示例1:程序尝试修改字符串文字(6.4.5)。此未定义行为分类为:未定义行为(需要信息/确认)

示例2:评估时,左值不指定对象(6.3.2.1)。此未定义行为分类为:关键未定义行为

示例3:对象的存储值不是由允许类型(6.5)的左值访问的。此未定义行为分类为:有界未定义行为

示例4:调用
fopen
函数时,
mode
参数指向的字符串与指定的字符序列之一(7.21.5.3)不完全匹配。这种未定义的行为被分类为:可能的一致性语言扩展


分类的含义是什么?这些分类向程序员传达了什么信息?

我只能获得标准草案,但从我阅读的内容来看,这种未定义行为的分类似乎并不是标准所规定的,只是从编译器和环境的角度来看才有意义,这些编译器和环境明确表示他们想要创建更容易分析不同错误类别的C程序。(这些环境必须定义一个特殊符号
\uuuu STDC\u可分析的\uuuuu

这里的关键思想似乎是“越界写入”,它被定义为修改数据的写入操作,而这些数据不是作为对象的一部分分配的。例如,如果您意外地对现有变量的字节进行了重击,这不是越界写入,但是如果您跳转到内存的随机区域并使用您最喜欢的位模式对其进行修饰,则将执行越界写入

如果结果未定义,但不会执行越界写入,则特定行为为有界未定义行为。换句话说,行为是未定义的,但您不会跳转到与任何对象或分配的空间无关的随机地址并将字节放在那里。如果您得到的未定义行为不能保证它不会进行越界写入,那么该行为就是关键的未定义行为

标准接着讨论了什么会导致关键的未定义行为。默认情况下,未定义的行为是有界的未定义行为,但UB也有例外情况,这些情况是由内存错误导致的,例如访问释放的内存或使用未初始化的指针,这些错误具有严重的未定义行为。但是,请记住,这些分类只存在于选择专门区分这些行为的C实现的上下文中,并且具有意义。除非您的C环境保证它是可分析的,否则所有未定义的行为都可能做任何事情


我的猜测是,这是为构建驱动程序或内核插件这样的环境设计的,在这些环境中,您希望能够分析一段代码,并说“好吧,如果您要射中某人的脚,最好是您的脚,而不是我的脚!”如果您使用这些约束编译一个C程序,运行时环境可以对极少数允许成为关键未定义行为的操作进行检测,并将这些操作捕获到操作系统中,并假设所有其他未定义的行为最多会破坏与程序本身相关的内存。

所有这些都是行为未定义的情况,即标准。传统上,在未定义的行为中,考虑一种实现(即C编译器+C标准库),可以看到两种未定义的行为:

  • 行为不会被记录,或会被记录导致崩溃,或行为不稳定的结构
  • 标准未定义但实现定义了一些有用行为的构造
有时这些可以由编译器开关控制。例如,示例1通常会导致不良行为-陷阱、崩溃或修改共享值。早期版本的GCC允许使用
-fwritable strings
的可修改字符串文本;因此,如果给出了该开关,则实现定义了该情况下的行为

C11增加了一个可选的正交分类:和。有界未定义行为是指不执行指定操作的行为,即它不能导致将值写入内存中的任意位置。任何未定义的行为

如果定义了
\uuuuu STDC\uuuuu可分析的
,则实施将符合,其具有关键未定义的行为:

  • 对象在其生存期()之外被引用
  • 对具有两个不兼容声明()的对象执行存储
  • 指针用于调用类型与引用类型(,)不兼容的函数
  • 左值在求值()时不指定对象
  • 程序尝试修改字符串文字()
  • 一元
    *
    运算符的操作数具有无效值()
  • 将指针加减到数组对象和整数类型中,或将指针加减到数组对象和整数类型之外,将生成一个只指向 超出数组对象,用作一元
    *
    计算为()的运算符
  • 试图通过使用带的左值来修改使用常量限定类型定义的对象 非常量限定类型()
  • 标准库中定义的函数或宏的参数
    int32_t triplet_may_be_interesting(int32_t a, int32_t b, int32_t c)
    { 
      return a*b > c;
    }
    
    #define SCALE_FACTOR 123456
    int my_array[20000];
    int32_t foo(uint16_t x, uint16_t y)
    {
      if (x < 20000)
        my_array[x]++;
      if (triplet_may_be_interesting(x, SCALE_FACTOR, y))
        return examine_triplet(x, SCALE_FACTOR, y);
      else
        return 0;
    }