个人全栈之路-C语言基础之数据存储

个人全栈之路-C语言基础之数据存储

2.1 计算机的计算单位

2.1.1 容量单位

在购买电脑时,一般会选择高配置的内存、硬盘。例如最新款(2019)15寸的MacBookPro已经能够选配32G内存和4T的固态硬盘,而这里的32G和4T就是经常使用的容量单位。html

在物理层面,咱们使用高低电平来记录信息,一般使用高电平表示1,低电平表示0,所以在计算机底层只能认识0,1两种状态。而0,1可以表示的内容太少,迫切须要更大的容量表示方法,所以诞生了字节(Byte),千字节(KB),兆字节(MB),吉字节(GB),太字节(TB),拍字节(PB),艾字节(EB),它们之间的换算关系以下所示linux

1EB=1024PB
1PB=1024TB
1TB=1024GB
1GB=1024MB
1MB=1024KB
1KB=1024B
1Byte=8bit

位(bit)是最小的计算机容量单位,一般用于门电路。
而字节(Byte)、千字节(KB)、兆字节(MB)表示网络传输,文件大小,是最基本的容量计量单位。
吉字节(GB)一般用于表示计算机内存、磁盘的容量单位
太字节(TB),拍字节(PB)一般是用于表示云盘、移动硬盘的容量单位
艾字节(EB)一般是用于表示数据中心的容量单位web

如今一般笔记本的内存一般是8G,16G,32G,64G等等,而运行在笔记本之上的操做系统广泛都是64位的,由于32位系统只能使用4G内存,下面是4G的内存换算算法

4G=2^2 * 2^10 * 2^10 * 2^10 =4*1024*1024*1024=2^32

在购买内存或者买移动硬盘时,一般使用的存储单位就是GB或者是TB,
可是在买4T的移动硬盘时,实际的可用容量却只有3T多,由于计算机的存储单位是以2的10次方(即1024)换算,而硬盘厂商们是以1000为换算单位。编程

4T的硬盘换算成位以下所示windows

4T=4*1024GB*1024MB*1024KB*1024B*8bit

而硬盘厂商的实际容量数组

4T=1000*1000*1000*1000*8

所以实际的可用容量是缓存

4*1000*1000*1000*1000/1024/1024/1024/10243.63T

而在一些互联网巨头(例如国内的BAT,国外的亚马逊、苹果、微软、谷歌,脸书)公司中,可能使用到比TB更大的海量数据,也就是PB或者EB,它们的换算单位以下所示。服务器

1PB=1024TB
1EB=1024PB

2.1.2 速度单位

  • 网络速度
    网络经常使用的单位是Mbps
    而网络带宽提供商(例如长城宽带)声称的百兆带宽其实是100Mbit/s,可是100M光纤测试的峰值速度只会有12.5MB/s,它们之间的换算是100Mbit/s=(100/8)MB/s=12.5MB/s。微信

  • CPU速度
    CPU的速度通常是由CPU的时钟频率所体现的,而时钟频率的单位是赫兹(Hz),目前主流的CPU时钟频率通常都在2GHz以上,而赫兹(Hz)其实就是秒分之一,也就是每秒钟的周期性变更重复次数的计量。
    GHz即十亿赫兹(10^9Hz),2GHz就是二十亿赫兹,也就是说2GHz的CPU每秒能够变化20亿次。

1Khz=1000hz
1Mhz=1000khz
1Ghz=1000Mhz

2.2 计算机底层为何只能识别二进制

咱们目前主要使用的计算机都是大规模集成电路,是采用大规模和超大规模的集成电路做为逻辑元件的。集成电路按其功能、结构的不一样,能够分为模拟集成电路、数字集成电路和数/模混合集成电路三大类。而咱们的计算机主要是采用数字集成电路搭建的。逻辑门是数字逻辑电路的基本单元。常见的逻辑门包括“与”门,“或”门,“非”门,“异或”等等。经过逻辑门能够组合使用实现更为复杂的逻辑运算和数值运算。逻辑门能够经过控制高、低电平,从而实现逻辑运算。电源电压大小的波动对其没有影响,温度和工艺误差对其工做的可靠性影响也比模拟电路小得多,因此相对稳定。由于数字计算机是由逻辑门组成,而逻辑电路最基础的状态就是两个——开和关。因此,数字电路是以二进制逻辑代数为数学基础。二进制的基本运算规则简单,运算操做方便,这样一来有利于简化计算机内部结构,提升运算速度。可是在平常开发中,一般都会使用八进制和十六进制,由于八进制和十六进制相对于二进制表示数据更加简洁,并且一个八进制表示三个二进制,一个十六进制表示四个二进制。例如1024使用二进制表示为0b100 0000 0000,使用八进制表示为02000,使用十六进制表示为0x400。

2.3 进制

2.3.1 进制概述

进制的定义:进制是一种计数方式,也称为进位计数法或者位值计数法,使用有限数字符号表示无限的数值,使用的数字符号的数目称为这种进位制的基数或者底数,例如十进制就是由0-9十个数字组成。在计算机内存中,都是以二进制的补码形式来存储数据的,生活中以十进制方式计算的数据居多,例如帐户余额,开发人员的薪水等等。计算的内存地址、MAC地址等等一般都是使用十六进制表示的,Linux系统的权限系统采用八进制的数据表示的。相同进制类型数据进行运算时会遵照加法:逢R进1;减法:借1当R,其中R就表示进制。

计算机经常使用进制的组成、示例和使用场景:

进制名称 组成 数值示例 应用场景
二进制 0,1 1010 计算机底层数据存储
八进制 0-7之间的8个整数 010 linux权限系统
十进制 0-9之间的10个整数 12 整数
十六进制 0-9,a-f之间的10个整数加上6个字母 12f 数据的内存地址

2.3.2 十进制转换二进制、八进制、十六进制

十进制转换二进制、八进制、十六进制能够采用短除法,即待转换的十进制数除以指定的进制(例如2,8,16),直到商数为0,求余数。

十进制101转换为二进制的计算过程

重复除以2 商数 余数
101/2 50 1
50/2 25 0
25/2 12 1
12/2 6 0
6/2 3 0
3/2 1 1
1/2 0 1

而后将余数的结果从下到上串联起来的结果:1100101,即十进制的101转换为二进制的结果为1100101

十进制的237转换为二进制

重复除以2 商数 余数
237/2 118 1
118/2 59 0
59/2 29 1
29/2 14 1
14/2 7 0
7/2 3 1
3/2 1 1
1/2 0 1

而后将余数的结果从下到上串联起来的结果:11101101,即十进制的237转换为二进制的结果为11101101。

2.3.3 二进制、八进制、十六进制转十进制

首先明确不一样进制的值是如何计算的,这里以十进制和二进制为例子,阐述它们的计算过程。

十进制整数1024

1024=1*10^3+2*10^1+4*10^0=1000+20+4=1024

二进制整数 10000000000

10000000000 =1*2^10=1024

二进制、八进制、十六进制整数转十进制整数是使用按权展开法计算的,这里以二进制数据01100101为例子。从右往左开始数,若是二进制位为1,则依次用1*2^n,n从0开始。

二进制整数01100101 转换为十进制整数的计算过程

01100101=126+1*25+1*22+1*20=64+32+4+1=101

八进制整数0127转换为十进制整数的计算过程

0127=1*8^2+2*8^1+7=87

十六进制整数0x12f转换为十进制整数的计算过程

0x12f=1*16^2+2*16^1+f*16^0=256+32+15=303

2.3.4 二进制转八进制、十六进制

二进制转八进制是按照从右往左,每3位二进制对应1位八进制,由于2的3次方等于8

二进制整数11001100转八进制计算过程

11 001 100 =0314

二进制转十六进制是按照从右往左,每4位二进制对应1位十六进制,由于2的4次方等于16。

二进制整数1100 1110转十六进制计算过程

1100 1110 =0xce

2.3.5 八进制、十六进制转二进制

八进制转二进制是按照从右往左,每1位八进制对应3位二进制。

八进制整数0127转二进制整数计算过程

0127=001 010 111

十六进制转二进制是按照从右往左,每1位十六进制对应4位二进制。

十六进制整数0x12f转换为二进制整数计算过程

0x12f=0001 0010 1111

2.4 数值在计算机的存取方式

2.4.1 数值在计算机的存储方式

数据在内存中有三种表现方式:原码、反码和补码
可是数据在内存中都是以二进制的补码存储,在理解补码以前首先得理解原码以及反码。

原码就是一个数据自己的二进制表现形式,而数值能够分为有符号和无符号两种。
有符号数其二进制表示最高位(最左边)是符号位,1表示负数,0表示正数。

  • 例如无符号数15的原码使用一个字节表示为0000 1111
  • 例若有符号15的原码使用一个字节表示为0000 1111
  • 例若有符号-15的原码使用一个字节表示为1000 1111

反码
无符号数的反码等于原码
例如无符号数15的反码等于原码,即无符号数15的反码用一个字节表示为0000 1111

有符号数正数的反码也等于原码
例若有符号正数15的反码等于原码,即有符号数15的反码用一个字节表示为0000 1111

有符号负数的反码是原码最高位不变,其余位取反
例若有符号负数-15的反码是原码最高位不变,其余位取反用一个字节表示为1111 0000

补码
无符号数的补码等于反码
例如无符号数15的补码等于反码,即无符号数15的补码使用一个字节表示为0000 1111

有符号正数的补码等于反码
例若有符号正数15的补码等于反码,即有符号正数15的补码使用一个字节表示为0000 1111

有符号负数的补码等于反码加1
例若有符号负数-15的补码等于反码1111 0000加1,即有符号负数15的补码使用一个字节表示为11111 0001

数值 是否有符号 原码 反码 补码
15 0000 1111 0000 1111 0000 1111
15 0000 1111 0000 1111 0000 1111
-15 1000 1111 1111 0000 1111 0001

无符号数以及有符号正数,计算机存储的是原码,由于原码等于反码
有符号负数计算机存储的是补码,补码等于原码取反加1,原码等于补码取反加1

2.4.2 数值在计算机的获取方式

在使用printf函数输出不一样格式的数据时

若是是有符号,可使用 %d, %ld,%lld,%f,%lf获取,由于int,long,long long ,float和double默认就是有符号类型的。

在解析有符号时,首先看内存中的二进制数据最高位是否为1;

若是是1,表示该数字是某个负数的补码,printf输出时还须要转换为原码,由于程序操做的是原码,此时须要根据符号位不变,其余位取反,末位加1。
若是是0,表示该数字是某个正数的补码,因为正数的补码等于原码,此时按照补码输出便可。

若是是无符号,可使用%u,%lu,%llu,%o,%x获取数据。因为无符号数不须要考虑负数,所以printf()输出数据时按照内存存放的数据原样输出。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* printf格式化输出数据 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/31 */
int main(int argc, char* argv[]) {

    /* 声明占据一个字节的变量number,并赋值为-15 有符号负数,内存存储的是补码 -15的原码是1000 1111 -15的反码是1111 0000 -15的补码是1111 0001 所以内存中存放的是 1111 0001 */
    char number = -15;
    
    /* 有符号输出 1111 0001 首先看最高位,最高位是1,表示该数是一个负数,须要转换成原码输出 补码 1111 0001 转换为原码前首先转换为反码 1000 1110 而后再加1 即1000 1111 按照十进制输出结果就是-15 由于最高位是1,表示该数字是负数,所以输出结果是-15 */
    printf("-15的有符号输出结果是 %d \n",number); //%d按照十进制解析,所以输出结果是-15


    /* 无符号输出时不考虑符号位 由于 -15在内存中是使用补码存储,即1111 0001 不考虑符号位输出,结果是241,由于1111 0001 转换为二进制的结果是241 %u按照无符号整数解析,占据四个字节,可是number只占1个字节 可是printf输出时编译器会自动添加24个1, 所以使用按位与去掉编译器自动添加的24个1 number&0x000000ff */

    printf("无符号输出-15的结果是%u\n",number&0x000000ff);

    system("pause");
    return 0;
}

若是以十六进制给某个变量赋值,十六进制不区分正负,内存原样存储,不须要考虑原码、反码和补码的换算。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 十六进制赋值输出 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/31 */
int main(int argc, char* argv[]) {

    /* 当给一个变量采用十六进制的值赋值时,内存原样存储,由于十六进制不区分正负 0x9b在内存中的存储 1001 1011 */
    char number = 0x9b;
    /* 有符号获取 0x9b 首先将0x9b转换为二进制1001 1011,最高位是1,表示为number的补码 这里须要将1001 1011转换为原码 首先将1001 1011转换为反码 1001 1011 最高位不变,其余位取反 11100100 获得反码后加1,最后的结果是 11100101 一个字节的 1110 0101 最高位是负数 1100101转换为10进制的结果是101 所以最终打印输出number =%d 的结果是-101 */
    printf("16进制0x9b按照有符号输出的结果是 %d \n",number);


    /* 无符号数输出时直接输出1001 1011转换为十进制的输出结果便可 1001 1011转换为十进制的结果是155 因为 */

    printf("16进制0x9b按照无符号输出的结果是 %u \n",number&0x000000ff);
    // 1001 1011 
    printf("number = %x \n",number);
    system("pause");
    return 0;
}

2.5 常量

2.5.1 常量概述

任何基本类型都有变量和常量两种类型。
常量是其值在运行期间不能修改,例如小数3.14,字符'a'都是常量,常量之因此不能被修改,是由于常量是在文字常量区。内存在存储数据时,考虑到数据不一样的用途和特色,把内存条分为各类区域:栈区、堆区、代码区、文字常量区、全局区。每一个区域的数据类型都有各自的特色。

2.5.2 平常开发中经常使用的常量

在平常开发中经常使用的常量有字符常量、短整型常量、长整型常量、单精度浮点型常量以及双精度浮点型常量。

结合C语言提供的printf函数以及格式替换符实现格式化输出常量的值

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 常量 常量一旦定义后不能被修改(赋值) @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    //平常开发中经常使用的常量
    //%c 用于输出一个字符,这里输出字符a
    printf("char c= %c \n",'a');
    //%hd用于输出短整型
    printf("short s= %hd \n",10);
    //%d用于输出整型
    printf("int i= %d \n",100);
    //%ld用于输出长整型
    printf("long l= %ld \n",10000);
    //%lld用于输出长长整型
    printf("long ll=%lld \n",100000000000000000);
    //%f用于输出单精度浮点型
    printf("float f= %f",3.14f);
    //%ld用于输出双精度浮点型
    printf("double d= %lf",3.14);
    //常量一旦定义后不能被修改(赋值)
    //10 = 20;

    
    system("pause");
    return 0;
}

2.5.3 const常量

C语言中除了字面量常量外,还可使用const修饰变量,使其成为不能直接修改,即常量,可是能够经过获取变量地址来间接修改。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* const常量 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    const int number = 12;
    //这里会发生编译错误,由于const修饰的变量是常量,常量一旦定义后没法再次被赋值
    //number = 18;

    //const虽然没法直接修改,可是能够经过间接的方式依然能够修改
    const long id = 10001;
    printf("常量 id的内存地址是%p \n", &id);

    printf("常量id修改以前的值是%d \n", id);

    //经过间接的方式修改id的值
    //(int*) 类型转换为非 常量地址
    //* 根据地址取出内容
    *(int*)(&id) = 10002;
    printf("常量id修改以后的值是%d \n", id);

    system("pause");
    return 0;
}

2.5.4 define常量

C语言中除了使用const定义常量之外还可使用#define来定义真正意义的常量,由于在C语言层面没法直接或者间接修改其值,由于C语言只能操做内存,不能操做寄存器,而#define定义的常量是在寄存器内部产生的。

在使用#define定义常量的语法为 #define CONST_NAME CONST_VALUE,须要注意的是使用#define定义常量时不须要使用分号。

#define _CRT_SECURE_NO_WARNINGS
//使用 #define定义常量
#define MY_NAME "Tony"
#define MY_GENDER "Boy"
#include <stdio.h>
#include <stdlib.h>
/* #define常量 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    //使用常量
    printf("my name is %s and my gender is %s \n",MY_NAME,MY_GENDER);
    system("pause");
    return 0;
}

使用#define定义常量时建议使用有意义的常量名,使得别人在使用常量时能明确知道其含义,常量亦能够在多个方法中使用,若是须要修改常量,只须要修改一次即可以实现批量修改,效率高并且准确。

define常量的应用案例:实现代码混淆

首先定义头文件define.h,其内容为

/* 使用define实现代码混淆 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
#include <stdio.h>
#include <stdlib.h>
#define _ void
#define __ main()
#define ___ {
#define ____ system("notepad");
#define _____ system("pause");
#define ______ }

而后在源文件define.c中使用#include包含define.h并引用define.h头文件中定义的常量值

#include "define.h"

/* 使用define实现代码混淆 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
_ __ ___ ____ _____ ______

而后在Visual Studio 2019中运行该程序,程序可以调用Windows的记事本程序。

2.6 变量

2.6.1 变量的概述

变量就是能够变化的量,在同一时刻,内存中的变量只能存储一份值。若是对变量进行修改,新值会覆盖旧值。

这里能够经过一段程序结合Visual Studio 2019的调试功能理解变量的本质。

#include <stdio.h>
#include <stdlib.h>

/* 变量在内存中的存储 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    int num = 20;
    //查看num的内存地址
    printf("整数变量num的地址是%p\n", &num);
    printf("整数变量num = %d\n", num);
    num = 30;
    printf("修改以后整数变量num的值是%d\n", num);
    system("pause");
    return 0;

}

首先经过printf的格式替换符%p获取变量的内存地址,而后经过Visual Studio 2019的调试->窗口->内存菜单查看内存,并在地址栏中输入整数变量num的地址,查看变量值。
Visual Studio 2019中查看内存的值默认是一字节,不带符号显示,能够经过鼠标右键设置成四字节、带符号显示。
Visual Studio 2019 调试变量
而后逐语句执行,并观察变量的值。
查看变量的值

生活中随处可见变量,例如股市的涨跌,游戏人物的武力值都是不断变化的。变量在使用前必需要先定义即声明并赋初始值,不然会出现编译错误。定义变量时系统会针对变量的类型开辟指定的内存空间。

2.6.2 变量的定义

变量的定义表示让变量在系统中存在,系统给变量开辟内存空间,变量定义的格式是 类型名+类型名,例如 int number,number就是变量名,类型为int,意味着编译器会针对number变量开辟4个字节的内存空间。
变量名的本质就是空间内容的别名,操做变量就是操做变量表明的那块内存空间。
变量使用前必须赋值完成初始化,即变量定义时就给变量赋值,例如int age=0;,不然会发生编译错误:error C4700: 使用了未初始化的局部变量

在定义变量时,变量名还要遵照如下的命名规则。

  • 变量名由字母、数字、下划线组成。
  • 变量名不能以数字开头。
  • 变量名不能是关键字,关键字是被C语言赋予了特殊的含义,可是能够包含关键字,Visual Studio 2019中蓝色的都是关键字。
#include <stdio.h>
#include <stdlib.h>

/* 变量的声明赋值及其命名规范 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    //合法的标识符
    int number;
    //见名知意
    int age;
    char ch;
    double db;
    //变量名不能是关键字
    //int void;
    //变量名不能以数字开头
    //int 1num;
    /****************************************编译器特性*******************************/
    //VC支持中文变量,GCC不支持中文命名
    int 年龄 = 29;
    printf("年龄 =%d\n", 年龄);
    //在老版(C++11以前)的编译器中,变量声明必须放在函数调用以前
    /****************************************编译器特性*******************************/
    //声明多个变量
    int one, two, three;
    system("pause");

    return 0;
}

定义变量时赋值叫变量的初始化,而定义完成之后再赋值不叫初始化,只是单纯的赋值。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 变量的定义 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    //变量定义的语法 变量类型 变量名
    int number;
    //变量使用前必须定义并初始化,初始化即定义时给变量赋值,不然会发生编译错误
    //printf("number = %d",number);

    //非法的变量名 会形成程序编译错误
    // 不能以数字开头
    //int 1number;
    //只能由字母、数字、下划线组成,这里包含了空格
    // int num ber;

    //定义时赋值叫变量的初始化
    int age = 26;

    //变量定义
    int val;
    //定义完成后再赋值就不叫初始化,只是单纯的赋值
    val = 12;


    system("pause");
    return 0;
}

2.6.3 变量的声明

变量的声明表示告诉编译器该变量已经存在,此处经过编译,可是不会再开辟内存空间。
若是变量定义在使用前面,编译器能够自动识别变量声明,由于编译器在编译时是从上到下逐语句编译。

若是变量的定义再也不使用的前面,可使用extern关键字显示声明变量,在声明时不用赋值,不然会引起变量“重定义,屡次初始化的编译错误”。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 变量的声明 变量的声明包含显示声明和非显示声明 通常状况下都是非显示声明,即变量的定义在使用前面。编译器编译时是从上到下逐语句编译的,当变量的定义在使用前面,编译器可以自动识别变量的声明 若是变量的定义在使用的后面,那么此时须要使用extern来显示声明变量,在声明时不用赋值,不然会引起变量“重定义,屡次初始化的编译错误 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
//显示声明变量,不用赋值,告诉编译器在使用到该变量的地方编译经过
extern int age;

//extern int age=300; // error C2374: “age”: 重定义;屡次初始化
int main(int argc, char* argv[]) {
    //编译器从上到下逐行编译,
    int number=10;
    //变量的定义在使用以前,此处会自动识别变量的声明
    printf("number = %d \n",number);

    printf("age = %d",age);
    system("pause");
    return 0;
}
//变量的定义在使用以后
int age = 26;

可是若是最终变量没有定义在使用以后,程序运行时仍是会出现异常,例如这里若是注释int age = 26;就会出现 没法解析的外部符号 age。

平常开发中一般都是先定义再使用变量,并且一般都须要在定义时初始化即赋值,若是使用了一个没有赋值的变量,程序会发生编译错误。

#include <stdio.h>
#include <stdlib.h>
/* 变量在使用前必须初始化赋值,不然会出现C4700错误-> "使用了未初始化的局部变量" @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main() {
    //声明一个整数变量并未初始化
    int num;
    //编译错误错误 C4700 使用了未初始化的局部变量“num”
    printf("num =%d\n", num);
    system("pause");
    return 0;
}

在定义变量时能够针对变量的类型赋对应的初始值,例如整数赋值为0,浮点数赋值为0.0。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 变量赋初始值 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {

    //变量初始化时若是类型为整数,推荐初始化为0
    int number = 0;
    double dbl = 0.0;
    char c = '\u00';
    

    system("pause");
    return 0;
}

2.6.4 变量的使用

变量的使用表示对变量的读写操做,所谓读就是获取变量的值,例如使用printf()输出变量的值,写就是赋值以及各类变量的运算,C语言中使用"="表示赋值,赋值是将右边的值赋值给左边的变量,也就是操做内存空间,除此之外后续还会学习各类运算符,例如算术运算符、逻辑运算符、关系运算符、三元运算符、位运算符等等。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 变量的操做:读&写 打印输出就是读操做 赋值操做就是写操做 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    //变量赋值 就是变量的写操做
    int age = 26;
    //printf函数就是对变量的读操做
    printf("age = %d\n ",age);
    //=是将右边的值赋值给左边的变量
    int my_age = age;
    //这里也会打印输出my_age = 26
    printf("my_age = %d\n ", my_age);
    system("pause");
    return 0;
}

变量除了能够赋值之外还能够进行算术运算

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 变量的操做:算术运算 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    int left = 10;
    int right = 20;
    int result = left + right;
    printf("%d + %d = %d \n",left,right, result);
    result = right - left;
    printf("%d - %d = %d \n", right, left, result);
    system("pause");
    return 0;
}

须要注意的是变量全部的运算都是经过CPU来完成的。

#include <stdio.h>
#include <stdlib.h>
/* 变量运算的原理 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    //只能给变量赋值
    int a = 1;
    int b = 2;
    //分配四个字节的内存
    int c;
    printf("变量a的地址是%p\t,变量b的地址是%p\t,变量c的地址是%p\n", &a, &b, &c);
    //数据的运算是在CPU的寄存器完成的
    c = a + b;
    c = b - a;
    //对数据的操做是由CPU完成的,所以这里不能直接复制
    //a + 1 = 4;
    printf("c=%d\n", c);
    system("pause");

    return 0;

}

使用汇编语言实现变量的赋值以及变量的运算

#include <stdio.h>
#include <stdlib.h>
/* 使用汇编语言实现变量的赋值以及运算来理解数据的运算是在CPU内部的寄存器完成的 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main() {

    //申请四个字节的内存
    int a;
    printf("整数变量a的地址是%p\n", &a);


    //变量的赋值都是经过CPU的寄存器来完成的
    //这里借助汇编语言实现将10赋值给变量a
    _asm {

        mov eax, 10
        mov a, eax
    }

    printf("整数变量a的值等于%d\n", a);

    _asm {

        //把变量a的值赋值给寄存器eax
        mov eax, a
        //将eax的值加5
        add eax, 5
        //把eax的值赋值给a
        mov a, eax
    }
    printf("变量a加5以后的结果是%d\n", a);
    system("pause");

    return 0;
}

在运行程序时。能够经过调试查看寄存器EAX的变化,证实数据的运算都是经过CPU来完成的,而后将运算的结果存储在寄存器中。
寄存器EAX

2.6.5 变量的内存机制

当声明变量时,编译器会使用变量表维护变量的信息,包含变量名、变量类型以及变量的内存地址。而内存中经过变量地址关联变量值。若是在程序中使用了未声明的变量,则会发生编译错误。

#include <stdio.h>
#include <stdlib.h>

/* 编译器和内存对变量的处理 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {

    //声明3个整数变量
    int a, b, c;
    //这里会发生编译错误 不能使用未声明的变量
    //printf(" %d\n", d);
    system("pause");
    return 0;
}

2.6.6 变量交换

变量的交换,能够经过采用中间变量,算术(加减法或者乘除法)运算、异或运算
三种方式实现,其应用场景主要在使用在排序算法中,每种实现变量交换方法的时空复杂度有不一样的考量。

经过中间变量实现变量交换

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 变量交换的三种方式 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/09/03 */
int main(int argc, char* argv[]) {
    int left = 5;
    int right = 10;
    printf("使用临时变量实现变量交换交换以前\t left=%d \t right=%d\n", left, right);
    int middle = left;
    left = right;
    right = middle;
    printf("使用临时变量实现变量交换交换以后\t left=%d \t right=%d\n", left, right);
    system("pause");
    return 0;
}

经过算术运算实现变量交换

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 变量的交换:经过算术运算实现 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/09/03 */
int main(int argc, char* argv[]) {
    int left = 5;
    int right = 10;
    printf("使用算术运算实现变量交换交换以前\t left=%d \t right=%d\n", left, right);
    left = left + right; // 加号变成乘号
    right = left - right;//减号变成除号
    left = left - right; //减号变成除号
    printf("使用算术运算实现变量交换交换以后\t left=%d \t right=%d\n", left, right);

    system("pause");
    return 0;
}

使用异或运算实现变量的交换

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 变量的交换:使用异或运算实现变量交换 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/09/03 */
int main(int argc, char* argv[]) {
    int left = 5;
    int right = 10;
    printf("使用异或运算实现变量交换交换以前\t left=%d \t right=%d\n", left, right);
    left = left ^ right;
    right = left ^ right;
    left = left ^ right;
    printf("使用异或运算实现变量交换交换以后\t left=%d \t right=%d\n", left, right);
    system("pause");
    return 0;
}

2.7 读取键盘的输入

以前在定义整数变量并初始化值时只能写死一个值,整数变量建议初始化时为0,这样能够避免许多没必要要的错误出现。
为了让程序变得更加灵活,这里引入C语言标准库函数scanf()函数实现基于终端的人机交互。固然平常应用(例如淘宝、京东)都是基于UI界面实现人机交互,可是底层处理的逻辑是同样的。

scanf()函数能够从键盘中读取用户输入的整数、小数、字符等等,该函数的参数须要传递数据格式和变量地址两个参数,数据格式指的就是用户输入的数据类型,例如整数、小数等等,变量地址就是&变量名,&表示地址符号,变量名只能表明空间的内容。
scanf()函数是阻塞式的,当用户输入数据以后,数据会存储到标准输入缓存区中,而后scanf()函数负责从标准缓冲区中拿指定格式的数据,若是用户不输入数据,那么程序会一直阻塞在那里。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* scanf函数获取键盘的输入实现终端不带界面交互 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {

    //变量初始化时若是类型为整数,推荐初始化为0
    int age = 0;
    printf("请输入你的年龄\n(按回车键结束)");
    //当用户输入数据并回车以后先到达标准输入缓冲区
    //scanf函数负责从标准缓冲区拿整数,此函数是阻塞的,即等到用户输入数据才会往下执行
    //%d表示提取键盘输入的数字
    //变量名仅仅表明变量的内容,若是想要获取变量的起始地址,须要使用&变量名
    scanf("%d",&age);

    printf("你的年龄是%d\n",age);

    system("pause");
    return 0;
}

2.8 数据类型

2.8.1 数据类型概述

数据类型就是给数据分类,其目的就是合理的利用内存空间,提升存储效率。
类型是抽象的概念,类型有大小,可是没有空间,系统不会给类型分配空间,可是会给类型定义的变量分配空间,例如定义变量 int age =28;时系统会给age变量分配四个字节的空间。
不一样的数据类型占据不一样的内存大小,其存储数据的极限也不同、可以执行的运算也是不相同的。
C语言中基本数据类型有整型、浮点型、字符型,布尔型。其余的类型都是由基本数据类型封装而来的。

其中整数按照不一样的字节大小有short,int,long,long long(C99支持)。
浮点数按照精度不一样有float,double,其中float表示单精度浮点型,double表示双精度浮点型。
字符只有char表示,用于存储单个字符。
布尔使用boolean表示,C语言中的0表示false,非0表示true。

2.8.2 sizeof()关键字

C语言提供了提供了sizeof()关键字来获取类型占据的内存空间。
sizeof()中能够传类型名或者变量名。传递变量名其实是求变量类型的大小

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
/* sizeof关键字的使用 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    printf("char 占据的字节数量是%d\n",sizeof(char));
    printf("short 占据的字节数量是%d\n",sizeof(short));
    printf("int 占据的字节数量是%d\n",sizeof(int));
    printf("long 占据的字节数量是%d\n",sizeof(long));
    printf("long long 占据的字节数量是%d\n",sizeof(long long));
    printf("float 占据的字节数量是%d\n",sizeof(float));
    printf("double 占据的字节数量是%d\n",sizeof(double));
    printf("bool 占据的字节数量是%d\n",sizeof(bool));
    system("pause");
    return 0;
}

须要注意的是C语言相同的数据类型在不一样的操做系统环境下占据的空间是不同的。
在Visual Studio 2019中,C程序默认是以32位运行的。
x86
32位sizeof()关键字运算结果
32位sizeof()关键字运算
若是想要切换到64位下运行,只须要将X86换成x64便可
x64
64位sizeof()关键字运算
在Windows平台下long类型不管是在32位仍是64位都是占据四个字节,而Linux(Ubuntu18.04)则是占据8个字节。

Ubuntu18.04下sizeof()关键字测试

//
// Created by guanglei on 8/28/19.
//
#include <stdio.h>
#include <stdlib.h>
int main(){

    printf("char 占据的字节数量是%d\n",sizeof(char));
    printf("short 占据的字节数量是%d\n",sizeof(short));
    printf("int 占据的字节数量是%d\n",sizeof(int));
    printf("long 占据的字节数量是%d\n",sizeof(long));
    printf("long long 占据的字节数量是%d\n",sizeof(long long));
    printf("float 占据的字节数量是%d\n",sizeof(float));
    printf("double 占据的字节数量是%d\n",sizeof(double));
    printf("_Bool 占据的字节数量是%d\n",sizeof(_Bool));
    return 0;
}

Ubuntu18.04 sizeof()关键字运算结果
Ubuntu18.04 sizeof()关键字运算结果

2.8.3 数据类型的有符号和无符号

有符号表示数据有正负之分,而无符号则没有正负之分,即全是正数(大于等于0数)。

有符号数最高位(左边第一位)是符号位,若是是0表示该数据是整数,1表示该数据为负数。
以2个字节的数据 1为例子,其二进制表示为0000 0000 0000 0001由于是正数,所以最高位是0,-1的二进制表示为1000 0000 0000 0001,由于是负数,所以最高位是1。
无符号数即没有符号位,全是数据位,仍是以2个字节的数据1为例子,其二进制表示为0000 0000 0000 0001

2.8.3.1 有符号和无符号常量(整型和长整型)

整数常量默认是有符号的整数,若是想要变成无符号数,须要在正数后面加上U后缀。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 有符号数(整型,长整型)和无符号数(整型,长整型) 常量 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    //整型常量10默认是有符号的整数,若是想要变成无符号数,须要在10后面加上U,即10U
    printf("%d \n",10);
    printf("%u \n", 10U);

    //长整型常量的有符号和无符号

    //有符号长整型
    printf("%ld \n",10L);
    //无符号长整型
    printf("%lu \n", 10U);

    system("pause");
    return 0;
}

2.8.3.2 有符号和无符号变量

C语言中的变量默认就是有符号的,使用signed修饰,例如signed int,可是一般直接使用int声明变量,而无符号的变量使用unsigned修饰,例如unsigned int,这里的unsigned不能省略。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 有符号和无符号变量 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {

    //整数默认就是有符号的,即默认是使用signed
    signed int number = 12;
    printf("number = %d ",number);

    // 无符号必须加上unsigned
    unsigned int value = 12;
    printf("value = %u ", value);


    system("pause");
    return 0;
}

2.8.4 数据类型的极限

数据类型都有其存储范围(即存储的最大值和最小值),C语言中的limits.h和float.h头文件中分别定义了整数和浮点数的极限。在使用数据类型时,切勿超过其极限,不然会形成程序异常。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <float.h>

/* 数据类型的极限 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    printf("int能存储的最大值是%d\tint能存储的最小值是%d,占据的字节数量是%d\n", INT_MAX, INT_MIN, sizeof(int));
    printf("double能存储的最大值是%e\t double能存储的最小值是%e,double占据的字节数量是%d\n \n", DBL_MAX, DBL_MIN,sizeof(double));

    printf("unsigned char 表示的最大值是%d\n",UCHAR_MAX);
    //无符号的char存储的最大值是255 这里超过了最大范围,输出的结果不是预期的256,,并且0
    unsigned char num = 255+1;
    printf("num = %d\n",num);

    system("pause");

    return 0;
}

2.8.5 数据类型与printf

在使用prinf函数打印变量的值时,若是没有正确使用格式转换符,获得一个非预期的结果,由于printf函数不会进行类型转换的操做,只会按照传递的格式转换数据后输出到终端上。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
/* printf处理数据类型 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]){

    int number = 10;
    
    printf("number = %f \n",number);
    printf("number = %f \n", (float)number);


    float flt = 10.8;
    //printf不会进行数据类型转换
    printf("flt = %d \n",flt);
    //使用强制类型转换实现输出正确的结果
    printf("flt = %d \n", (int)flt);


    return 0;
}

2.8.6 数据在内存中的存储

在手机、PC上,数据在内存中的存储是低位在低字节,高位在高字节。
而在Unix大型服务器上为了加速寻址,数据在内存中的存储是低位在高字节, 高位在低字节。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
/* 数据在内存中的存储 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {

    int number = 10;
    printf("number的内存地址是%p\n", &number);
    printf("number = %d",number);


    return 0;
}

数据在内存中的存储
数据在内存中的存储

2.9 整型

2.9.1 整数常量

C语言整数常量可使用u后缀表示位无符号整数,使用l后缀表示long类型的整数,使用ll后缀表示为long long类型的整数

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 整数常量的三种类型 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/09/03 */
int main(int argc, char* argv[]) {
    printf("有符号整数常量%d\n",100);
    printf("无符号整型常量%u\n",100u);
    printf("有符号长整型常量%ld\n",2147483647L);
    printf("有符号长长整型常量%lld\n",9223372036854775807LL);
    system("pause");
    return 0;
}

同时C语言中的整型常量支持八进制、十进制和十六进制三种进制类型,不支持二进制。

  • 八进制由0-7之间的八个整数组成,八进制的常量值是以0开头。
  • 十进制由0-9之间的十个整数组成。
  • 十六进制由0-9,a-f之间的十个整数加上6个字母组成。

printf()函数针对整数的三种进制类型提供了对应的输出格式,其中八进制输出使用%o表示,十进制使用%d表示,十六进制使用%f表示,#表示输出进制的完整格式,例如八进制会在最左边填充一个0,十六进制会在最左边填充0x。

不一样进制的输出不会改变数据原来的值,底层依然是以二进制存储,只是输出的表现形式变了。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* printf输出整数的三个进制表现方式 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    int number = 0;
    printf("请输入一个整数\n");
    scanf("%d",&number);
    //不一样进制的输出不会修改number的值,只是数据的表现形式变化了
    // %o表示按照八进制解析,#表示输出完整进制格式,即以0开头
    printf("%d按照八进制输出的结果是%#o\n",number,number);
    printf("%d按照十进制进制输出的结果是%d\n",number, number);
    // %x表示按照十六进制解析,#表示输出完整进制格式,即以0x开头
    printf("%d按照十六进制输出的结果是%#x\n",number, number);
    system("pause");
    return 0;
}

2.9.2 整数的极限

整数按照占据不一样的字节大小能够分为short,int,long和long long 四种类型,它们默认是有符号(signed)类型用于存储正负数,而对应的无符号类型(unsigned)则用来存储非负数的整数。
整数的极限定义以#define的方式在<limits.h>头文件中定义,Visual Studio 2019中能够选中一个极限值常量,而后使用快捷键F12转到定义,直接查看常量值的表示范围。

转到定义
转到定义

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
/* 整数的极限 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int int_limits_main(int argc, char* argv[]) {
    //16位(嵌入式系统) int和short是等价的
    printf("short能存储的最大值是%d\tshort能存储的最小值是%d,占据的字节数量是%d\n", SHRT_MAX, SHRT_MIN, sizeof(short));
    printf("unsigned short能存储的最大值是%d\n", USHRT_MAX);
    //32位和64位系统 int和long是等价的
    printf("int能存储的最大值是%d\tint能存储的最小值是%d,占据的字节数量是%d\n", INT_MAX, INT_MIN, sizeof(int));
    printf("unsigned int能存储的最大值是%d\n", UINT_MAX);
    //无符号的整数 最小值都是0 即不能表示负数
    printf("long能存储的最大值是%d\tlong能存储的最小值是%d,占据的字节数量是%d\n", LONG_MAX, LONG_MIN, sizeof(long));
    printf("long long能存储的最大值是%lld\tlong long能存储的最小值是%lld,占据的字节数量是%d\n", LLONG_MAX, LLONG_MIN, sizeof(long long));
    printf("unsigned long long 能存储的最大值是%llu\n", ULLONG_MAX);
    system("pause");
    return 0;
}

在使用整数参与运算时,须要考虑到数据范围对应的极限,不然会发生错误的结果

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 整数的越界 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    //为了保证结果运算正确,必须在极限范围以内
    unsigned short int  shortnum = 65536;
    printf("无符号short int所能存储的最大值是%d\n", USHRT_MAX);
    printf("shortnum=%d", shortnum); //结果为0 由于chnum所能表示的最大值为65535,这里发生了越界,结果错误
    system("pause");
    return 0;
}

若是想要存储身份证号等超大类型的整数数据,可使用无符号的long long类型来存储

#include <stdio.h>
#include <stdlib.h>
/* long long 的应用场景 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main() {

    unsigned long long mobilePhone = 18601767221;
    printf("mobilePhone=%llu\n", mobilePhone);
    unsigned long long qq = 1079351401;
    printf(" qq = %llu", qq);
    system("pause");
    return 0;
}

2.9.3 跨平台的整型

为了解决不一样平台,相同的类型占据的大小不一致的问题,C语言标准委员会在C99标准中提出了跨平台的整数,在<stdint.h>头文件中定义,意味着一样的类型在不一样的系统下的大小是一致的。
例如int64_t在全部实现C99标准的编译器下占据的都是8个字节,int32_t在全部实现C99标准的编译器下占据的都是4个字节。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
/* 跨平台的整数 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    long val = 100;
    printf("windows下long占据的字节数量是%d\n", sizeof(val));

    //在不一样的平台下占据都是32字节
    int32_t int_32_MAX_VALUE = INT32_MAX;
    printf("sizeof(int_32_MAX_VALUE ) = %d\n", sizeof(int_32_MAX_VALUE));
    printf("int_32_MAX_VALUE = %d\n", int_32_MAX_VALUE);


    //在不一样的平台下占据都是64字节
    int64_t int_64_MAX_VALUE = INT64_MAX;
    printf("sizeof(int_64_MAX_VALUE ) = %d\n", sizeof(int_64_MAX_VALUE));
    printf("int_64_MAX_VALUE = %lld\n", int_64_MAX_VALUE);
    system("pause");
    return 0;
}

三角形面积计算:给定三角形的三条边,使用math.h头文件中的sqrt函数实现三角形的面积计算。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
/* 根据给出的边长求面积 使用math.h文件中提供的开平方根函数 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    int a = 6;
    int b = 8;
    int c = 10;
    int p = (a + b + c) / 2;
    //sqrt返回float,这里使用赋值运算完成了类型转换
    int s = sqrt(p * (p - a) * (p - b) * (p - c));
    printf("三角形的面积是%d\n", s);
    system("pause");
    return 0;
}

2.10 浮点型

2.10.1 浮点型常量

浮点型即生活中使用的小数类型(例如3.14),例如帐户的余额,银行的存款利率等等都是浮点型。
C语言中按照精度的不一样分别使用float,double和long double表示,默认浮点类型是double,float占据四个字节,double占据8个字节,long double大于等于8个字节,Windows 32位和64位系统long double都是8个字节,Ubuntu18.04系统下long double是占据16个字节。

浮点数的常量可使用十进制的小数和科学计数法表示,科学计数法能够存储特大或者特小的数字

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 浮点常量 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    //输出结果显示3.14占据8个字节,由于浮点数默认是double类型
    printf("浮点数3.14占据的字节数量是%d\n",sizeof(3.14));
    //若是以f结尾的就是float类型
    printf("浮点数3.14f占据的字节数量是%d\n",sizeof(3.14f));



    //十进制
    float flt = 12.0f; //小数后面加f表示float类型
    double dbl = 12.0; //小数默认是double类型
    //科学计数法
    double db1 = 0.12e3;
    //e以前必须有数字,指数必须为整数
    double db2 = 12000.124e5; //e5表示10的5次方
    //%f默认输出小数点后六位
    printf("flt = %f \n", flt);
    printf("db1 = %f \t db2 = %f\n", db1, db2);
    system("pause");
    return 0;
}

2.10.2 浮点数变量

在初始化浮点数变量时,默认值建议为0.0或者0.0f,赋值时变量的值和变量的类型保持一致。
printf()函数输出float类型的变量使用格式符%f,输出double类型的变量使用%lf。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 浮点数变量 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    //在赋值时尽可能保证赋值号(=)左右两边类型一致,若是将表示范围大的值赋值给表示范围小的变量,可能会形成数据溢出,
    float flt = 3.14f;
    printf("flt = %f \n",flt);
    flt = 0.0f;
    printf("flt = %f \n", flt);

    double dbl = 5.67;
    //printf()默认输出小数后6位数
    printf("dbl = %lf \n", dbl);
    //若是只要输出小数点后2位数,可使用格式符 %.2lf 实现
    printf("dbl = %.2lf \n",dbl);

    //请输入一个浮点数
    printf("请输入一个浮点数\n");
    scanf("%lf",&dbl);
    printf("你输入的浮点数是%.2lf \n",dbl);


    system("pause");
    return 0;
}

2.10.3 浮点型极限

C语言在limits.h的头文件中使用常量定义了float和double以及long double的极限值,咱们可使用sizeof()关键字求出float,double和long double的字节数量以及使用常量
FLT_MAX,FLT_MIN求出float表示的最大值和最小值以及DBL_MAX,DBL_MIN求出double所能表示的最大值和最小值。

在windows上double和long double是等价的,可是在Linux(例如Ubuntu 18.04上)long double是占据16个字节,这也就意味着long double的极限比double更大。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <float.h>
/* 浮点数占据的内存数量和取值范围 在32位和64位Windows上double 和long double是等价的 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    printf("float占据的字节数量是%d\n", sizeof(float));
    printf("float能存储的最大值是%e\t float能存储的最小值是%e \n", FLT_MAX, FLT_MIN);
    printf("\n");
    printf("double占据的字节数量是%d \n ",sizeof(double));
    printf("double能存储的最大值是%e\t double能存储的最小值是%e \n", DBL_MAX, DBL_MIN);
    printf("\n");
    printf("long double占据的字节数量是%d\n", sizeof(long double));
    printf("long double 能存储的最大值是%e\t long ouble能存储的最小值是%e \n", LDBL_MAX, LDBL_MIN);


    system("pause");
    return 0;
}

2.10.4 浮点数在内存中的存储

首先明确一点,不管是整型、浮点型仍是字符等等数据类型在计算机底层都是以二进制的方式存储的。

浮点数在内存中的存储和整数不一样,由于整数均可以转换为一一对应的二进制数据。
而浮点数的存储是由符号位(sign)+指数位(exponent)+小数位(fraction)组成。
其中float是由1位符号位+8位指数+23位小数组成,
而double是由1位符号位+11位指数位+52位小数位组成。

int和float一样占据四个字节的内存,可是float所能表示的最大值比int大得多,其根本缘由是浮点数在内存中是以指数的方式存储。
咱们都知道在内存中,一个float类型的实数变量是占据32位,即32个二进制的0或者1组成

四字节浮点数  最左边的第一位是最高位

0000 0000 0000 0000 0000 0000 0000 0000

从低位依次到高位叫第0位和第31位,这32位能够由三部分组成:

符号位:第31位数表示符号位,若是为0表示整数,若是为1表示负数
指数:第23位到第30位,这8个二进制表示该实数转化为规格化的二进制实数后的指数与127(127即所谓的偏移量)之和所谓阶码,规格化的二进制实数只能在-127-127之间。
小数位:第0位到第22位,最多能够表示23位二进制小数,不然超过了就会产生偏差。

2.10.5 浮点数相等性判断

float占据四个字节,提供的有效位是6-7位,而double占据八个字节,提供的有效位数是15-16位,若是在使用float或者double表示实数时超过有效数字,若拿来进行关系运算(例如等于)的话,会获得一个错误的结果。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 浮点数的相等性判断 若是实数超过有效范围,使用==判断会出错 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    float flt1 = 1.00000000001;
    float flt2 = 1.00000000000000000001;
    //由于float的有效数字是6-7位 这里超出有效数字 计算不许确
    printf(" flt1 == flt2 ? %d\n", (flt1 == flt2)); // 输出结果1表示相等 0则表示不相等

    //double精确的有效位数是15-16位,这里也超出了有效数字,计算不够正确 
    double db1 = 1.00000000000000000000000000000001;
    double db2 = 1.000000000000000000000000000000000000000000000000000000000000000000000000000000001;

    printf(" db1 == db2 ? %d\n", (db1 == db2)); // 输出结果1表示相等 0则表示不相等
    system("pause");
    return 0;
}

中美GDP计算:给出当前美国和中国的GDP以及增加率,使用math.h的pow函数实现计算出中国GDP超过美国GDP的年份

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
/* 给出当前中国、美国的GDP以及增加率,计算中国超过美国的年份 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    //当前中美GDP
    double ch_current_gdp = 14.6;
    double us_current_gdp = 20.5;

    //当前中美GDP的增加率
    double ch_rate = 1.06;
    double us_rate = 1.04;

    double ch_gdp=0.0;
    double us_gdp=0.0;
    int year=2018;
    for (int i = 1; i <= 100; i++) {
        //使用pow函数计算中美每一年增加后的GDP
        ch_gdp = ch_current_gdp * pow(ch_rate, i);
        us_gdp = us_current_gdp * pow(us_rate, i);
        year++;
        printf("%d年中国的GDP是%f\n", year, ch_gdp);
        printf("%d年美国的GDP是%f\n", year, us_gdp);

        if (ch_gdp > us_gdp) {
            printf("在%d年,中国的GDP超越了美国的GDP", year);
            break;
        }

    }

    system("pause");
    return 0;
}

2.11 字符型

2.11.1 字符型常量

在平常开发应用中,字符是最经常使用的数据类型。由于生活中的许多数据都是经过字符表示,而不是数字表示,字符能表示更多的含义,最典型的就是网络协议,例如超文本传输协议HTTP协议。

C语言中字符使用一对单引号('')表示,注意单引号只能做用域一个字符。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 字符常量(英文) @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    //输出英文字符
    printf("输出英文字符%c\n",'a');
    printf("char占据的字节数量是%d\n",sizeof(char));
    system("pause");
    return 0;
}

C语言中的char只占用1个字节,所以不能存储中文,若是想要存储中文,须要使用wchar_t表示,而后还要进行本地化的设置

#define _CRT_SECURE_NO_WARNINGS
#include <locale.h> //引入本地化的头文件
#include <stdio.h>
#include <stdlib.h>
/* 中文字符 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {


    //设置本地化
    setlocale(LC_ALL, "chs");
    wchar_t ch = L'刘'; //使用wprintf()函数输出中文 wprintf(L"ch = %c \n",ch); system("pause"); return 0; }

除了使用prinf函数结合%c输出字符之外,C语言还提供了putchar()函数来输出字符

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 输出字符的两种方式 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    char c = 'A';
    putchar(c);
    printf("\n");
    printf("c = %c \n",c);
    system("pause");
    return 0;
}

2.11.2 字符的本质

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 字符变量 字符变量的本质存储的是字符的ASC||码值 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {

    //声明并初始化字符变量ch,初始化值为z
    char ch = 'z';
    printf("char ch = %c \n", ch);
    printf("字符变量%c对应的整数是%d \n",ch,ch);
    printf("char占据的字节大小是 %u \n",sizeof(ch));
    system("pause");
    return 0;
}

因为计算机最底层只能识别二进制的数据,可是字符不是二进制的数据。
若是将字符和特定的数值一一对应起来,这张对应表就是ASC||表。
若是字符变量ch存储的字符是'z',那么实际上存储的是字符z对应的ASC||值即整数122。即字符变量存储的本质就是存储的字符对应的ASC||值

平常开发中最经常使用的字符就是大小写字母以及数字字符
咱们能够借助printf()函数的格式%c和%d实现输出字符对应的ASC||值。
数字字符'0'表示的整数是48,小写字符'a'表示的整数是97,大写字符'A'表示的整数时65。数字0表示空字符。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 经常使用的字符对应的ASC||表 输出字符使用%c,输出ASC||值使用%d @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    char ch = '0';
    printf("字符%c对应的整数是%d\n", ch, ch);
    ch = 'a';
    printf("字符%c对应的整数是%d\n", ch, ch);
    ch = 'A';
    printf("字符%c对应的整数是%d\n", ch, ch);

    int number = 97;
    printf("数字%d对应的字符是%c\n", number, number);
    number = 65;
    printf("数字%d对应的字符是%c\n", number, number);
    number = 48;
    printf("数字%d对应的字符是%c\n", number, number);


    system("pause");
    return 0;
}

既然字符变量的本质存储的ASC||值,即整数。所以字符也能够参与加减运算。
因为ASC||码规定了小写字母''a' 表示97,而后一次递增,小写字母'z'表示122,而大写字母'A'表示65,而后依次递增,大写字母'Z'表示90。所以根据这个规律能够经过加减运算实现大小写字母转换。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 字符的大小写转换 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {

    
    //小写转大写 
    char lower_ch = 'a';
    // 0x20就是十进制的32
    char upper_ch = lower_ch - 0x20;

    printf("小写字母%c转换为大写字母的结果是%c \n",lower_ch,upper_ch);


    //大写转小写
    upper_ch = 'A';
    lower_ch = upper_ch + 32;
    printf("大写字母%c转换为小写字母的结果是%c \n", upper_ch, lower_ch);


    system("pause");
    return 0;
}

2.11.3 转义字符

一些特殊的符号没法直接显示时,咱们使用\特殊字符来表示。
例如\a表示发声,没法在终端上直接显示。
在平常开发中还会使用到各类经常使用的转义字符,例如\t实现tab的效果。
转义字符\n实现换行的效果,转义字符\实现路径转义。
%%输出百分号。
转义字符还可使用八进制和十六进制表示,用于表示字符对应的ASC||码值。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 转义字符 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {

    //声音没法输出在屏幕上显示,所以这里使用转字符\a实现发声
    printf("%c",'\a');
    //平常开发中经常使用的转移字符 \n实现换行
    printf("hello \n");
    printf("world \n");
    // \t 实现tab效果
    printf("C\t语\t言\t核\t心\t编\t程\n");
    // \r 实现光标移到行首 因为光标移到行首,最终只会输出C语言核心编程
    printf("CPrimerPlus%cC语言核心编程\n",'\r');
    //路径转义 \\表示路径转义 
    //使用system函数使用电脑上的微信,调用成功的前提条件是电脑上安装了微信
    system("\"C:\\Program Files (x86)\\Tencent\\WeChat\\WeChat.exe\"");
    // %%实现输出百分号
    printf("合格率为%%%d\n",90);

    //八进制转义和十六进制转义
    //八进制的62转换为十进制是50,50表示字符2
    char ch = '\62';
    printf("八进制的转义字符62转换为字符的结果是%c \n",ch);//2
    //十六进制的62转换为十进制是98,98表示的字符是b
    ch = '\x62';
    printf("十六进制的转义字符62转换为字符的结果是%c\n", ch); //b

    system("pause");
    return 0;
}

2.12 字符串

字符串用于表示字符序列,也就是一串使用" "包含起来的内容。
C语言中的字符串以\0结尾,这也就是意味着即便双引号""中什么都没有也会占据一个字节,而中文字符串中的每一个字符一样会占据两个字节

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* C语言字符串以\0结尾 空字符串也会占据一个字节 中文占据两个字节 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    //字符串是以/0结尾,这里字符串A占据2个字节
    printf("字符串A占据的字节数量是%d\n", sizeof("A"));
    //空字符串也是以\0结尾,所以这里占据一个字节
    printf("\"\"占据的字节数量为%d\n", sizeof("")); //以\0结尾

    //字符串单个中文占据两个字节
    printf("字符串刘光磊占据的字节数量是%d\n", sizeof("刘光磊")); //每一个中文占据两个字节,而后以\0结尾 所以是7个
    system("pause");
    return 0;
}

使用算术运算实现字符串加密解密

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 字符串加密解密简单实现 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {

    //使用字符数组初始化一个字符串 calc
    char str[5] = { 'c','a','l','c','\0' };

    //调用计算器
    system(str);

    printf("加密以前str = %s\n", str);


    //循环 将字符+1 由于字符的本质就是ASC||码 所以能够看成整数来使用
    for (int i = 0; i < 4; i++) {

        str[i] += 1;
    }

    printf("加密以后str = %s\n", str);


    //逆运算解密
    for (int i = 0; i < 4; i++) {

        str[i] -= 1;
    }
    printf("解密以后str = %s\n", str);
    
    system("pause");
    return 0;
}

2.13 布尔类型

bool类型只有两个值,即true和fasle,它们在内存中分别使用1和0表示,这样一个字节即可以存储bool类型的变量。
在C程序中使用bool类型的变量,须要引入头文件stdbool.h,后续的各类逻辑、关系运算以及选择结构if/else和while循环会大量使用bool类型的变量。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
/* bool类型的使用 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    bool flag = true;
    //占据的字节数量为1
    printf("bool占据的字节数量是%d\n", sizeof(flag));
    //成立的结果为1
    printf("bool = %d\n", flag);

    flag = false;
    //不成立的结果为0
    printf("bool = %d\n", flag);


    //bool在if/else结构和关系运算的使用
    int age = 20;
    bool isAdult = age > 18;
    if (isAdult==1) {
        printf("你成年了\n");
    }
    else {
        printf("你没有成年\n");
    }
    
    system("pause");
    return 0;
}

2.14 数据类型转换

2.14.1 数据类型自动转换

当多种数据类型(整数、浮点数、字符)同时参与运算时,会发生自动类型转换,容量小的数据类型的变量与容量大的大数据类型的变量作运算时,结果自动提高为容量大的数据类型,防止运算时超过极限值,防止运算结果的精度丢失。此时容量大小指的是,数据类型表示数的范围大小,而不是占用内存大小,好比float容量大于long的容量。

自动类型转换的规则以下

char,short->int->long->float->double->long double

其中对应的有符号类型还会自动转换为无符号类型,char和char运算,short和short运算也会转换为int。

char和int,double的自动类型转换

#include <stdio.h>
#include <stdlib.h>
/* 自动类型转换 在进行算术运算时,会发生自动类型转换 表示范围小的值自动转换为表示范围大的变量,保存精度 char->short>int->long->float->double->long double @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int type_convert_auto_main(int argc, char* argv[]) {

    char ch = 'A';
    printf("1.0占据的字节数量是%d\n", sizeof(1.0));
    //ch+1自动转换为int类型,所以占据四个字节
    printf("字符变量ch+1的字节数量是%d\n", sizeof(ch + 1));
    printf("字符变量ch+1.0的字节数量是%d\n", sizeof(ch + 1.0));
    getchar();

    return 0;
}

int和unsigned int的自动类型转换

#include <stdio.h>
#include <stdlib.h>
/* 自动类型转换 有符号类型int转换为无符号类型int @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {

    int number1 = -10;
    unsigned int number2 = 5;

    //有符号数和无符号数参与运算时,先将有符号数转换为无符号数

    printf("number1按照无符号输出结果为%u\n",number1);
    int result = number1 + number2;

    //使用三目运算和算术运算输出表达式number1+number2大于0仍是小于0
    //由于number1按照无符号的结果是4294967286 所以结果是大于0
    number1 + number2 > 0 ? printf("number1 + number2 > 0 \n"):printf("number1 + number2 < 0 \n");
    printf("按照有符号整数输出 result = %d \n", result);
    printf("按照无符号整数输出 result = %u \n", result);

    system("pause");
    return 0;

}

char、short的自动类型转换:因为char,short占据的内存空间太小,编译器规定,只要是char,short参与运算,都会自动转换为int类型。

#include <stdio.h>
#include <stdlib.h>
/* char,short的自动类型转换 char和short参与运算都会转换为int @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    
    char x = 10;
    int target = x + x;

    short s = 20;
    int short2 = s + s;
    int result = x + s;
    system("pause");
    return 0;

}

2.14.2 数据类型强制转换

强制类型转换就是在待转换的表达式的左边使用强转符(目标类型)实现

#include <stdio.h>
#include <stdlib.h>
/* 强制类型转换 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {

    float fl = 10.8;
    float flt = 10.3;
    int num = (int)fl + flt; //20.3 先把fl强制转换为int类型,而后再和flt相加,再转换为int,由于赋值会进行自动类型转换
    printf("num =%d\n", num);
    num = (int)(fl + flt);//21 先把fl和flt相加后,强制转换为int
    printf("num =%d\n", num);
    getchar();
    return 0;

}

强制类型转换只是临时改变变量的值

#include <stdio.h>
#include <stdlib.h>
/* 强制类型转换的特性 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main() {
    float flt = 3.14f;
    int number = 0;
    //强制类型转换只是临时改变变量flt的值,其本质是读取flt的值,而后强制转换为int类型的值
    number = (float)flt;
    //number =3
    printf("number = %d", number);
    // flt=3.14
    printf("flt = %f", flt);

    getchar();

    return 0;
}

在使用printf函数打印输出变量值时,不会进行自动类型转换,若是想要获取预期的结果,须要使用强制类型转换实现。

#include <stdio.h>
#include <stdlib.h>

/* printf()函数与强制类型转换 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main() {
    //由于printf函数不会进行类型转换,因此这里获得一个错误的结果
    printf("%d\n", 12.1);
    //12.1为浮点类型,这里使用强制类型转换实现转换为整数
    printf("%d\n", (int)12.1);
    printf("%f\n", 10); //整数按照浮点数解析,获得的结果就是0.000000
    printf("%f\n", (float)10); //强制类型转换
    getchar();
    return 0;
}

强制类型转换案例:实现对小数点后三位实现四舍五入

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>

/* 实现对小数点后三位实现四舍五入 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    printf("请输入四舍五入的三位小数\n");
    double input = 0.0;
    scanf("%lf", &input);
    double val = 1.235;
    //1.234*100=123.4 123.4+0.5=123 123/100.0=1.23

    // 1.235*100=123.5 123.5+0.5=124 124/100=1.24
    // 1.24>1.235
    // 1.24-1.235=0.05
    //1.235+0.05=1.24
    double result = (int)(input * 100 + 0.5) / 100.0;
    printf("result =%.2f", result);
    getchar();
    return 0;

}

强制类型转换案例:帐户余额的分大于4分就偷钱

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>


/* 实现偷钱程序 若是帐户余额的分大于等于4分就不偷钱,小于等于3分就偷走 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {

    printf("请输入你的帐户余额\n");
    double balance = 0.0;
    scanf("%lf", &balance);

    // 12.34*10=123.4 123.4+0.6=124 124/10.0=12.4 12.4>12.34
    double rest = (int)((balance * 10) + 0.6) / 10.0;
    printf("rest = %f", rest);
    if (rest < balance) {
        //
        printf("能够偷钱%.2f元", balance - rest);
    }

    getchar();

    return 0;
}

2.14.2 数据类型转换的原理

当在进行数据类型转换时,若是该数据是有符号的,在进行数据类型转换时按照符号位数来填充,若是是无符号则按照0来填充。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>

/* 数据类型转换的内存原理 有符号 低字节转高字节按照符号位数填充 无符号 低字节转高字节按照0填充 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int convert_main(int argc, char* argv[]) {
    //正数按照0填充
    char ch = 1; // 二进制表示 0000 0001
    int num = 1; // 二进制表示 0000 0000 0000 0000 0000 0000 0000 0001


    //负数按照1填充
 // 二进制表示 原码 1000 0001 反码 1111 1110 补码 1111 1111 ->ff
    ch = -1;
    // 二进制表示 原码 1000 0000 0000 0000 0000 0000 0000 00001
    // 反码 1111 1111 1111 1111 1111 1111 1111 1110
    // 补码 1111 1111 1111 1111 1111 1111 1111 1111 -> ffffffff
    num = ch;


    unsigned char data = 255 + 1; // 二进制补码 1 0000 0000 可是char只能占据8位,所以这里会截取8位即0000 0000,结果位0
    printf("unsigned char data的地址是%p", &data);
    printf("data = %d", data);


    unsigned int u_num = -1; //赋值错误,能编译不意味着结果正确
                            // 1000 0000 0000 0000 0000 0000 0000 0000 0001
                            // 1111 1111 1111 1111 1111 1111 1111 1111 1110
                            // 1111 1111 1111 1111 1111 1111 1111 1111 1111 无符号解析结果为2的32次方即4294967295
    for (int i = 0; i < u_num; i++) {
        system("mspaint");
    }
    getchar();

    return 0;
}
相关文章
相关标签/搜索