首先我们要知道数据类型的结构大概有几种?大概是什么样的。
char ——>字符数据类型
short ——>短整型
int ——>整形
long ——>长整型
long long ——>更长的整形
float ——>单精度浮点数double ——>双精度浮点数
其中char类型占用1字节。short类型占用2字节。int占用4字节。long int 4字节。long long 8字节,float 4字节。double 8字节。long double 8字节。
char 这里我们可以看见各种数据类型都被分为
unsigned char 有符号数和无符号数
signed char
short——>signed short
unsigned short [int]
signed short [int]
int
unsigned int
signed int——>int
long
unsigned long [int]
signed long [int]
在这里我们需要注意char类型,在C语言中其实并没有规定char就等价于signed char.
大多数取决于编译器,绝大多数的编译器都把char等价于signed char.
我们先来看看这一段代码
#includeint main() { int n = 10; printf("%dnn", n); n = -10; printf("%dnn", n); unsigned int m = 10; printf("%unn", m); m = -10; printf("%unn", n); return 0; }
分析代码之前我们首先需要知道,无符号整型的打印方式是%u.
相信很多同学不知道这段代码的意义。我们先来看一下结果:
int n部分大部分同学都能看懂,问题就是unsigned int m的-10打印出来为什么是4294967286?
答案就是我们用无符号整形就是,编译器将负数的二进制的补码直接当成正数的来打印。
至于如何转换的我们在后面会再讲
原码 反码 补码
计算机中的整数有三种表示方法,即原码、反码和补码。
且内存中存储的是二进制的补码
三种表示方法均有符号位和数值位两部分,符号位都是用0表示“正”,用1表示“负”,而数值位
负整数的三种表示方法各不相同整数首先分为正数和负数
首先正数的原码,反码,补码相同
其次是负数的原码,反码,补码的转换:
例如:-15
1000 0000 0000 0000 0000 0000 0000 1111
首先我们要知道第一个数是符号位
正数为0,负数为1
原码:按照一个数的正负直接写出来的二进制就是原码
1000 0000 0000 0000 0000 0000 0000 1111
反码:符号位不变,其他位,按位取反
1111 1111 1111 1111 1111 1111 1111 0000
补码:反码的二进制+1就是补码.
1111 1111 1111 1111 1111 1111 1111 0001
内存中都是用16进制的方法存储的所以:
f f f f f f f 1
内存中就是存储的就是:ff ff ff f1
在这里我们看见内存中a存放的是:f1 ff ff ff
这里为什么是反着放的在后面我们就会讲。
这里我们讨论一下我们为什么要用补码的这一套东西存储在计算机中。
有个前提就是计算机只有加分
计算机的cpu所有的工作都是二进制的加法,就算是减法,乘法,除法等等都是转化为加法,都是基于二进制的换算算法的。
比如1-1 其实计算机看成了1+(-1)
我们看看-1在计算机中
1000 0000 0000 0000 0000 0000 0000 0001——原码
1111 1111 1111 1111 1111 1111 1111 1110——反码
1111 1111 1111 1111 1111 1111 1111 1111——补码
在这里我们有1的补码与-1的补码相加(正数的原码,反码,补码相同)
0000 0000 0000 0000 0000 0000 0000 0001
+
1111 1111 1111 1111 1111 1111 1111 1111
= 1 0000 0000 0000 0000 0000 0000 0000 0000
在这里发生了int类型的是32bit,这里发生了截断,就变成了
0000 0000 0000 0000 0000 0000 0000 0000(就问你妙不妙)
这里我们放心大胆的用符号位进行运算。
在这里我们回到我们之前那个问题,为什么内存中是反正存放数据的
我们将存储方式分为两种:大小端字节序
大端字节序:当一个数据的低字节的数据存放在高地址处,高字节序的内容放在了低地址处,这种存储方式就是大端字节序存储。
大端字节序:当一个数据的低字节的数据存放在低地址处,高字节序的内容放在了高地址处,这种存储方式就是小端字节序存储。
为什么会有大小端模式之分呢?这是因为在计算机系统中,我们是以字节为单位的,每个地址单元 都对应着一个字节,一个字节为8bit。但是在C语言中除了8 bit的char之外,还有16 bit的short型,32 bit的long型(要看具体的编译器),另外,对于位数大于8位的处理器,例如16位或者32位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如何将多个字节安排的问题。因此就
导致了大端存储模式和小端存储模式。
例如:一个 16bit 的 short 型 x ,在内存中的地址为 0x0010 , x 的值为 0x1122 ,那么 0x11 为
高字节, 0x22 为低字节。对于大端模式,就将 0x11 放在低地址中,即 0x0010 中, 0x22 放在高地址中,即 0x0011 中。小端模式,刚好相反。我们常用的 X86 结构是小端模式,而 KEIL C51 则为大端模式。很多的ARM,DSP都为小端模式。有些ARM处理器还可以由硬件来选择是大端模式还是小端模式百度2015年系统工程师笔试题:
请简述大端字节序和小端字节序的概念,设计一个小程序来判断当前机器的字节序。
int check_sys() { int n = 1; char* p = (char*)&n; if (*p) { return 1; } else { return 0; } } int check_sys() { int n = 1; return *(char*)&n; }
这里的第二种其实是第一种的优化。
在我们了解了这些之后我们来做一些题目,更方便我们了解计算机的一些存储方式。
//输出什么? #includeint main() { char a = -1; signed char b = -1; unsigned char c = -1; printf("a=%d,b=%d,c=%d", a, b, c); return 0; }
在这里我们知道一个整数是4个字节,但是我们的char类型是1个字节,所以这里必然会发生截断。
我们知道-1的补码是全1
-1补码:1111 1111 1111 1111 1111 1111 1111 1111
截断最后一个字节:1111 1111
所以我们的a,b,c,内放的都是:1111 1111
在这里我们看似都一样但是到使用的时候就发生变化。
在打印时,我们用%d的形式来打印整数(4个字节),所以我们肯定要进行整形提升
a:1111 1111
这个时候我们将a的第一个数字当作a的符号位,然后进行整型提升用符号位从左往右补,补到4个字节为止。
a:1111 1111 1111 1111 1111 1111 1111
这个时候我们a是补码,这个时候就要-1取反,把原码算出来。(b同)
这个时候我们还要再看一下c,我们要注意c的类型是unsigned char,unsigned char的整形提升和char不同,我们要知道unsigned char整形提升的时候只补0.
c:0000 0000 0000 0000 0000 0000 1111 1111
同时c的原码,反码,补码相同,所以c打印出来就是255.
#includeint main() { char a = -128; printf("%un", a); return 0; }
我们先把-128的原码反码补码写出来:
1000 0000 0000 0000 0000 0000 1000 0000
1111 1111 1111 1111 1111 1111 0111 1111
1111 1111 1111 1111 1111 1111 1000 0000
这个时候发生截断
1000 0000
打印时用%u的形式打印这个时候我们要进行整型提升:
用符号位进行整形提升:
1111 1111 1111 1111 1111 1111 1000 0000
这个值我们再用%u打印的时候就把这个数当成一个正数的原码来打印。
这个值就是4294967168
#includeint main() { char a = 128; printf("%un", a); return 0; }
这个时候我们再来看看这个代码
思路一样a:0000 0000 0000 0000 0000 0000 1000 0000
截断:1000 0000
整形提升:1111 1111 1111 1111 1111 1111 1000 0000
打印当成无符号整形:4294967168
#includeint main() { int i = -20; unsigned int j = 10; printf("%dn", i + j); return 0; } //按照补码的形式进行运算,最后格式化成为有符号整数
i->原码:1000 0000 0000 0000 0000 0000 0001 0100
i->反码:1111 1111 1111 1111 1111 1111 1110 1011
i->补码:1111 1111 1111 1111 1111 1111 1110 1100
j->0000 0000 0000 0000 0000 0000 0000 1010
i+j:
1111 1111 1111 1111 1111 1111 1111 0110
原码:
1000 0000 0000 0000 0000 0000 0000 01010
答案就是-10.
在这里我们来总结一下:
unsigned char ——>整型提升的时候补0
%u——>将数当成原码输出
#includeint main() { unsigned int i; for (i = 9; i >= 0; i--) { printf("%un", i); } return 0; }
其实这道题只要关注了unsigned int就很容易知道i永远是个正数,那么就永远是个死循环。
#includeint main() { char a[1000]; int i; for (i = 0; i < 1000; i++) { a[i] = -1 - i; } printf("%d", strlen(a)); return 0; }
在这里我们有一个点必须清楚,我们这里是把int类型放在char类型中,所以必然会发生截断
我们先讨论一下,一个char类型的变量中到底能放什么数值。
char unsigned char
0000 0000 ——>0 0000 0000 ——>0
0000 0001 ——>1 0000 0001 ——>1
0000 0010 ——>2 0000 0010 ——>2
...... ........
0111 1111 ——>127 0111 1111 ——>127
1000 0000 ——>-128 1000 0000 ——>128
...... .......
1111 1111 ——>0 1111 1111 ——>255
unsigned char类型没有符号位,那么第一位就是有效位。
同时这里都是补码
所以char类型的数值的范围是0~127~(-128)~0 (相当于一个闭环)
unsigned char类型的数值范围是0~255
我们再回来这道题上面,char从-1开始储存进数组中从-1到-128
再到127,再到0再到-1。
我们现在知道了类型的数组数组的排列方式,我们在了解一下strlen的知识点,
strlen是从第一个元素到0之间字节数,所以strlen 数到第一个0时就停下了所以就是-1到-128到127再到0
就是255了。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)