嵌入式Day3

嵌入式day3

一、在VS的MSVC平台下调用scanf函数的细节问题

1. 安全函数警告

  • MSVC默认开启安全函数警告,使用scanf会提示改用安全版本scanf_s
  • scanf_s是MSVC的“方言”,不属于C语言标准库,不建议使用

2. 解决方案

  • 在源文件开头第一行添加宏定义以禁用警告:
#define _CRT_SECURE_NO_WARNINGS

注意:必须放在代码真正意义上的第一行,不可后置。

二、基础数据类型

C语言基本数据类型分为两大类:整型浮点型

(一)整型

1. 传统整型(4类)
  • 类型shortintlonglong long
  • 大小规定
    • C标准未明确固定长度,仅规定最小长度和大小关系:
      • shortint ≥ 2字节,long ≥ 4字节,long long ≥ 8字节;
      • 大小关系:short ≤ int ≤ long ≤ long long
    • 具体长度由编译器和平台决定,属于实现定义行为(非未定义行为,平台需明确文档说明)。
    • 特别注意int在普通机器上通常为4字节,嵌入式设备中可能为2字节。
2. 有符号与无符号整型
  • 分类
    • 有符号整型:默认类型(如shortintlonglong long),取值范围含负数,如4字节signed int[-2³¹, 2³¹ - 1]
    • 无符号整型:需显式加unsigned关键字(如unsigned int),取值范围为非负数,如4字节unsigned int[0, 2³² - 1]
  • 默认规则shortintlonglong long直接使用时默认有符号,C标准明确规定,不可改变。
3. char类型
  • 固定长度:C标准规定char必须为1字节,区别于其他整型。
  • 符号性
    • 标准未规定默认符号性,部分平台为有符号(如MSVC,多数现代平台),部分为无符号。
    • 建议显式声明符号性:signed char(有符号)、unsigned char(无符号)。
  • 用途
    • 表示字符(对应ASCII码表),存储范围有限;
    • 表示1字节小整数,符号性需明确。
4. 整型变量取值范围
  • 有符号数(1字节)[-128, 127]
  • 无符号数(1字节)[0, 255]
  • 存储特性:取值范围为环形(溢出时循环),如:
    • 最大值 + 1 = 最小值(如127 + 1 = -128);
    • 最小值 - 1 = 最大值(如-128 - 1 = 127)。

(二)浮点型

  • 类型floatdoublelong double
  • 存储标准:遵循IEEE 754标准(二进制科学计数法)。
  • 特点
    1. 范围大:同4字节空间,float表示范围远大于int
    2. 精度有限:仅保证有限有效数字内的准确性,本质不精确,适用于对精度要求不高的场景(如科学计算、工程计算)。
  • 示例
float a = 0.1f;
double b = 0.1;
printf("%d\n", a == b); // 输出0(false,精度不同)

三、无符号整数使用建议

  1. 混合运算风险:有符号与无符号数混合运算时,C会自动将有符号数转换为无符号数,可能引发逻辑错误(如负数转为极大正数)。
  2. 非必要不使用:同等长度下,有无符号数取值范围不同,转换易导致bug。
  3. 避免混用:必须使用时,避免与有符号数混合,防止自动类型转换引发问题。
  4. 适用场景
    • 非负数场景:数组长度、字符串长度、数据结构容量、内存大小、内存地址值(地址值必为非负数,适合无符号数)。
  • 示例
unsigned num = 10;
while (num--) { /* 循环体 */ } // 结束时num为0,后缀--执行后变为UINT_MAX(32位无符号最大值)

四、关键总结

  • 整型默认符号性shortintlonglong long默认有符号,char符号性依平台而定,建议显式声明。
  • 浮点数特性:范围大但精度有限,适用于非精确计算场景。
  • 无符号数原则:仅在明确非负数场景使用,避免与有符号数混用。

五、类型别名(重点)

1. 语法与定义

  • 关键字typedef,用于为已存在的类型定义别名,使同一类型拥有两个名称。
  • 语法格式
typedef 现有类型名 别名;
  • 示例
typedef int Integer;  // 为int定义别名Integer
Integer a = 10;      // 等价于int a = 10;

2. 使用优点

优点说明与示例
提升代码可读性别名可更清晰描述类型功能,例如链表结点结构体Node在链式栈中可起别名StackFrame(栈帧),明确其功能定位。
提升扩展性与维护性通过别名统一管理数据类型,便于修改。例如定义ElementType作为链表元素类型,需存储inttypedef int ElementType;,需存储double时仅修改别名定义即可,无需修改所有使用处。
增强跨平台性应对不同平台数据类型长度差异。例如定义BigInteger表示大整数,在int为4字节的平台用typedef int BigInteger;,在int为2字节的平台用typedef long BigInteger;long最小4字节,符合跨平台需求)。

3. 使用建议

  • 跨平台场景首选:若代码需在不同平台(如PC与嵌入式设备)运行,用别名替代直接使用基本类型(如int),避免依赖平台特定长度。
  • 工程实践常用:在嵌入式开发、Linux编程中,广泛使用类型别名替代基本整型(如size_t替代unsigned int)。

4. C标准库提供的类型别名

类型别名说明头文件示例与用法
size_t无符号整数,长度与平台位数相关(32位平台为32位,64位平台为64位),用于表示内存大小、数组索引等非负数场景。隐含(部分需<stddef.h>打印时用%zu(十进制)、%zo(八进制)、%zx(十六进制):
size_t num = 100; printf("%zu", num);
int8_t/uint8_t精确位数的有符号/无符号整数(8位、16位、32位等),平台无关。<stdint.h>uint16_t port = 8080;(明确16位无符号整数)
ssize_t有符号版本的size_t,长度与平台位数相关,类Unix平台(如Linux)常用。<stddef.h>用于可能返回负数的数组操作(如strlen返回size_tread系统调用返回ssize_t)。

5. 底层原理(以size_t为例)

  • 通过预处理指令适配平台:
#ifdef WIN64
    typedef unsigned __int64 size_t;  // 64位Windows
#else
    typedef unsigned int size_t;     // 32位平台
#endif

六、sizeof运算符(重点)

1. 定义与功能

  • 作用:计算类型或变量所占内存空间的字节数,结果为无符号整数size_t类型),值恒为非负数(非零)。
  • 语法形式
sizeof(类型名)  // 如sizeof(int)
sizeof 变量名   // 如sizeof(arr)

2. 经典应用场景:求数组长度

  • 公式
数组长度 = sizeof(数组名) / sizeof(数组元素类型)
  • 代码示例
int arr[] = {1, 2, 3, 4};
int len = sizeof(arr) / sizeof(arr[0]);  // len = 4
  • 原理
    1. sizeof(arr):获取整个数组的字节大小(如int数组占4 * 4 = 16字节)。
    2. sizeof(arr[0]):获取单个元素的字节大小(int为4字节)。
    3. 相除得到元素个数(16 / 4 = 4)。

3. 注意事项

  • 数组与指针区别
    • 数组名在sizeof中表示整个数组,而非指针(仅在作为函数参数时退化为指针)。
    • 禁止在函数中传递数组参数:未掌握指针前,避免将数组作为参数传递,防止sizeof失效(此时数组退化为指针,长度计算错误)。
  • 空数组禁止:C语言不允许空数组,arr[0]必然存在,确保sizeof(arr[0])合法。
  • 替代固定值:需使用类型长度时,优先用sizeof而非硬编码(如#define ARR_SIZE(arr) (sizeof(arr)/sizeof(arr[0])))。

七、关键总结

  • 类型别名核心价值:通过typedef提高可读性、扩展性和跨平台性,标准库提供的_t后缀别名(如size_t)是工程实践的首选。
  • sizeof核心用途:动态计算类型/变量字节大小,尤其用于求数组长度(公式sizeof(arr)/sizeof(arr[0])),避免硬编码和平台依赖。
  • 最佳实践
    1. 跨平台代码中用int32_t/uint8_t等精确位数别名,而非直接使用int/unsigned char
    2. 永远用sizeof计算数组长度,禁止手动编写固定数值。

八、表达式的主要作用和副作用(重点)

  1. 主要作用:表达式最重要的特点是一定会计算出一个结果,这个结果就是表达式的主要作用。例如,1 + 1的主要作用是结果2,变量a作为表达式,其主要作用是a的取值。当小表达式出现在大表达式中时,小表达式在大表达式中起到的作用就是主要作用,分析大表达式时,关注每个小表达式的主要作用即可。
  2. 副作用:除计算结果外,表达式的其他功能都是副作用。表达式不一定有副作用,常见副作用有:
    • 赋值(最常见):所有赋值运算符(=+=-=等)组成的表达式都有此副作用。如int a; a = 10;,该表达式主要作用是结果10,副作用是将变量a的值设置为10
    • IO操作(也很常见):例如printf函数调用表达式,主要作用是函数返回值,副作用是将内存中的数据格式化输出到屏幕上。
  3. 总结:一个表达式的主要作用和副作用(如果有)同时存在且相互独立。例如,a + b主要作用是计算出的a + b的结果,无副作用;a <<= 1主要作用是计算出的a左移1位的结果,副作用是将a的值修改为左移1位后的值。在while((ch = getchar())!='\n')循环中,getchar()主要作用是返回读取的字符,副作用是读键盘输入IO操作;ch = getchar()主要作用是返回读取的字符,副作用是将读到的字符赋值给ch(ch = getchar())!='\n'主要作用是返回布尔值判断是否为换行符,由!=组成的大表达式自身无副作用,但组成它的小表达式有副作用,该循环在读到换行符时结束。

九、运算符的优先级和结合性(重点)

  1. 优先级:当表达式中有多个运算符时,优先级决定运算符的计算顺序。例如,a + b * c中,乘法优先级高,先算乘法,再算加法。一些重要规律需记住:
    • 一元运算符的优先级一定高于二元运算符。
    • 在所有二元运算符中,算术运算符的优先级更高。
    • 赋值运算符的优先级往往最低,只有逗号运算符的优先级比它还低(逗号运算符很少用)。
    • 所有运算符中()优先级最高。当不清楚运算符优先级时,可在需要先计算的部分加(),增强代码可读性。
  2. 结合性:结合性决定多个相同优先级的运算符组成表达式时的运算顺序,分为左结合和右结合。左结合性意味着相同优先级的运算符从左到右计算,右结合性则从右到左计算。例如,a + b - c中,加减运算优先级一致,是左结合的,从左往右计算;int a, b, c; a = b = c = 0;中,=赋值号是右结合性的,从右往左计算,等价于a = (b = (c = 0));
  3. 示例
    • 优先级示例:求数组区间[a, b]的中间索引,简单做法(a + b)/2int可能是2个字节的情况下易出问题,C语言中惯用法是(b - a)/2 + a((b - a) >> 1) + a,最常见写法是(b - a >> 1) + a,因为+-高于>>,里面的()多余,外面的()不能去掉,否则会先算+。此需求在数组算法、数据结构(如归并排序、二分查找)中常见。
    • 结合性示例:对于结构体Studentage成员,s1是结构体对象,s1.age++等价于(s1.age)++.和后缀++优先级相同,左结合,从左往右计算;若p是指向s1的指针,p->age++等价于(p->age)++->和后缀++优先级相同,左结合,从左往右计算。

十、自增自减运算符

  1. 两种形式:自增自减运算符分为前缀形式和后缀形式,后缀形式运算优先级更高。
    • 前缀自增自减:先进行自增自减,然后返回自增自减的结果。如--a,主要作用是返回a自减1后的结果,副作用是a的值减少1。
    • 后缀自增自减:直接返回自增自减前的结果,再进行自增自减。如a++,主要作用是直接返回a,副作用是a的值增加1 。
  2. 指针操作应用:自增自减符号在指针操作中很有用。例如,指针p指向数组首元素,对于表达式*p++,后缀++优先级更高,等价于*(p++)p++主要作用是返回指针p原本的值,副作用是让p指针指向数组的下一个元素;*(p++)主要作用是返回当前指针p指向的元素的取值,p++的副作用仍然会生效,利用*p++及循环可实现指针遍历数组。
  3. 使用建议(重要)
    • for循环中使用自增自减符号是最常见、清晰、稳定准确不出错的用法。
    • 自增自减运算符尽量不要用于连接表达式,尽量单独成行,避免因主要作用不同产生歧义。
    • 如果一定要将自增自减符号写在表达式中,严禁被自增自减的变量在同一个表达式中出现多次,否则会导致未定义行为,如a+++--a;在C语言中是错误的。

十一、取余运算符的特点

  1. 操作数要求:取余/取模运算符要求操作数必须是整数,不能是浮点数。
  2. 结果符号性:取余的结果除了0(整除)外,其余结果的符号性总和被除数保持一致。例如,-100 % 3 = -1100 % 3 = 1

十二、短路逻辑运算符

C语言的逻辑运算符“或”(||)、“与”(&&)都是短路设计的:

  1. &&(逻辑与):如果第一个条件为假,后面的就不再执行了,结果一定是假。
  2. ||(逻辑或):如果第一个条件为真,后面的就不再执行了,结果一定是真。
    这种短路设计符合现实场景,语义清晰,可避免不必要的运算,提高效率,还能提升程序的安全性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值