C 阐明位运算符、非负运算符和类型转换是如何相互作用的 问题:
作为一名初出茅庐的C语言律师,我遇到了这样一种情况:我不确定自己是否理解C规范在逻辑上正确保证了什么 据我所知,“按位运算符”(C 阐明位运算符、非负运算符和类型转换是如何相互作用的 问题:,c,bit-manipulation,language-lawyer,C,Bit Manipulation,Language Lawyer,作为一名初出茅庐的C语言律师,我遇到了这样一种情况:我不确定自己是否理解C规范在逻辑上正确保证了什么 据我所知,“按位运算符”(&,,和&)将在非负值和任何C整数类型(char/short/int/long)上按直觉预期工作,无论有符号还是无符号)-不考虑底层对象表示 这是对C语言中严格定义的行为的正确理解吗 重点 在许多方面,这个问题归结为是否允许一致性实现将两个非陷阱、非负值作为按位运算符的操作数,并生成陷阱表示结果(来自操作本身,而不是将结果赋值/解释为不合适的类型) 例子 考虑以下代码:
&
,
,和&
)将在非负值和任何C整数类型(char
/short
/int
/long
)上按直觉预期工作,无论有符号
还是无符号
)-不考虑底层对象表示
这是对C语言中严格定义的行为的正确理解吗
重点
在许多方面,这个问题归结为是否允许一致性实现将两个非陷阱、非负值作为按位运算符的操作数,并生成陷阱表示结果(来自操作本身,而不是将结果赋值/解释为不合适的类型)
例子
考虑以下代码:
#include <limits.h>
#define MOST_SIGNIFICANT_BIT = (unsigned char )((UCHAR_MAX >> 1) + 1)
/* ... in some function: */
unsigned char byte;
/* Using broad meaning of "byte", not necessarily "octet" */
int val;
/* val is assigned an arbitrary _non-negative_ value at runtime */
byte = val | MOST_SIGNIFICANT_BIT;
#包括
#定义最高有效位=(无符号字符)((UCHAR\u MAX>>1)+1)
/* ... 在某些功能中:*/
无符号字符字节;
/*使用广义的“字节”,不一定是“八位字节”*/
int-val;
/*val在运行时被分配一个任意的非负值*/
字节=val |最高有效位|;
注意上面的注释,val
在运行时收到一个非负的值(要学究化:所述值可以用val
的类型表示)
我的期望是byte
具有最高有效位集,较低的位是val
数值的底部字符位-1
位的纯二进制表示(无填充位或陷阱表示)
即使val
的类型更改为任何其他整数类型,我也希望这种情况保持不变,但我希望只要val
的值变为负值(所有实现都不保证一个结果),或者val
的类型更改为任何非整数类型,这种保证就会消失(违反了C对位运算符定义的约束)
自动应答
我将我对当前理解的解释作为一个答案,因为我对此相当有信心,但我希望纠正我的任何误解,并将接受任何更好/更正确的答案,而不是我的答案。为什么(我认为)这(可能)是正确的
位运算符&
、|
和^
被定义为对转换后的操作数的实际二进制表示进行操作:操作数被称为进行“常规算术转换”
据我所知,逻辑上,当使用两个具有非负值的整数类型表达式作为其中一个运算符的操作数时,无论填充位或陷阱表示如何,它们的值位都将“对齐”:因此,结果的值位将具有与假设为“纯二进制表示”时所期望的值相匹配的数值
更重要的是,只要您以有效(非陷阱)非负值作为操作数开始,操作数就应该始终升级为整数类型,该类型可以表示它们的两个值,因此可以从逻辑上表示这三个操作中的任何一个的结果值。您也永远不会遇到可能的问题,例如“带符号零”,因为将自己限制为非负值可以避免此类问题。而且只要结果用作可以保存结果值的类型(或无符号整数类型),就不会引入其他类似/相关的问题
这些操作能否从非负非陷阱操作数生成陷阱表示?
最近的C99/C11草案的脚注44/53和45/54似乎表明,对这种不确定性的回答取决于foo | bar
、foo&bar
和foo | bar
是否被视为“算术运算”。如果是,则不允许在给定非陷阱值的情况下生成陷阱表示结果
C99和C11标准草案的索引将按位运算符列为“算术运算符”的子集,这表明是的。尽管C89没有按这种方式组织索引,而且我的C编程语言(第二版)有一个称为“算术运算符”的部分它只包括+
、-
、*
、/
和%
,将按位运算符保留在单独的部分。换句话说,在这一点上没有明确的答案
实际上,我不知道有哪种系统会发生这种情况(考虑到两个操作数的非负值约束),因为这是值得的
可以考虑以下内容:类型<代码>无符号char < /> >(基本上由C99和C11祝福)为了能够访问一个类型的底层对象表示的所有位—其目的似乎是位运算符可以正确地处理
无符号字符
—在大多数现代系统中,该字符将被整数提升为int
,而在其他系统中则是无符号int
:因此它看起来非常不协调至少如果foo
和bar
都是可以保存在无符号字符中的值,并且如果结果被分配到无符号字符中,那么foo | bar
或foo ^ bar
将被允许生成陷阱表示
从前面的两点可以很容易地概括出这不是一个问题,尽管我不认为这是一个严格的证明
应用于示例
这就是为什么我认为这是正确的,并且会像预期的那样起作用:
UCHAR\u MAX>>1
受试者UCHAR\u MAX
到“常用算术转换”