大小端
什么是大小端?
表示数据在存储器中的存放顺序。
大端:按照字节为单位,低权值位数据存储在高地址处,就叫做大端。
小端:按照字节为单位,低权值位数据存储在低地址处,就叫做小端。
那么再来看看大小端概念
口诀
以小端为例
小端低地,否则为大端。
大小端如何影响数据存储的
本质是数据和内存空间的一种映射关系。
例子:
unsigned int a = -10;
这个a变量如何存呢?
-10=1111 1111 1111 1111 1111 1111 1111 0110(补码)=0xFFFFFFF6(十六进制补码)
如何取呢?
先看大小端
1111 1111 1111 1111 1111 1111 1111
再看自身类型
自身类型是unsigned, 不用看符号位,直接变十进制。
为什么有大小端?
- 数据按字节为单位的情况下,有高权值和低权值之分。
- 内存的地址有高地址和低地址之别的。
整型取值范围
以signed char为例:
char类型是一个字节的大小空间。
那么它的取值范围不就是:11111111 ~ 01111111(-127~127)吗?
这是有问题的。
什么是数据取值范围?
假如两个比特位:
00 ~ 11 这中间还有有01 10
假如是三个比特位:
000 ~ 111 这中间还有001 010 011 100 101 110
那么数据类型能表示多少个数据,是取决于比特位排列组合的个数。
两个比特位就是2^2 个,三个比特位就有2^3 个,三十二个比特位就有2^32个。
回到上面的问题,其实char类型的范围是-128 ~127。那么这个-128怎么来的呢?
首先char类型是占一个字节的内存大小空间,也就是八个比特位,那么八个比特位就有2 ^ 8的排列组合,它的取值范围也就是1111 1111 ~ 0111 1111(-127 ~127),在这个取值范围内,有一个二进制是1000 0000,它的十进制不在-127 ~ 127之间,那么我们规定它是-128或者128,这里符号位是1,那么只能取-128,这就是-128的由来,那么我们怎么去理解-128呢?举一个例子。
char c = -128; printf("%d\n",c);
打印出的结果是-128,可以打印出正确结果。我们来分析分析,存的过程是先开辟内存空间,然后把-128转化为补码:1 1000 0000存进去;然后取的时候,char类型只能取八个比特位,这时就发生了截断,取的不是1 1000 0000,而是1000 0000,然后先看最高符号位,最高符号位是1,确认是补码,然后转换成原码:0000 0000这时我们发现,它取的时候不是-128而是0;那么就规定1000 0000是-128,直接用。所以char类型的取值范围就是-128 ~ 127。short的取值范围就是-2^15 ~ 2^15 -1。int的取值范围就是:-2^31 ~ 2^31 -1。
#include <stdio.h> #include <string.h> int main() { char a[1000]; for(int i=0;i<1000;i++) { a[i] = -1-i; } printf("%d\n",strlen(a)); return 0; }
最终输出结果是255。
我们来分析一下,首先strlen函数计算的是这个数组在’\0’结束标志之前的元素个数。那么也就是计算这个数组元素为零之前的元素个数。
a[0] = -1 = 1000 0001(原码) = 1111 1111(补码) a[i] = -1 + (-1) = 1 1111 1110 (补码) = 1111 1110(截断后取的补码) = 1111 1101(反码) = 1000 0010(原码) = -2
那么i=0,a[0] = -1;i=1,a[1] = -2;i=2,a[2] = -3;…那么i=127,a[127] = -128;
那么-1 + (-127) = ?
a[127] = -1(1111 1111)(补码) + -127(1000 0001)(补码) = 1 1000 0000 = 1000 0000(截断后取的补码) = -128
那么a[128] = ?
a[128] = -1+(-128) = 1111 1111(-1的补码) + 1000 0000(-128的补码) = 1 0111 1111 = 0111 1111(截断后取的补码) = 127 //-1+(-128)本身越界了,超过了范围,出现错误得正数.
那么a[129] = 126,a[130] = 125,依次递减,那么我们的a[255] = 0.0~255有256个元素(包含\0),所有只有255个元素,打印出255.
例题
#include <stdio.h> int main() { int i = -20; //-20=1000 0000 0000 0000 0000 0000 0001 0100(原码) //1111 1111 1111 1111 1111 1111 1110 1011(反码) //1111 1111 1111 1111 1111 1111 1110 1100(补码) unsigned int j = 10; //0000 0000 0000 0000 0000 0000 0000 1010(原码=补码) //i+j //1111 1111 1111 1111 1111 1111 1110 1100(-20) //0000 0000 0000 0000 0000 0000 0000 1010(10) //1111 1111 1111 1111 1111 1111 1111 0110(-20 + 10) //%d格式打印是unsigned int //看最高符号位 //1111 1111 1111 1111 1111 1111 1111 0110(-20 + 10)(补码) //1000 0000 0000 0000 0000 0000 0000 1010(原码) //1000 0000 0000 0000 0000 0000 0000 1010 = -10 printf("%d\n", i + j); return 0; } //如果被%u格式解释 //则输入结果是4294967286 //这个值对应的二进制序列仍然是1000 0000 0000 0000 0000 0000 0000 1010 //这就是类型的价值,类型决定我们如何解释内存当中二进制的含义。
#include <stdio.h> int main() { //死循环 unsigned int i; //unsigned int i ——> 是无符号整数,读取出来的时候就会直接读取,不会转化再转为原码 for(i = 0; i >= 0; i--) { printf("%u\n", i); //Sleep(1000);//休眠一秒以便观察 } return 0; } //死循环从4294967295到0再到4294967295到0一直循环下去(-1对应的%u格式打印出的结果是4294967295)
规则
无符号型常量都应该带有字母U后缀
int a = 10; unsigned int b = 10u;
调试起来看一下。
if-else语句
什么是语句?
c语言中由一个分号;隔开的就是一条语句。
表达式是什么?
c语言中,用各种操作符把变量连起来,形成有意义的式子,就是一个表达式。
if-else基本语法
#include <stdio.h> int main() { int flag = 1; if(1 == flag) { printf("hello girl\n"); } else { printf("hello boy\n"); } return 0; }
结论:
- 注释:ctrl+k+c(注释),ctrl+k+u(取消注释)。
还有一种方法(不推荐)
#include <stdio.h> int main() { if(0) { int i = 1; if(1 == i) { printf("hello\n"); } else { printf("*****\n"); } } return 0; }注释可以用if(0)来注释,不推荐,但是要能看懂就行。
- c语言当中0为假,非0为真。
- if-else语句怎么执行的?
先执行()中的表达式或者函数,得到真假结果——>条件判定——>进行分支功能。
说了怎么执行,那么我们再来看怎么用,深入理解一下。
int fun() { printf("如果没有数据\n"); return 1; } #include <stdio.h> int main() { if(fun()) //先执行()中的函数再条件判断是非零即为真,则进入分支。 { printf("Yes\n"); } return 0; }
前面用if-else语句做了铺垫,然后我们一起来看bool类型。
bool类型
c99引入了bool类型。但是c语言大部分都是以c90为标准。所以只需要知道就可以,另外还有微软的BOOL类型,它的空间大小是4个字节,但是不推荐使用,可移植性差(只能适用于微软的编译器)。
#include <stdio.h> #include <stdbool.h> int main() { bool x = true; if(x) { printf("hello world!"); } return 0; }
#include <stdio.h> #inlcude <stdbool.h> int main() { bool x = true; //#define bool _BOOL(转到定义) printf("%d\n", sizeof(x));//输出结果是1,只占一个字节的空间大小。 return 0; }
bool类型占一个字节的空间。
c语言中怎么进行的bool和0比较呢?
#include <stdio.h> #include <stdbool.h> int main() { int flag = 0; if (flag == 0) //不推荐 { printf("1\n"); } if (flag == false) //不推荐 { printf("2\n"); } if (flag) //推荐,flag就相当于bool { printf("3\n"); } return 0; } //注意false要引入头文件#include <stdbool.h>
float变量和“零值”进行比较
前言
浮点数在内存中存储,并不是完整存储的,在十进制转化为二进制,有可能有精度损失(数值可能变大,也有可能变小)。
#include <stdio.h> int main() { double x = 3.4; printf("%.50lf\n", x); return 0; }
产生了精度的损失。看图
正言
#include <stdio.h> int main() { double x = 1.0; double y = 0.1; printf("%.50lf\n", (x - 0.9)); printf("%.50lf\n", y); if((x-0.9) == 0.1) { printf("yes\n"); } else { printf("no\n"); } return 0; }
运行后结果:
浮点数和"零值"进行比较
精度损失导致,那么显然 == 判断操作符绝对不能进行浮点数之间的比较。那么我们规定进行比较是对数和EPSILON进行比较,也就是数是否在一个合法的精度范围内。
#inlcude <float.h> int main() { //使用DBL_EPSILON要引入头文件#include <float.h> DBL_EPSILON; //double最小精度 //#define DBL_EPSILON 2.2204460492503131e-016 // smallest such that 1.0+DBL_EPSILON != 1.0 FLT_EPSILON; //float最小精度 //#define FLT_EPSILON 1.192092896e-07F // smallest such that 1.0+FLT_EPSILON != 1.0 return 0; }
再来看怎么比较
- 第一种比较(自定义比较)
#include <stdio.h> #include <math.h> #define EPS 0.000000000000000000001 int main() { double x = 1.0; double y = 0.1; printf("%.50lf\n", (x - 0.9)); printf("%.50lf\n", y); if (fabs((x - 0.9))-y < EPS) { printf("yes\n"); } else { printf("no\n"); } return 0; }//输出结果是yes
- 第二种方法(DBL_EPSILON比较——c语言中表示最小的精度值)
#include <stdio.h> #include <math.h> #include <float.h> int main() { double x = 1.0; double y = 0.1; printf("%.50lf\n", (x - 0.9)); printf("%.50lf\n", y); if (fabs(x - 0.9) - y < DBL_EPSILON) { printf("yes\n"); } else { printf("no\n"); } return 0; }//输出结果是yes
最后我们来看浮点数和零值比较
#include <stdio.h> #include <math.h> #include <float.h> int main() { double x = 0.0; if (fabs(x)< DBL_EPSILON) { printf("yes\n"); } else printf("no\n"); return 0; }
写法上不建议比较的时候带上等号。
总结:
- 浮点数存储的时候是有精度损失的。
- 浮点数是不能进行==比较的。
- double用DBL_EPSILON比较,float用FLT_EPSILON比较
指针和“零值”比较
#include <stdio.h> int main() { printf("%d\n", 0); printf("%d\n", NULL); printf("%d\n", '\0'); return 0; } //输出结果都是0 //数值没变,变的是类型。(强制类型转化) //强制类型转化:不改变内存中的数据,只改变对应的类型。
看看if语句中括号中的写法注意事项。
#include <stdio.h> int main() { int *p = NULL; //三种写法 //if(p == 0) if(p != 0) //if(p == NULL) if(p != NULL) //if(p) if(!p) //这些写法不建议,我建议这样写 //if(NULL == P) if(NULL != P) return 0; }
else到底和哪个if匹配呢?
int main() { int x = 0; int y = 1; if (10 == x) if (11 == y) printf("hello girl!\n"); else printf("hello boy!\n"); return 0; }
这样的排版结果是什么都没打印,为什么呢?
原因就是,else匹配if是就近原则。建议循环和if分支语句都带上花括号。