从零开始学习嵌入式P2——C语言基本数据类型
从零开始学习嵌入式P2——C语言基本数据类型day02内容包括 GCC 编译流程、计算机中的数据表示、进制转换、C 语言基本数据类型以及常量和变量。今日目标学完今天的内容我们应该能够说清楚一个 C 程序从源代码到可执行文件经历了什么理解 bit、byte 以及常见容量单位完成二进制、八进制、十进制和十六进制之间的简单转换C语言数据类型有什么该怎么区分数据类型什么是常量及简单的宏定义。一、C 程序是怎样变成可执行文件的电脑CPU只能读懂汇编语言没办法直接读懂我们在vi中写下的.c文件想要让写的C语言变成能被电脑读懂就需要用到GCC。GCC 依次经过预处理、编译、汇编和链接四个阶段最终将C语言生成可执行程序。源文件(.c) - 预处理文件(.i) - 汇编文件(.s) - 目标文件(.o) - 可执行文件 预处理 编译 汇编 链接1. 预处理预处理器负责处理以#开头的预处理指令常见内容包括#include展开头文件#define进行宏替换#if、#elif、#else、#endif条件编译#ifdef、#ifndef根据宏是否定义来决定是否编译某段代码。只进行预处理的gcc指令gcc-Efilename.c-ofilename.i2. 编译编译器检查语法并将预处理后的 C 代码翻译成汇编代码。进行预处理、编译的gcc指令gcc-Sfilename.c-ofilename.s3. 汇编汇编器将汇编代码转换成机器可以识别的二进制目标代码。只进行预处理、编译、汇编的gcc指令gcc-cfilename.c-ofilename.o此时得到的.o文件通常还不能直接运行4. 链接链接器把目标文件和所需的库组合起来生成最终的可执行程序。完整的gcc编译代码gcc filename.c-ofilename运行程序./filename如果不使用-o指定文件名GCC 默认生成名为a.out的可执行文件。二、程序如何在计算机中运行C 源代码和编译完成的可执行文件平时保存在硬盘、固态硬盘等外部存储器中。执行程序时操作系统把程序所需的机器指令和数据装入内存CPU 再从内存中取出指令并执行。程序结束后操作系统会回收它占用的内存空间。可以简单理解为外存保存程序 - 操作系统装载到内存 - CPU 执行 - 回收运行空间三、计算机中的数据单位计算机底层使用是依靠电平高低代表0和1所有数据也会以二进制的形式保存。单位含义bit位一个二进制位简写为bbyte字节8 bit简写为B1 KiB1024 B1 MiB1024 KiB1 GiB1024 MiB1 TiB1024 GiB日常资料中经常把上表写成 KB、MB、GB、TB。严格来说以 1024 为进位时更准确的写法是 KiB、MiB、GiB、TiB。四、常用进制与转换1. 四种常用进制进制可使用的数字规则二进制0、1逢二进一八进制07逢八进一十进制09逢十进一十六进制09、AF逢十六进一十六进制中的AF分别表示十进制的 1015。大小写均可例如0x1a与0x1A的值相同。2. 其他进制转十进制方法是每一位乘以该位的权值再把结果相加。以二进制101101为例101101₂ 1×2⁵ 0×2⁴ 1×2³ 1×2² 0×2¹ 1×2⁰ 32 8 4 1 45₁₀十六进制2D转为十进制2D₁₆ 2×16¹ 13×16⁰ 32 13 45₁₀3. 十进制转其他进制使用“除基取余余数逆序排列”。例如把十进制 45 转为二进制45 ÷ 2 22 …… 1 22 ÷ 2 11 …… 0 11 ÷ 2 5 …… 1 5 ÷ 2 2 …… 1 2 ÷ 2 1 …… 0 1 ÷ 2 0 …… 1从下往上读取余数得到101101₂。4. 二进制与八进制、十六进制互转每 3 位二进制数对应 1 位八进制数每 4 位二进制数对应 1 位十六进制数。二进制 001 011 101 八进制 1 3 5 - 135₈ 二进制 0010 1101 十六进制 2 D - 2D₁₆在 C 语言中整数常量可以使用不同的前缀表示进制intdecimal45;// 十进制intoctal055;// 八进制前缀为 0inthexadecimal0x2D;// 十六进制前缀为 0x 或 0X这三个变量保存的数值完全相同。在 C17 及更早的标准中0b二进制字面量不是标准写法只是部分编译器提供的扩展C23 已正式支持0b和0B前缀。编写需要兼容旧工具链的嵌入式代码时应先确认编译器所支持的 C 标准。五、什么是数据类型数据类型描述一个数据对象具有什么特性包括它需要多少存储空间、可以表示哪些值以及能够参与哪些运算。C语言已经提前帮我们定义好了一些基本的数据类型。C 语言中已经定义好的基本类型主要包括整数类型short、int、long等浮点类型float、double字符类型char布尔类型bool空类型void。数组、结构体和联合体等由基本类型进一步组合而成属于构造类型后续再展开学习。六、整数类型1. 有符号与无符号整数类型通常分为两类signed可以表示负数、零和正数unsigned只能表示零和正数因此同样位数下能表示更大的正数。short、int和long不写signed时默认是有符号类型shorta;// 等价于 signed short a;intb;// 等价于 signed int b;longc;// 等价于 signed long c;对于一个占n位的二进制整数在常见的二进制补码系统中无符号范围0 2ⁿ - 1有符号范围-2ⁿ⁻¹ 2ⁿ⁻¹ - 1。注意因为有符号二进制数保存时会经历补码的原因符号位会导致存储的负数范围比正数范围多一位2. 常见平台上的大小和范围下面是常见 64 位 Linux 环境中的情况类型常见大小常见取值范围signed short2 B-32768 32767unsigned short2 B0 65535signed int4 B-21亿 21亿unsigned int4 B0 42亿signed long8 B-2⁶³ 2⁶³ - 1unsigned long8 B0 2⁶⁴ - 1需要注意C 标准没有规定这些类型在所有平台上必须占用完全相同的字节数。例如在不少 32 位嵌入式平台中long仍然是 4 字节。想要明确具体大小可以使用sizeof查看printf(short: %zu bytes\n,sizeof(short));printf(int: %zu bytes\n,sizeof(int));printf(long: %zu bytes\n,sizeof(long));七、浮点类型浮点数用来表示带小数的实数。最常用的是float和double。类型常见大小常见有效数字常见二进制组成float4 B约 68 位1 位符号位 8 位指数 23 位尾数double8 B约 1517 位1 位符号位 11 位指数 52 位尾数floate2.71f;doublepi3.141592653589793;浮点常量默认是double类型末尾加f或F后才是float类型3.14// double3.14f// float12.56e5// 12.56 × 10⁵double13.98e-10//C语言允许写成指数形式浮点数的存储空间有限许多十进制小数不能被二进制精确表示因此浮点运算结果不一定绝对精确。八、字符类型与 ASCIIchar占 1 字节主要用于保存字符或较小的整数。charletterA;charspace ;//空格也是一个字符chardigit9;计算机实际保存的仍然是数字。为了将字符与二进制代码一一对应需要用到ASCII代码。ASCII 规定了常见字符与数值之间的对应关系例如A - 65 - 01000001₂因此字符也可以参与整数运算charnextA1;// next 的值是 B字符char、signed char和unsigned char都占 1 字节但它们并不完全相同类型常见 8 位平台上的范围signed char-128 127unsigned char0 255char由编译器和目标平台决定九、布尔类型与void1. 布尔类型在C语言中包含stdbool.h后可以使用bool、true和false#includestdbool.hbool atrue;bool bfalse;在C 语言中进行逻辑判断时0表示假所有非0值表示真而非只有1能表示真。2.void类型void表示“没有具体的值”常见用途包括表示函数没有返回值表示函数不接收参数构造通用指针void *。voidled_on(void){// 不接收参数也不返回值}十、常量与变量1. 常量程序运行过程中值不发生改变的量称为常量。整数常量普通整数常量通常从int开始选择合适的类型也可以使用后缀明确表达要求100// 十进制整数123U// unsigned 类型123L// long 类型123UL// unsigned long 类型0123// 八进制0x1A// 十六进制以0开头的整数0123并不是十进制 123而是八进制数字符常量字符常量使用单引号一次表示一个字符A0 !\\//两个\才是 \字符单个\为转义字符常见转义字符如下写法含义\n换行\t水平制表符\r回车\b退格\a响铃\\反斜杠\单引号\双引号\0空字符数值为 0\065使用八进制数表示字符\x41使用十六进制数表示字符其中\x41和A表示相同的字符。字符串常量字符串常量使用双引号hello worldC语言 字符串末尾会自动添加一个\0作为结束标志。因此a由字符a和\0组成它与字符常量a不同。宏定义的标识常量使用#define可以为常量取一个有意义的名字增强代码的可读性#definePI3.1415926宏定义本质上是预处理阶段的文本替换不会自动进行类型检查。为了避免计算出错宏表达式应当加上括号#defineM((N)*(N))2. 变量程序运行过程中值可以改变的量称为变量。定义变量时需要先写类型再写变量名intcount0;floattemperature25.0f;charcommandA;countcount1;temperature26.5f;commandB;变量名应该表达其用途。与a、b、x1相比led_count、temperature、key_pressed更容易阅读和维护。十一、综合示例下面的程序把本篇出现的几种基本类型放到一起并使用sizeof查看它们在当前平台上的大小#includestdbool.h#includestdio.h#defineDEVICE_ID(0x2DU)intmain(void){unsignedintdevice_idDEVICE_ID;intsample_count45;floattemperature25.5f;doublevoltage3.300000;charstatusA;bool readytrue;printf(device_id %u\n,device_id);printf(sample_count %d\n,sample_count);printf(temperature %.1f\n,temperature);printf(voltage %.6f\n,voltage);printf(status %c (ASCII: %d)\n,status,status);printf(ready %d\n,ready);printf(sizeof(int) %zu\n,sizeof(int));printf(sizeof(float) %zu\n,sizeof(float));printf(sizeof(double) %zu\n,sizeof(double));printf(sizeof(char) %zu\n,sizeof(char));return0;}gcc编译并运行gcc basic_types.c-obasic_types //编译 ./basic_types //运行小结本篇从 C 程序的编译流程出发认识了计算机中的数据单位和进制表示随后学习了整数、浮点数、字符、布尔及void等基本类型最后区分了常量与变量。感谢观看