一、字面

字面量是指C/C++源代码中直接使用的常量,字面量的意思是“一眼看上去是什么就是什么”。例如语句 int x = 10; 中10就是字面量,它相对于变量,只有一个确定的值不能改变,这个值就是它看上去的值。
C/C++中的字面量有一下几种:
1.0和正整数,如10、123等(负整数不是字面量,因为加了-运算符)
2.浮点数,如1.23
3.单个字符,如’a’、‘c’
4.多个字符,如’abc’、‘abcd’(至少一个字符、至多四个字符)
5.字符串,如”abcdefg”(可以是空串””)
将字面量解释成何种类型的值使用编译器决定的,通常也决定了这个值如何转化为二进制串存储到计算机中。

二、整数

C/C++中,整数类型有以下几种:
1.char
2.short
3.int
4.long
5.long long
类型决定了计算机将用用多少二进制位表示这个整数值也就是这个值的数据宽度。在64位的机器中,上述整数类型所用的存储宽度通常是1字节、2字节、4字节、4字节、8字节。
整数可以分为0、正数和负数,我们写出一个整数字面量时如10,这个整数字面量被认为是何种整数类型,也就是用多少二进制位表示是由编译器决定的。

0和正整数:

字面量0,被编译器认为是 int 类型,也就是说0被表示成4个字节的二进制串:0000 0000 0000 0000 0000 0000 0000 0000,注意这里并没有说它存储在计算机中就是4个字节,因为并不是将它赋值给int类型的变量。可以理解为,在存储这个字面量之前,编译器先将它翻译成了4个字节的0,至于真正如何存储则要看用何种类型的变量接收这个字面量(字面量总是要赋值给一个变量的,即总要存储在计算机中,单纯写一个字面量并不会被存储)

虽然编译器将字面量表示成的二进制串并不一定和真正存储时的一致,但这一个中间步骤是很重要的,在这个中间二进制形式到最终存储的二进制形式进行转化时要同时参考这两个类型,最终决定按何种规则转换。
暂时将这两个称为识别类型和存储类型。
编译器很据不同的数值范围将正整数字面量识别类型自动改变:

上述识别类型的数据宽度分别为4字节、4字节、8字节、8字节
windows系统下unsigned int 和unsigned long宽度一致,都是4个字节,只不过写十进制形式和十六进制(二进制)形式时编译器识别为这两种类型。
上面的识别类型中,可以对正整数字面量添加U、L、LL、UL、ULL后缀将其他类型转化为另一种类型的字面量。但是只能将小宽度的转化为大宽度类型,对大宽度字面量使用小宽度后缀是无效的,同样没有将unsigned类型转化为signed类型(非usingned默认就是signed)。小宽度字面量使用大后缀的结果是扩展可数据宽度。如255ULL、0xFFULL将int 字母量识别为unsigned long long。值得注意的是,signed和unsigned对字面量转化为的二进制串是没有任何影响的,只有数据宽度的变化才影响二进制串。所以对字面量加U后缀或者不加,没有任何影响。但是对编译器自动根据字面量值的范围识别出来的识别类型,unsigned的字面量值在使用“-”取相反数时可能会出现逻辑上的错误,但本质上二进制串与singned无差别。
正整数转化为二进制的方法是“除2取余,逆向排列”,一个正整数的字面量首先由编译器自动确定识别类型即确定二进制位数,然后按照此方法转化为二进制串,当转换后二进制位数不足识别类型的位数则在高位补0。
如12在0到2147483647之间,识别类型为int(4字节32位)则相应的二进制串为:
0000 0000 0000 0000 0000 0000 0000 1100表示为十六进制为:0000000C
如果是十六进制形式的正整数字面量,如0xFF则识别为int类型,相应的二进制串为0000 0000 0000 0000 0000 0000 1111 1111即十六进制000000FF

负数

并不存在真正的负数字面量,整数字面量只有0和正整数,在正整数前加一个“-”单目运算符这并不意味着得到的是这个正整数字面量的相反数。C/C++中这一特点好像不合逻辑,但像之前所说,一个正整数字面量最终只是转化为一个二进制串但并不明确它是何值。我们完全可以按照自己的方式将内存中的一个二进制串解释为整数、浮点数等等,显然值是不同的。
那么,对这样一个正整数字面量使用“-”运算符也不一定是此正整数的相反数。就像对一个二进制串使用“-”运算符,并不存在相反数的概念。实际上,对一个正整数字面量使用“-”运算符只是将该字面量识别类型的二进制串逐位取反再加1(溢出则舍弃高位,称为二进制反码)。所以,一个二进制串做此操作后得到的新二进制串在被指定解释为整数时,这个反码表示的不一定就是原整数字面量的相反数。例如,字面量255被认为是int类型,对应二进制串0000 0000 0000 0000 0000 0000 1111 1111。而-255对应二进制串1111 1111 1111 1111 1111 1111 0000 0001,如果仍解释为int类型则其对应十进制的-255,此时正是字面量的相反数。但是再如,字面量2147483648被识别为unsigned long,对应二进制串0x80000000。而-2147483648对应的二进制串为0x80000000和2147483648一样(取反加1后一样),所以无论将二者解释成何种类型,因为对应的二进制串一致所以应该总是同一个值,而不是相反数。
由于“-”运算符这种对二进制串的操作规则,使得对整数字面量使用后得到的并不一定是原字面量值的相反数。在整数字面量的识别类型中,带有unsiged标志的类型可能会出现这种情况(但不一定),所以有些编译器如vs自带编译器会将这种对unsigned类型字面量使用“-”的操作视为错误,但非unsigned类型的字面量取“-”之后一定是原值的相反数。如果要关闭这一检查,可以在vs中项目属性中关闭SDL安全检查。

实际上并不存在真正的负数字面量,“负数”表示成的二进制串只是原正整数字面量二进制串取补码的操作结果。最安全的使用取相反数的解决方案是只对signed类型使用,最有效的检查方法还是从二进制串的变化来分析取相反数后是否和逻辑上的一致。
只需要记住一条规则:“-”运算符只是将原二进制串取反加1,并非取相反数(不一定)。

三、总结

上述所说只是整数字面量如何表示成二进制串、整数字面量加”-“运算符后表示的二进制串如何改变,并不一定是是该字面量最终的存储形式也不一定整数字面量的值就是其整数的值,这些要结合它被赋值给何种类型的变量来说明。一串二进制被解释成何值、一个整数字面量存储成何种二进制串是由存储它的变量的类型决定的。