C语言的单引号问题
单引号的原理
C语言的单引号实际上时将' '
内的字符转化为ASCII码对应的整型值,并且在存储时占据一个字节,即sizeof(char)
// 第一个例子
int main()
{
char ch;
ch = '?';
printf("%d, %c", ch, ch);
// 63, ?
}
输出63, ?
这是因为?
对应的ASCII码值为63
现在来看看一个单引号中有两位字符时的情况
// 第二个例子
int main()
{
char ch;
ch = '!?';
printf("%d, %c", ch, ch);
// 63, ?
}
输出竟然还是63, ?
先别急着追问为什么,我们再来看看下一种有些差异的情况
// 第三个例子
int main()
{
printf("%d, %c",'!?','!?');
// 这次的输出变成了8511, ?
}
这其实和上述的原理' '
中的字符会被转化为ASCII的整型值 一致
我们来看看8511
是如何得到的,从ASCII表上,我们得到!
对应33
,?
对应63
按一个字符一个字节转化为二进制就分别是0010 0001
,0011 1111
,将它们拼接得到0010 0001 0011 1111
,转换为十进制我们得到8511
这就是8511
的由来,那为什么在第二个例子中的输出不一样呢?
原来是8511
赋值给char
类型时发生了溢出,得到的结果就是0011 1111
这一个字节,也就是63
,从结果上看char
得到的就是单引号中的最后一个字符
知道原理后可以用更简便的方式计算,一个数向左移一个字节<< 8
,相当于乘以2 ^ 8 = 256
'!?' = 8511
33 * 256 + 63 = 8511
'xyz' = 7895418
120 * 256 * 256 + 121 * 256 + 122 = 7895418
'456789' = 909588537
这次整型值也溢出了,'456789' = '6789' = 909588537
int和char
int和char在很多方面都是相同的
int main()
{
printf("%d, %c\n", 65, 65);
printf("%d, %c\n", 100, 100);
printf("%d, %c\n", 1000, 1000);
printf("%d, %c\n", 10000, 10000);
/*
输出
65, A
100, d
1000, ?
10000,
*/
}
比如:
-
都使用二进制补码法表示正负数;
-
char可以用
%d
输出ASCII码,小的整数也可以用%c
输出字符,大的字符使用%c
相当于经过了一次溢出处理,同样也会输出某个字符。它们最主要的区别在于
int
占4个字节,而char
占1个字节,理论上'xxxx'
(x表示一个字符)可以表示一个int
更复杂的情况
基于以上理论,我们可以有一个有趣的玩法
int main()
{
int a[] = {'1234', 'abcd', 'qwer', 'hjkl', 0};
printf("%s", a);
}
在我的电脑上,这输出4321dcbarewqlkjh
%s
实际上也是通过字符指针,来一个个输出字符的,在这个过程中,实际上可以理解为发生了
a = (char *) a
'1234', 'abcd'
等经过单引号转变为连续排列的一个个字节,这和字符串的储存方式很像,同时我还加了一个0
来结束%s
的输出,这就更像了。
可以看到,输出和我们初始化的数字是倒置的,这说明整型的存储是反向的。也就是说在整型存储时,低位字节存在前面的地址,高位字节存在后面的字节。
int main()
{
int a[] = {'1234', 0};
printf("%s\n", a);
// 4321
a[0] <<= 1;
printf("%s\n", a);
// hfdb
}
再找出它们对应的二进制
'1234'
对应0011 0001
0011 0010
0011 0011
0011 0100
'4321'
对应0011 0100
0011 0011
0011 0010
0011 0001
'hfdb'
对应0110 1000
0110 0110
0110 0100
0110 0010
'hfdb'
确实是'4321'
左移一位后的结果。