学嵌入式C语言,看这一篇就够了(3)
C语言的基本元素
我们通过写一篇文章的方式来类比写一段程序,从而认识一下C语言有哪些基本元素:
一篇文章 --> 若干段落 --> 几条语句 --> 几组词(单词 + 语句)--> 若干个字
一段程序 --> 若干函数 --> 几组语句 --> 表达式 --> 标识符 --> 字符
自然语言 | 字 | 词 | 句 | 段 | 章 |
---|---|---|---|---|---|
单词 | 短语 | ||||
C语言 | 字符 | 标识符 | 表达式 | 语句 | 函数 |
写一个程序相当写一篇文章,而一篇文章(程序)是由多个段落(函数)构成的,一个段落(函数)是由多个句子(语句)构成的,一个句子(语句)是由多个短语(表达式)构成的,一个短语(表达式)是由多个单词(标识符)构成的,一个单词(标识符)是由多个文字(字符)组成的
-
常用汉字有3500左右,常用英语单词有6000左右,那么想要掌握C语言需要学习多少个关键字(keywords)?
- C语言关键字有32个,都是小写的,英文单词的缩写。
- int -- integer 整数; char -- character 字符; if 如果; continue 继续......
计算机储存器结构
数据的概念
使用计算机的目的就是为了高效的处理大量数据,但是数据的类型有很多,比如声音、图像、文字、数字等,计算机不用区分数据,计算机只采用二进制存储。
-
计算机把还未处理的数据以及处理好的数据保存到哪里呢??
- 这些数据都是存储在存储器,用户通过输入设备(键盘)录入的数据都是先存储在内存中,然后控制器会把这些数据传递给运算器,运算器处理完成后传递给控制器,控制器吧数据传递给输出设备(显示器)
内存的概念
对于计算机中的存储器而言,一般分为两种:RAM(随机存储器)和 ROM(只读存储器)
-
只读存储器
只读存储器的英文全称是Read Only Memory,简称为ROM,属于非易失性存储设备,指的是掉电不丢失数据,可以把数据长时间存储,一般分为很多种,具体的区别可以通过《数电》进行了解,对于计算机的机械硬盘或者固态硬盘而言指的就是ROM -
随机存储器
随机存储器的英文全称是Random Access Memory,简称为RAM,属于易失性存储设备,指的是掉电会丢失数据,但是读写速度很快。所以一般CPU都是直接和RAM打交道
对于计算机的内存条而言,其实指的就是RAM,RAM的内存空间越大,CPU处理数据越多
- 数据输入到计算机之后,计算机的CPU是如何知道数据存放在内存的位置?
内存的单位
内存中一个MOS管可以存储一个二进制数0或1,为了方便记忆,标准规定把一个二进制数用bit来进行表示,bit(比特)是binary digit的缩写,中文翻译为位。专业术语一般把一个二进制数称为一位二进制数。所以bit就用来表示计算机数据的最小单位
- 注意:计算机处理数据的基本单位是byte,计算机处理数据的最小单位是bit,1byte = 8bit
C语言的常量变量
内存是一块连续的空间,并且内存中的每个存储单元(8bit)都有对应的编号,并且编号是固定的,对于存储单元的编号,一般称为存储单元的地址,用户可以直接通过存储单元的地址来对存储单元进行读写访问,但是一般情况下用户是不会直接通过地址来访问,因为访问效率太低。
常量和变量
C语言标准规定:用户有权利从内存中申请一块存储单元,并且用户可以对存储单元进行命名,要求用户提前说明申请的存储单元(单位以字节来计算)的大小,这样内核才会从内存中寻找一块合适的空间,并把这块空间的地址反馈给用户
用户可以对内核提供的存储单元进行命名,然后内核会把用户的命名和存储单元的地址建立一个映射关系,这样用户就可以通过存储单元的名称来访问存储单元
-
既然用户可以对申请的存储单元进行命名,那请问用户是可以随意命名还是遵循规则?
- C语言标准中规定根据数据在程序运行期间的值是否发生变化,可以存储单元分为两种:常量 or 变量
-
常量
常量指的是在程序运行期间值不会发生改变的量,一般使用的数字,比如3.14用来表示圆周率,一般情况下,用户都是用通过宏定义的方式来实现常量的设计,宏定义其实就是简单的文本替换,在预处理阶段预处理器会把程序中的宏定义进行展开。C语言中规定宏定义使用预处理指令 #define 来进行设计 -
变量
变量指的是在程序运行期间值可能发生变化的量,用户可以根据实际需要来向kernel(内核)申请一块存储单元,并对这块存储单元进行命名,然后用户可以随时修改存储单元中的数据。申请的存储单元的大小可以通过C语言中的数据类型进行指定
命名规则
变量和常量也被称为标识符,对于标识符的命名必须遵循规则:标识符是由字母、数字、下划线以及美元符号$组成,并且标识符的第一个字符只能是字母或者下划线。
123m(错误) m123 _m123 _123 _12m M12a_ m12a_ INT Main
-
注意:为了防止二义性出现,C语言标准规定不可以把系统保留字,以及程序中已经存在的函数名称、以及用户已经定义出来的标识符名称作为新的标识符名称(同一个作用域内不能)。
-
C语言的标识符是区分大小写,并且作为用户而言,标识符的命名应该是有意义的!!!
驼峰命名法:secondsperyear;second_per_year;_myData
C语言的数据类型
-
既然用户申请存储单元的时候要说明存储单元的大小,请问如何让计算机知道大小?
- C语言中提供了一些关键字,专门用于表示数据的类型,其实就是用于表示要申请的内存空间的大小
- C语言中提供了一些关键字,专门用于表示数据的类型,其实就是用于表示要申请的内存空间的大小
-
已知计算机只认识二进制,也就是数据都是以二进制形式存储到内存中,请问为什么还需要这些关键字来表示数据的类型?
- 程序是给人看的,这些关键字是用于提高程序可读性的,另外这些关键字也是说明数据的宽度的(占几个字节)
-
字符型
C语言标准中提供了一个关键字char,其实是character单词的缩写,表示字符的意思,操作系统char类型的数据宽度定义为1字节,用于存储字符,C语言标准中用单撇号' '表示字符
- C语言标准规定:用户打算存储需要按照规则字符 数据宽度 变量名称 ; 举例: char ch = ‘c’
C语言标准中关于字符的种类有两种:普通字符 and 转义字符,对于ASCII码表中转义字符
注意:ASCII码中的转义字符需要使用 ‘\0’ ‘\r’ ‘\n’进行表示,代表字符具有特殊的意义。
已知计算机是以二进制存储数据,意味着写入到存储器中的字符都会被编译器转换为而二进制指令,那请问用户能否直接以二进制指令的形式把字符输入到存储器中,编译时是否会报错?? 回答:会报错,因为C语言不支持二进制输入,但是C语言支持八进制、十进制、十六进制。
-
整数型
C语言标准中使用关键字int表示整数,关键字int的英文单词是integer,对应的中文具有整数的含义,在32系统下关键字int的数据宽度是4字节,也就意味着存储单元所能存储的整数范围比较广泛
C语言标准规定了用户可以采用不同的进制来表示数据,常用的进制有八进制、十进制以及十六进制,并且为了区分这三种进制,所以规定每种进制都有对应的前导符(前缀),规定八进制的前缀以0表示,比如064,规定十六进制的前缀以0x/0X表示,比如0x64。
具体的转换规则可以看《数字电路》
-
短整型
C语言标准中规定使用关键字short来表示短整型,一般短整型的全称是short int,只不过写程序的时候可以只写short即可,在32位系统下short短整型占2字节
-
长整型
C语言标准中规定使用关键字long来表示长整型,一般长整型的全称是long int,只不过写程序的时候可以只写long即可,在32位系统下long长整型占4字节,在64位系统占8字节
-
长长整型
C语言标准中规定使用关键字long来表示长整型,但是长长整型是使用long long来表示,在32位和64位系统下长长整型占8字节
-
C语言中提供了char、int、short、long、long long来表示整型,但是整型数是分正负的,编译器如何来区分整数的正负呢?
-
C语言标准中提供了两个关键字 signed && unsigned用于修饰整数,unsigned表示无符号,signed表示有符号,C语言中的signed修饰符是隐式声明,也就是用户定义整型变量的时候如果没有特别强调,则整型变量默认是有符号的
-
用户如果要存储无符号的整数,则必须定义变量必须显式声明变量是无符号的(unsigned)
-
-
计算机可以区分整数的正负,但是程序最终都会被转换为二进制指令,但是二进制是不分正负的,请问编译器是如何转换数据的???
- 二进制可以分正负,在二进制数的前面添加1bit,作为符号位,并且bit=0,用于表示二进制数为正数,bit=1,用于表示二进制数为负数
- 二进制可以分正负,在二进制数的前面添加1bit,作为符号位,并且bit=0,用于表示二进制数为正数,bit=1,用于表示二进制数为负数
-
计算机内部是以二进制的补码方式来存储数据的,所以需要把二进制数的原码转换为补码形式
-
正数的原码和补码是相同的,所以负数才需要把原码转换为补码,再进行存储!
反码:二进制数除了符号位(二进制数中的最高位),把数据位取反即可(0变1,1变0)
-1 --- 1 000 0001 原码 -- 计算反码 -- 1 111 1110 反码
补码:二进制数的反码 + 1
-1 -- 1 000 0001 原码 --计算反码 -- 1 111 1110 反码--计算补码 -- 1111 1110 +1 -- 1111 1111
-
练习:用户打算定义一个字符型变量,命名叫做data,准备把一个正整数存储到变量data中,并设计了一段程序,请问这段程序输出的结果是多少?
- char data; data = 128; printf(“%d\n”,data); --> 请问输出的结果是多少?? 答案:-127
128--0000 0000 0000 0000 0000 0000 1000 0000原码--正数的原码和补码是相同的 1000 0000
1000 0000补码 -- 属于数据类型中非常特殊的存在,可以计算是 -0,但是0不分正负,所以 把-0的位置用-128进行替换。
- 注意:设计程序时,定义的变量如果是有符号的,则尽量避免写入超过变量范围的数值!!!!!!
字符型: char 1字节 -- 有符号 -- 数值范围 -128 ~ 127 -- 无符号 -- 数值范围 0 ~ 255
短整型:short 2字节 -- 有符号 -- 数值范围 -32768 ~ 32767 无符号 --数值范围 0 ~ 65535 -
-
浮点型
数据有整数和小数之分,一般情况下处理的数据也是具有小数的,所以C语言标准中规定使用关键字float来表示单精度浮点数,单精度浮点型占4字节,另外C语言标准中也提供了另一个关键字double用来表示双精度浮点数,double占8字节,其实C语言也提供了一种类型long double,该类型占16字节C语言中一般表示浮点数有两种方案:十进制形式 or 指数形式,两者的具体区分如下所示
-
十进制形式
十进制形式是采用整数+小数的组合表示浮点数,比如3.14 、5.0 ,基本上也是最常用的方案
-
指数形式
指数形式指的是采用以10为底的形式表示浮点数,比如 3.14 * 102 ,但是在编写程序的时候采用英文半角输入法进行设计,这种输入法是不支持输入上标或者下标,所以C语言规定采用字符e或者E来表示以10为底的指数,比如3.14E2
-
注意:C语言标准中规定字符e/E的后面必须是整数,另外字符e/E的前面必须有数字!!!!!!
-
思考:用户定义了一个单精度浮点型变量,并把变量命名为a,此时用户不小心把一个整数5存储到了该变量中,请问变量中存储的数是什么? float a; 回答:%f 5.000000
-
整型数据在计算机中存储的时候是以二进制补码形式进行存储,请问一个浮点型数据在计算机中是如何存储的?
-
-
字符串
字符串是表示某种含义的一个字符序列,字符串在内存是需要一块连续的内存空间进行存储,C语言中规定字符串使用双引号””表示,并且规定字符串的结束标志是’\0’,但’\0’不需要用户手动添加,系统会自动在一个字符串的末尾添加’\0’- 思考:既然在内存中字符串需要一块连续的空间来存储,内核肯定会返回字符串的开始地址,请问内核如何判断字符串何时结束?
- 回答:遇到’\0’自动结束
- 思考:既然在内存中字符串需要一块连续的空间来存储,内核肯定会返回字符串的开始地址,请问内核如何判断字符串何时结束?
-
布尔型
用户有时候需要在程序进行判断,在C89标准中,用户如果想要判断某种条件是否成立,一般是定义一个整型变量,然后利用数字0和数字1来表示条件是否成立,用户就可以把变量作为一个标志位使用。在C99标准可以使用布尔型来表示真假两种情况,头文件stdbool.h有相关描述,在头文件中定义了三个宏,分别是bool、true以及false
思考:已经知道基本数据类型的宽度是取决于系统环境以及编译器,假设用户此时并不清楚当前使用的机器的位数(32bit/64bit)和编译器类型,那应该如何知道基本数据类型的宽度?
回答:
在 C 语言里,若想更简单快捷地知晓基本数据类型的宽度,其实主要还是依赖 sizeof 运算符
编写宏定义简化代码
使用宏定义能够避免重复编写 printf 语句,让代码更简洁,示例如下:
#include <stdio.h> // 定义一个宏,用于输出指定类型的大小 #define PRINT_TYPE_SIZE(type) printf("Size of " #type ": %zu bytes\n", sizeof(type)) int main() { // 调用宏输出各种基本类型的大小 PRINT_TYPE_SIZE(char); PRINT_TYPE_SIZE(short); PRINT_TYPE_SIZE(int); PRINT_TYPE_SIZE(long); PRINT_TYPE_SIZE(long long); PRINT_TYPE_SIZE(float); PRINT_TYPE_SIZE(double); PRINT_TYPE_SIZE(long double); return 0; }
代码解释: - PRINT_TYPE_SIZE 宏借助 # 运算符把类型名转换为字符串,再使用 sizeof 运算符获取该类型的大小,最后用 printf 输出结果。 - 在 main 函数里,只需调用这个宏并传入不同的基本数据类型,就能快速输出各类型的宽度。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程