MaximPro Asked:2020-03-02 12:23:05 +0000 UTC2020-03-02 12:23:05 +0000 UTC 2020-03-02 12:23:05 +0000 UTC signed int 与 unsigned int(未定义行为情况) 772 简而言之,我感兴趣的是:每种类型的未定义行为的数量和示例。 c++ 2 个回答 Voted Best Answer AnT stands with Russia 2020-03-02T14:57:11Z2020-03-02T14:57:11Z 对类型执行算术运算时溢出signed int会导致未定义的行为。 对象表示signed int都可以unsigned int包含填充位,即 不参与值形成但执行辅助功能或根本不使用的位。填充位值的组合可能不正确,即 形成所谓的陷阱表示。尝试访问陷阱表示会导致未定义的行为。(例如,整数类型的对象表示可能包含奇偶校验位。) 但是,该语言保证将整数类型对象表示的所有位设置为0(包括填充位)不能创建陷阱表示,但始终会产生正确表示的整数值0。从实用的角度来看,这意味着memset(..., 0, ...)和calloc保证为任何整数类型形成正确的空值。 如果结果值不在目标类型的范围内,将浮点或指针值转换为任何整数类型会导致未定义的行为。 基于前向或后向代码的实现signed int可以自由禁止使用负零,即 将负零的表示视为陷阱表示。在这种情况下,访问负零的表示会导致未定义的行为。 基于二进制补码signed int的实现有权禁止1在符号位中使用表示 c 和0在所有有效位中使用 c,即 将此表示视为陷阱表示。在这种情况下,访问此类视图会导致未定义的行为。(换句话说,在 16 位signed int中,该值-32768可以是“禁止的”。) wololo 2020-12-29T20:32:49Z2020-12-29T20:32:49Z 我将在 C++ 语言中添加更多未定义行为和实现定义行为的示例,这不仅在处理整数时会发生,而且在相关情况下(例如,指针算术)也会发生。 位移位(在下文中,结果类型是经过整数提升的左操作数的类型)。( [表达式转换] 8.5.7 ) 如果右移操作数为负,则大于或等于提升后的左操作数 UB 的位长度。 如果负号值向左移动 - UB。 如果一个非负有符号值E1左移E2一位并且该值E1 * (2^E2) 不能用与结果类型对应的无符号整数类型表示,则 UB。 如果一个非负有符号值E1左移E2一位,并且该值E1 * (2^E2) 可以用结果类型对应的无符号整数类型表示,但不能用结果类型表示,则将该值E1 * (2^E2)转换为结果类型。这种转换的结果是实现定义的。 如果负号值向右移动,则结果值是实现定义的。 如果陷阱表示是由于某些按位运算(不仅仅是移位)而生成的,则该按位运算的行为是未定义的。(C 6.2.6.2/4) 指针算术。( [expr.add] 8.5.6 ) 如果一个表达式P指向元素x[i]数组x的一个n元素,则表达式P + J和J + P(where )仅当 if时J指向j一个元素,否则为 UB。x[i + j]0 <= i + j <= n 表达式仅在 if时P - J引用元素,否则 UB。x[i - j]0 <= i - j <= n 如果表达式P指向x[i]array的一个元素x,并且 expressionQ指向x[j]同一数组的一个元素,则表达式的结果P - Q等于i - jtype的有符号整数值std::ptrdiff_t。如果P和Q指向不同数组的元素 - UB。如果数值i - j不能按类型表示std::ptrdiff_t - UB。语言标准参照C语言标准定义了一个类型的取值范围std::ptrdiff_t,根据该标准,该类型必须包含该范围内的所有值[-65535, 65535]。该标准不要求std::ptrdiff_t所有类型的值size_t。([cstdint.syn] 21.4.1,C 7.20.3/2) 指针。 当某个内存区域的生命周期结束时,那么所有指向该内存区域任何部分的指针都将变为无效(invalid pointer value)。在这样的指针处取消引用或释放内存 - UB。( [basic.stc]6.6.4/4 ) 任何其他使用无效指针值的后果是实现定义的。特别是,该标准明确指出,程序在执行以下代码 ( [basic.stc] 6.6.4 35)时崩溃是正常行为: int *p1, *p2; p1 = new int; delete p1; p2 = p1; //system-generated runtime fault; 如果指向对象类型T1的指针被强制转换为指向对象类型的指针,并且不满足T2该类型的对齐要求,则生成的指针值是未指定的。T2我相信,特别是,它可能会变成一个无效的指针值,并带来所有后果。( [expr.reinterpret.cast] 8.5.1.10/7 , [expr.static.cast] 8.5.1.9/13 ) 好吧,如果我们谈论未定义的行为,那么如何不提以下几点: 要求严格的别名规则。 与同一表达式中未排序的值计算和副作用关联的未定义行为。 自然地,给出的示例远非未定义、实现定义和未指定行为的完整列表:)
对类型执行算术运算时溢出
signed int会导致未定义的行为。对象表示
signed int都可以unsigned int包含填充位,即 不参与值形成但执行辅助功能或根本不使用的位。填充位值的组合可能不正确,即 形成所谓的陷阱表示。尝试访问陷阱表示会导致未定义的行为。(例如,整数类型的对象表示可能包含奇偶校验位。)但是,该语言保证将整数类型对象表示的所有位设置为
0(包括填充位)不能创建陷阱表示,但始终会产生正确表示的整数值0。从实用的角度来看,这意味着memset(..., 0, ...)和calloc保证为任何整数类型形成正确的空值。如果结果值不在目标类型的范围内,将浮点或指针值转换为任何整数类型会导致未定义的行为。
基于前向或后向代码的实现
signed int可以自由禁止使用负零,即 将负零的表示视为陷阱表示。在这种情况下,访问负零的表示会导致未定义的行为。基于二进制补码
signed int的实现有权禁止1在符号位中使用表示 c 和0在所有有效位中使用 c,即 将此表示视为陷阱表示。在这种情况下,访问此类视图会导致未定义的行为。(换句话说,在 16 位signed int中,该值-32768可以是“禁止的”。)我将在 C++ 语言中添加更多未定义行为和实现定义行为的示例,这不仅在处理整数时会发生,而且在相关情况下(例如,指针算术)也会发生。
位移位(在下文中,结果类型是经过整数提升的左操作数的类型)。( [表达式转换] 8.5.7 )
如果右移操作数为负,则大于或等于提升后的左操作数 UB 的位长度。
如果负号值向左移动 - UB。
如果一个非负有符号值
E1左移E2一位并且该值E1 * (2^E2)不能用与结果类型对应的无符号整数类型表示,则 UB。如果一个非负有符号值
E1左移E2一位,并且该值E1 * (2^E2)可以用结果类型对应的无符号整数类型表示,但不能用结果类型表示,则将该值E1 * (2^E2)转换为结果类型。这种转换的结果是实现定义的。如果负号值向右移动,则结果值是实现定义的。
如果陷阱表示是由于某些按位运算(不仅仅是移位)而生成的,则该按位运算的行为是未定义的。(C 6.2.6.2/4)
指针算术。( [expr.add] 8.5.6 )
如果一个表达式
P指向元素x[i]数组x的一个n元素,则表达式P + J和J + P(where )仅当 if时J指向j一个元素,否则为 UB。x[i + j]0 <= i + j <= n表达式仅在 if时
P - J引用元素,否则 UB。x[i - j]0 <= i - j <= n如果表达式
P指向x[i]array的一个元素x,并且 expressionQ指向x[j]同一数组的一个元素,则表达式的结果P - Q等于i - jtype的有符号整数值std::ptrdiff_t。如果P和Q指向不同数组的元素 - UB。如果数值i - j不能按类型表示std::ptrdiff_t- UB。语言标准参照C语言标准定义了一个类型的取值范围std::ptrdiff_t,根据该标准,该类型必须包含该范围内的所有值[-65535, 65535]。该标准不要求std::ptrdiff_t所有类型的值size_t。([cstdint.syn] 21.4.1,C 7.20.3/2)指针。
当某个内存区域的生命周期结束时,那么所有指向该内存区域任何部分的指针都将变为无效(invalid pointer value)。在这样的指针处取消引用或释放内存 - UB。( [basic.stc]6.6.4/4 )
任何其他使用无效指针值的后果是实现定义的。特别是,该标准明确指出,程序在执行以下代码 ( [basic.stc] 6.6.4 35)时崩溃是正常行为:
如果指向对象类型
T1的指针被强制转换为指向对象类型的指针,并且不满足T2该类型的对齐要求,则生成的指针值是未指定的。T2我相信,特别是,它可能会变成一个无效的指针值,并带来所有后果。( [expr.reinterpret.cast] 8.5.1.10/7 , [expr.static.cast] 8.5.1.9/13 )好吧,如果我们谈论未定义的行为,那么如何不提以下几点:
要求严格的别名规则。
与同一表达式中未排序的值计算和副作用关联的未定义行为。
自然地,给出的示例远非未定义、实现定义和未指定行为的完整列表:)