【C】【笔记】嵌入式C/C++语言精华文章集锦

本文深入探讨了C/C++语言中结构体的成员对齐原理、指定对齐方式的方法,以及在C语言编程中如何通过数学方法、位操作和汇编嵌入来提高效率。此外,文章还提供了成为嵌入式程序员所需了解的基本问题解答,包括预处理指令、宏函数、错误处理、死循环编写和变量类型的理解。涵盖了从基础概念到实际应用的全面指导。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文为个人读书笔记,仅供记录学习过程中遇到的日后需要留意的问题,如有相关版权问题请及时通知作者。


C/C++语言struct深层探索
 
struct的成员对齐
     
自然对界     
     对于结构体,编译器会自动进行成员变量的对齐,以提高运算效率。缺省情况下,编译器为结构体的每个成员按其自然对界(natural alignment)条件分配空间。各个成员按照它们被声明的顺序在内存中顺序存储,第一个成员的地址和整个结构的地址相同。     
     自然对界(natural alignment)即默认对齐方式,是指按结构体的成员中size 最大的成员对齐。

指定对界
     使用伪指令#pragma pack(n),编译器将按照n个字节对齐
     使用伪指令#pragma pack(),取消自定义字节对齐方式、
     *如果#pragma pack(n)中指定的n大于 结构体中最大成员的size,则其不起作用,结构体仍然按照size最大的成员进行对界
     VC++中:project→setting→C/C++菜单 struct member alignment

在C语言中,当结构体中存在指针型成员时,一定要注意在采用赋值语句时是否将两个实例中的指针型成员指向了同一片内存。


C语言高效编程   

以空间换时间     
     函数和宏函数的区别就在于,宏函数占用了大量的空间,而函数占用了时间。函数调用使用系统的栈来保存数据的,如果编译器里有栈检查选项,一般在函数的头会嵌入一些汇编语句对当前栈进行检查;同时,CPU也要在函数调用时保存和恢复当前的现场,进行压栈和弹栈操作,所以,函数调用需要一些CPU时间。而宏函数不存在这个问题。宏函数仅仅作为预先写好的代码嵌入到当前程序,不会产生函数调用,所以仅仅是占用了空间,在频繁调用同一个宏函数的时候,该现象尤其突出。

数学方法解决问题
     例如,使用等差数列求和公式来代替循环。

使用位操作
     减少除法和模除运算

汇编嵌入
     缺点在于限制了程序的可移植性。

想成为嵌入式程序员应知道的几个基本问题
     
1、用预处理指令声明一个常数,用以表示1年中有多少秒,忽略闰年
     #define SECOMDS_PER_YEAR (60*60*24*365)UL
     正确的使用括号,使用清晰的表达式计算结果而不是自己计算出来,正确的类型标识符。

2、写一个标准宏 MIN,输入两个参数并返回比较小的一个。
     #define MIN(A,B) ((A)<=(B)?(A):(B))
     三重条件操作符能够产生比if-else更优化的代码。

3、预处理器标识#error的目的是什么

4、怎么用C写死循环
     while(1)或goto
     不要for(;;)

5、用变量a给出定义
     
一个整型数
An integer
int a;

一个指向整型数的指针
A pointer to a integer
int *a;

一个指向指针的指针,指向的指针指向一个整型数
A pointer to a pointer to an integer
int **a;

一个有10个整型数的数组
An array of 10 integers
int a[10];
一个有10个指针的数组,该指针是指向一个整型数的
An array of 10 pointers to integers
int *a[10];
一个指向有10个整型数数组的指针
A pointer to an array of 10 integers
int(*)a[10];
一个指向函数的指针,该函数有一个整型参数并返回一个整型数
A pointer to a function that takes an integer as an argument and returns an integer
int (*a)(int);
一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数
An array of ten pointers to functions that take an integer argument and return an integer
int (*a[10])(int);

static的作用是什么
     1、在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。
     2、在模块内(函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其他函数访问。它是一个本地的全局变量。
     3、在模块内,一个被声明为静态的函数只可被这一模块内的其他函数调用。也就是,这个函数被限制在声明它的模块的本地范围内使用。
//这个问题昨天招聘会的时候正好被HR问到,很幸运以前看到过这段话,可惜并没有回答完整。

const的含义
     去掉类型符,const离谁近修饰谁

volatile的含义
     一个定义为volatile的变量说这变量可能会被意想不到的改变,这样编译器每次在用到这个变量时都会去重新读取,而不使用保存在寄存器例的备份。
     三个例子:并行设备的硬件寄存器,一个中断服务子程序中会访问到的非自动变量,多线程应用中被几个任务共享的变量。

     一个参数既可以是const还可以是volatile吗?
     可以。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到的改变。它是const因为程序不应该视图去修改它。

     一个指针可以是volatile吗?
     可以。服务子程序修改一个指向一个buffer的指针时

     指出下面函数的错误。
          int square(volatile int *ptr)
          {
               return *ptr * *ptr;
          }
     原本的目的是返回*ptr指向值的平方,但是,由于*ptr指向一个volatile型参数,*ptr的值可能会被意想不到地改变。
     正确的代码:
          long square(volatile int *ptr)
          {
               int a;
               a=*ptr;
               return a*a;
          }

位操作
     尽量使用bitfield

访问特定的内存位置
     为了访问一个绝对地址把一个整型数强制转换(typecast)为一指针是合法的。
     设置绝对地址0x67a9的整形变量的值为0xaa66
          int *ptr;
          ptr=(int*)0x67a9;
          *ptr=0xaa66;
     或
          *(int* const)(0x67a9)=0xaa66;

中断函数
     中断函数不能返回一个值,不能传递参数。在许多的处理器/编译器中,浮点数一般都是不可重如的。有些处理器/编译器需要让出额外的寄存器如栈,有些处理器/编译器就是不允许在ISR中做浮点运算。ISR应该是短而有效率的,应避免浮点数运算。printf经常有重入和性能上的问题。尽量不在ISR中使用。

类型转换
     当表达式中存在有符号类型和无符号类型时所有的操作数都自动转换为无符号类型。

类型定义
     typedef要比#define好。


C语言嵌入式系统编程修炼
     
不要再头文件中定义变量,只做声明。
中断程序不能返回值,不能向ISR传递参数,应该尽可能短小精悍,不能用printf

malloc与free要成对出现,而且谁申请谁释放。申请后要判断是否成功。

寄存器变量
     只有局部变量

C/C++数组名与指针区别深层探索

     数组名的内涵在于其指代实体是一种数据结构,这种数据结构就是数组;
     数组名的外延在于其可以转换为指向其指代实体的指针,而且是一个指针常量;
     指向数组的指针则是另外一种变量类型。在win32下,长度为4,仅仅意味着数组的存放地址。
     数组名作为函数形参时,在函数体内,失去了本身的内涵,仅仅是一个指针。同时,失去了常量特性,可以作自增自减等操作。










评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值