学嵌入式C语言,看这一篇就够了(3)

image

C语言的基本元素

我们通过写一篇文章的方式来类比写一段程序,从而认识一下C语言有哪些基本元素:

一篇文章 --> 若干段落 --> 几条语句 --> 几组词(单词 + 语句)--> 若干个字

一段程序 --> 若干函数 --> 几组语句 --> 表达式 --> 标识符 --> 字符

自然语言
单词 短语
C语言 字符 标识符 表达式 语句 函数

写一个程序相当写一篇文章,而一篇文章(程序)是由多个段落(函数)构成的,一个段落(函数)是由多个句子(语句)构成的,一个句子(语句)是由多个短语(表达式)构成的,一个短语(表达式)是由多个单词(标识符)构成的,一个单词(标识符)是由多个文字(字符)组成的

  • 常用汉字有3500左右,常用英语单词有6000左右,那么想要掌握C语言需要学习多少个关键字(keywords)?

    • C语言关键字有32个,都是小写的,英文单词的缩写。
    • int -- integer 整数; char -- character 字符; if 如果; continue 继续......

image

计算机储存器结构

数据的概念

使用计算机的目的就是为了高效的处理大量数据,但是数据的类型有很多,比如声音、图像、文字、数字等,计算机不用区分数据,计算机只采用二进制存储。

  • 计算机把还未处理的数据以及处理好的数据保存到哪里呢??

    • 这些数据都是存储在存储器,用户通过输入设备(键盘)录入的数据都是先存储在内存中,然后控制器会把这些数据传递给运算器,运算器处理完成后传递给控制器,控制器吧数据传递给输出设备(显示器)

内存的概念

对于计算机中的存储器而言,一般分为两种:RAM(随机存储器)ROM(只读存储器)

博客:ROM和RAM的区别

  1. 只读存储器
    只读存储器的英文全称是Read Only Memory,简称为ROM,属于非易失性存储设备,指的是掉电不丢失数据,可以把数据长时间存储,一般分为很多种,具体的区别可以通过《数电》进行了解,对于计算机的机械硬盘或者固态硬盘而言指的就是ROM

  2. 随机存储器
    随机存储器的英文全称是Random Access Memory,简称为RAM,属于易失性存储设备,指的是掉电会丢失数据,但是读写速度很快。所以一般CPU都是直接和RAM打交道

image

对于计算机的内存条而言,其实指的就是RAM,RAM的内存空间越大,CPU处理数据越多

image

  • 数据输入到计算机之后,计算机的CPU是如何知道数据存放在内存的位置?
    image

内存的单位

内存中一个MOS管可以存储一个二进制数0或1,为了方便记忆,标准规定把一个二进制数用bit来进行表示,bit(比特)是binary digit的缩写,中文翻译为位。专业术语一般把一个二进制数称为一位二进制数。所以bit就用来表示计算机数据的最小单位

image

博客:内存是怎么制作的?

image

  • 注意:计算机处理数据的基本单位是byte,计算机处理数据的最小单位是bit,1byte = 8bit
    image

C语言的常量变量

内存是一块连续的空间,并且内存中的每个存储单元(8bit)都有对应的编号,并且编号是固定的,对于存储单元的编号,一般称为存储单元的地址,用户可以直接通过存储单元的地址来对存储单元进行读写访问,但是一般情况下用户是不会直接通过地址来访问,因为访问效率太低。

常量和变量

C语言标准规定:用户有权利从内存中申请一块存储单元,并且用户可以对存储单元进行命名,要求用户提前说明申请的存储单元(单位以字节来计算)的大小,这样内核才会从内存中寻找一块合适的空间,并把这块空间的地址反馈给用户

image

用户可以对内核提供的存储单元进行命名,然后内核会把用户的命名和存储单元的地址建立一个映射关系,这样用户就可以通过存储单元的名称来访问存储单元

  • 既然用户可以对申请的存储单元进行命名,那请问用户是可以随意命名还是遵循规则?

    • C语言标准中规定根据数据在程序运行期间的值是否发生变化,可以存储单元分为两种:常量 or 变量

image

  1. 常量
    常量指的是在程序运行期间值不会发生改变的量,一般使用的数字,比如3.14用来表示圆周率,一般情况下,用户都是用通过宏定义的方式来实现常量的设计,宏定义其实就是简单的文本替换,在预处理阶段预处理器会把程序中的宏定义进行展开。C语言中规定宏定义使用预处理指令 #define 来进行设计

  2. 变量
    变量指的是在程序运行期间值可能发生变化的量,用户可以根据实际需要来向kernel(内核)申请一块存储单元,并对这块存储单元进行命名,然后用户可以随时修改存储单元中的数据。申请的存储单元的大小可以通过C语言中的数据类型进行指定

命名规则

变量和常量也被称为标识符,对于标识符的命名必须遵循规则:标识符是由字母、数字、下划线以及美元符号$组成,并且标识符的第一个字符只能是字母或者下划线。

image

123m(错误) m123 _m123 _123 _12m M12a_ m12a_ INT Main

  • 注意:为了防止二义性出现,C语言标准规定不可以把系统保留字,以及程序中已经存在的函数名称、以及用户已经定义出来的标识符名称作为新的标识符名称(同一个作用域内不能)。

  • C语言的标识符是区分大小写,并且作为用户而言,标识符的命名应该是有意义的!!!

驼峰命名法:secondsperyear;second_per_year;_myData

C语言的数据类型

  • 既然用户申请存储单元的时候要说明存储单元的大小,请问如何让计算机知道大小?

    • C语言中提供了一些关键字,专门用于表示数据的类型,其实就是用于表示要申请的内存空间的大小
      image
      image
  • 已知计算机只认识二进制,也就是数据都是以二进制形式存储到内存中,请问为什么还需要这些关键字来表示数据的类型?

    • 程序是给人看的,这些关键字是用于提高程序可读性的,另外这些关键字也是说明数据的宽度的(占几个字节)
  1. 字符型
    C语言标准中提供了一个关键字char,其实是character单词的缩写,表示字符的意思,操作系统char类型的数据宽度定义为1字节,用于存储字符,C语言标准中用单撇号' '表示字符
    image

    • C语言标准规定:用户打算存储需要按照规则字符 数据宽度 变量名称 ; 举例: char ch = ‘c’

    C语言标准中关于字符的种类有两种:普通字符 and 转义字符,对于ASCII码表中转义字符

    image

    注意:ASCII码中的转义字符需要使用 ‘\0’ ‘\r’ ‘\n’进行表示,代表字符具有特殊的意义。

     已知计算机是以二进制存储数据,意味着写入到存储器中的字符都会被编译器转换为而二进制指令,那请问用户能否直接以二进制指令的形式把字符输入到存储器中,编译时是否会报错??
    
     回答:会报错,因为C语言不支持二进制输入,但是C语言支持八进制、十进制、十六进制。
    
  2. 整数型
    C语言标准中使用关键字int表示整数,关键字int的英文单词是integer,对应的中文具有整数的含义,在32系统下关键字int的数据宽度是4字节,也就意味着存储单元所能存储的整数范围比较广泛
    image
    image

    C语言标准规定了用户可以采用不同的进制来表示数据,常用的进制有八进制、十进制以及十六进制,并且为了区分这三种进制,所以规定每种进制都有对应的前导符(前缀),规定八进制的前缀以0表示,比如064,规定十六进制的前缀以0x/0X表示,比如0x64。

     具体的转换规则可以看《数字电路》
    
  3. 短整型
    C语言标准中规定使用关键字short来表示短整型,一般短整型的全称是short int,只不过写程序的时候可以只写short即可,在32位系统下short短整型占2字节
    image

  4. 长整型
    C语言标准中规定使用关键字long来表示长整型,一般长整型的全称是long int,只不过写程序的时候可以只写long即可,在32位系统下long长整型占4字节,在64位系统占8字节
    image

  5. 长长整型
    C语言标准中规定使用关键字long来表示长整型,但是长长整型是使用long long来表示,在32位和64位系统下长长整型占8字节
    image

    • C语言中提供了char、int、short、long、long long来表示整型,但是整型数是分正负的,编译器如何来区分整数的正负呢?

      • C语言标准中提供了两个关键字 signed && unsigned用于修饰整数,unsigned表示无符号,signed表示有符号,C语言中的signed修饰符是隐式声明,也就是用户定义整型变量的时候如果没有特别强调,则整型变量默认是有符号的

      • 用户如果要存储无符号的整数,则必须定义变量必须显式声明变量是无符号的(unsigned)
        image

    • 计算机可以区分整数的正负,但是程序最终都会被转换为二进制指令,但是二进制是不分正负的,请问编译器是如何转换数据的???

      • 二进制可以分正负,在二进制数的前面添加1bit,作为符号位,并且bit=0,用于表示二进制数为正数,bit=1,用于表示二进制数为负数
        image
        image
    • 计算机内部是以二进制的补码方式来存储数据的,所以需要把二进制数的原码转换为补码形式
      image

    • 正数的原码和补码是相同的,所以负数才需要把原码转换为补码,再进行存储!
      image

    反码:二进制数除了符号位(二进制数中的最高位),把数据位取反即可(0变1,1变0)

    -1 --- 1 000 0001 原码 -- 计算反码 -- 1 111 1110 反码

    补码:二进制数的反码 + 1

    -1 -- 1 000 0001 原码 --计算反码 -- 1 111 1110 反码--计算补码 -- 1111 1110 +1 -- 1111 1111

    image

    • 练习:用户打算定义一个字符型变量,命名叫做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

  6. 浮点型
    数据有整数和小数之分,一般情况下处理的数据也是具有小数的,所以C语言标准中规定使用关键字float来表示单精度浮点数,单精度浮点型占4字节,另外C语言标准中也提供了另一个关键字double用来表示双精度浮点数,double占8字节,其实C语言也提供了一种类型long double,该类型占16字节

    C语言中一般表示浮点数有两种方案:十进制形式 or 指数形式,两者的具体区分如下所示

    1. 十进制形式

      十进制形式是采用整数+小数的组合表示浮点数,比如3.14 、5.0 ,基本上也是最常用的方案

    2. 指数形式

      指数形式指的是采用以10为底的形式表示浮点数,比如 3.14 * 102 ,但是在编写程序的时候采用英文半角输入法进行设计,这种输入法是不支持输入上标或者下标,所以C语言规定采用字符e或者E来表示以10为底的指数,比如3.14E2

    • 注意:C语言标准中规定字符e/E的后面必须是整数,另外字符e/E的前面必须有数字!!!!!!

    • 思考:用户定义了一个单精度浮点型变量,并把变量命名为a,此时用户不小心把一个整数5存储到了该变量中,请问变量中存储的数是什么? float a; 回答:%f 5.000000

    • 整型数据在计算机中存储的时候是以二进制补码形式进行存储,请问一个浮点型数据在计算机中是如何存储的?
      image

  7. 字符串
    字符串是表示某种含义的一个字符序列,字符串在内存是需要一块连续的内存空间进行存储,C语言中规定字符串使用双引号””表示,并且规定字符串的结束标志是’\0’,但’\0’不需要用户手动添加,系统会自动在一个字符串的末尾添加’\0’

    • 思考:既然在内存中字符串需要一块连续的空间来存储,内核肯定会返回字符串的开始地址,请问内核如何判断字符串何时结束?
      • 回答:遇到’\0’自动结束

    image

  8. 布尔型
    用户有时候需要在程序进行判断,在C89标准中,用户如果想要判断某种条件是否成立,一般是定义一个整型变量,然后利用数字0和数字1来表示条件是否成立,用户就可以把变量作为一个标志位使用。

    在C99标准可以使用布尔型来表示真假两种情况,头文件stdbool.h有相关描述,在头文件中定义了三个宏,分别是bool、true以及false

    image
    image

    思考:已经知道基本数据类型的宽度是取决于系统环境以及编译器,假设用户此时并不清楚当前使用的机器的位数(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 函数里,只需调用这个宏并传入不同的基本数据类型,就能快速输出各类型的宽度。
    
posted @   lucky-Tang  阅读(39)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程
点击右上角即可分享
微信分享提示