嵌入式C_C++语言精华——笔记

Break和continue语句都可以用在循环中,用来跳出循环(结束循环);break语句还可以用在switch语句中,用来跳出switch语句。

Break语句通常用在循环语句和开关语句中。当break用于开关语句switch时,可使程序跳出switch而执行switch以后的语句;如果没有break语句,则将成为一个死循环而无法退出。

当break语句用于do-while、for、while循环语句时,可使程序终止循环而执行循环后面的语句,通常break语句总是与if语句连在一起,即满足条件便跳出循环。

struct的成员对齐。

         Continue语句的作用是跳过循环体中剩余的语句而强行执行下一次循环。Continue语句只用在for、while、do-while等循环体中,常与if语句使用,用来加速循环。

struct 是一种复合数据类型,其构成元素既可以是基本数据类型(如 int、long、float 等)的变量,也可以是一些复合数据类型(如 array、struct、union 等)的数据单元。对于结构体,编译器会自动进行成员变量的对齐,以提高运算效率。缺省情况下,编译器为结构体的每个成员按其自然对界(natural alignment)条件分配空间。各个成员按照它们被声明的顺序在内存中顺序存储,第一个成员的地址和整个结构的地址相同。

  1. 自然对界,自然对界(natural alignment)即默认对齐方式,是指按结构体的成员中 size 最大的成员对齐。如

struct natural{

           int a;

           char b;

           short c;

};        其sizeof(natural)的值就为12.

  1. 指定对界,一般地,可以通过下面的方法来改变缺省的对界条件:

· 使用伪指令#pragma pack (n),编译器将按照 n 个字节对齐;

· 使用伪指令#pragma pack (),取消自定义字节对齐方式。

注意: 如果#pragma pack (n)中指定的 n 大于结构体中最大成员的 size,则其不起作用,结构体仍然按照 size 最大的成员进行对界。如

#pragma pack(8)

struct example{

         short a;

         long b;

};       其中sizeof(example)的值仍为8;

C和C++间struct的深层区别

C++语言中 struct 具有了“类” 的功能,其与关键字 class 的区别在于 struct 中成员变量

和函数的默认访问权限为public,而 class 的为 private。

struct 可以在定义的时候直接以{ }对其成员变量赋初值,而 class 则不能。

struct注意事项

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

在 C++语言中,当结构体中存在指针型成员时,我们需要重写 struct 的拷贝构造函数并进行“=”操作符重载。

C++中 extern "C"含义深层探索

C++语言的创建初衷是“a better C”,但是这并不意味着 C++中类似 C 语言的全局变量和函数所采用的编译和连接方式与 C 语言完全相同。作为一种欲与 C 兼容的语言,C++保留了一部分过程式语言的特点(被世人称为“不彻底地面向对象”),因而它可以定义不属于任何类的全局变量和函数。

         但是,C++毕竟是一种面向对象的程序设计语言,为了支持函数的重载,C++对全局函数的处理方式与 C有明显的不同。

用变量a给出如下定义:

  1. 一个整型数(An integer)

int a;

  1. 一个指向整型数的指针(A pointer to an integer)

int *a;

  1. 一个指向指针的的指针,它指向的指针是指向一个整型数(A pointer to a pointer to an intege)

int **a; // A pointer to a pointer to an integer

  1. 一个有 10 个整型数的数组(An array of 10 integers)

int a[10];

  1. 一个有 10 个指针的数组,该指针是指向一个整型数的。(An array of 10 pointers to integers)

int *a[10];

  1. 一个指向有 10 个整型数数组的指针(A pointer to an array of 10 integers)

int (*a)[10];

  1. 一个指向函数的指针,该函数有一个整型参数并返回一个整型数(A pointer to a function that takes an integer as an argumentand returns an integer)

int (*a)(int);

  1. 一个有 10 个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数(An array of ten pointers to functions that take an integer argument and return an integer )

int (*a[10])(int);

const的用法

  1. const int a;        //a 是一个常整型数。
  2. int const a;        //a 是一个常整型数。
  3. const int *a;      //a 是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以)。
  4. int * const a;     //a 是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)。
  5. int const * a const;             //a 是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的)

为什么要用const关键字

  1. 关键字 const 的作用是为给读你代码的人传达非常有用的信息,实际上, 声明一个参数为常量是为了告诉了用户这个参数的应用目的。如果你曾花很多时间清理其它人留下的垃圾,你就会很快学会感谢这点多余的信息。(当然,懂得用 const 的程序员很少会留下的垃圾让别人来清理的。)
  2. 通过给优化器一些附加的信息,使用关键字 const 也许能产生更紧凑的代码。
  3. 合理地使用关键字 const 可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改。简而言之,这样可以减少 bug的出现。

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

处理器字长,16位机、32位机、64位机,有什么区别?

动态内存分配(Dynamic memory allocation)

嵌入式系统中,动态分配内存可能发生的问题是什么?

C语言模块化程序设计

  1. 模块即是一个.c 文件和一个.h 文件的结合,头文件(.h)中是对于该模块接口的声明;
  2. 某模块提供给其它模块调用的外部函数及数据需在.h 中文件中冠以 extern 关键字声明;
  3. 模块内的函数和全局变量需在.c 文件开头冠以 static 关键字声明;
  4. 永远不要在.h 文件中定义变量!定义变量和声明变量的区别在于定义会产生内存分配的操作,是汇编阶段的概念;而声明则只是告诉包含该声明的模块在连接阶段从其它模块寻找外部函数和变量。

 

多任务还是单任务?

所谓"单任务系统"是指该系统不能支持多任务并发操作,宏观串行地执行一个任务。而多任务系统则可以宏观并行(微观上可能串行)地"同时"执行多个任务。

几个著名的死循环

  1. 操作系统是死循环;
  2. WIN32 程序是死循环;
  3. 嵌入式系统软件是死循环;
  4. 多线程程序的线程处理函数是死循环。
  5. 这个世界从来不需要一个处理完几个消息就喊着要 OS 杀死它的WIN32 程序,不需要一个刚开始 RUN 就自行了断的嵌入式系统,不需要莫名其妙启动一个做一点事就干掉自己的线程。

中断服务程序(ISR)

  1. 不能返回值;
  2. 不能向 ISR 传递参数;
  3. ISR 应该尽可能的短小精悍;
  4. printf(char * lpFormatString,…)函数会带来重入和性能问题,不能在 ISR 中采用。

内存操作

数据指针

在嵌入式系统的编程中,常常要求在特定的内存单元读写内容,汇编有对应的 MOV 指令,而除 C/C++以外的其它编程语言基本没有直接访问绝对地址的能力。在嵌入式系统的实际调试中,多借助 C 语言指针所具有的对绝对地址单元内容的读写能力。 以指针直接操作内存多发生在如下几种情况:

  1. 某 I/O 芯片被定位在 CPU 的存储空间而非 I/O 空间,而且寄存器对应于某特定地址;
  2. 两个 CPU 之间以双端口 RAM 通信,CPU 需要在双端口 RAM 的特定单元(称为 mail box)书写内容以在对方 CPU 产生中断。
  3. 读取在 ROM 或 FLASH 的特定单元所烧录的汉字和英文字模。

若 p 指向 int,即:int *p = (int *)0xF000FF00;

p++(或++p)的结果等同于:p = p+sizeof(int),而 p-(或-p)的结果是 p = p-sizeof(int)。

long int *p = (long int *)0xF000FF00;

则 p++(或++p)的结果等同于:p = p+sizeof(long int) ,而 p-(或-p)的结果是 p = p-sizeof(long int)。

记住: CPU 以字节为单位编址,而 C 语言指针以指向的数据类型长度作自增和自减。

函数指针

  1. C 语言中函数名直接对应于函数生成的指令代码在内存中的地址,因此函数名可以直接赋给指向函数的指针;
  2. 调用函数实际上等同于"调转指令+参数传递处理+回归位置入栈",本质上最核心的操作是将函数生成的目标代码的首地址赋给 CPU 的 PC 寄存器;
  3. 因为函数调用的本质是跳转到某一个地址单元的 code 去执行,所以可以"调用"一个根本就不存在的函数实体,

函数无它,唯指令集合耳;你可以调用一个没有函数体的函数,本质上只是换一个地址开始执行指令!

数组VS动态申请

  1. malloc和free一定要成对出现。
  2. 尽可能的选用数组,数组不能越界访问(真理越过一步就是谬误,数组越过界限就光荣地成全了一个混乱的嵌入式系统);
  3. 如果使用动态申请,则申请后一定要判断是否申请成功了,并且 malloc 和 free 应成对出现!

系统时间显示,在QT中,可以使用定时器结合C语言函数,在标签上显示,也可以在时间显示函数中以静态变量分别存储小时、分钟、秒,只有在其内容发生变化的时候才更新其显示。

动画显示动画是无所谓有,无所谓无的,静止的画面走的路多了,也就成了动画。随着时间的变更,在屏幕上显示不同的静止画面,即是动画之本质。所以,在一个嵌入式系统的 LCD 上欲显示动画,必须借助定时器。没有硬件或软件定时器的世界是无法想像的:

菜单操作,面向对象!!!

C/C++语言 void 及 void 指针深层探索

void真正发挥的作用在于:

  1. 对函数返回的限定;
  2. 对函数参数的限定。

众所周知,如果指针 p1 和 p2 的类型相同,那么我们可以直接在 p1 和 p2 间互相赋值;如果 p1 和 p2 指向不同的数据类型,则必须使用强制类型转换运算符把赋值运算符右边的指针类型转换为左边指针的类型。而 void *则不同,任何类型的指针都可以直接赋值给它,无需进行强制类型转换:但这并不意味着,void *也可以无需强制类型转换地赋给其它类型的指针。因为“无类型”可以包容“有类型”,而“有类型”则不能包容“无类型”

void的使用规则

  1. 如果函数没有返回值,那么应声明为 void 类型
  2. 如果函数无参数,那么应声明其参数为 void
  3. 小心使用 void 指针类型,按照 ANSI(American National Standards Institute)标准,不能对 void 指针进行算法操作。
  4. 如果函数的参数可以是任意类型指针,那么应声明其参数为 void *
  5. void 不能代表一个真实的变量,如void a 是错的。

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

  1. 数组名的内涵在于其指代实体是一种数据结构,这种数据结构就是数组;
  2. 数组名的外延在于其可以转换为指向其指代实体的指针,而且是一个指针常量;
  3. 指向数组的指针则是另外一种变量类型(在 WIN32 平台下,长度为 4),仅仅意味着数组的存放地址!
  4. 数组名作为函数形参时,在函数体内,其失去了本身的内涵,仅仅只是一个指针;
  5. 很遗憾,在失去其内涵的同时,它还失去了其常量特性,可以作自增、自减等操作,可以被修改。
  6. 所以,数据名作为函数形参时,其全面沦落为一个普通指针!它的贵族身份被剥夺,成了一个地地道道的只拥有 4个字节的平民
  7. 函数内的局部自动变量,在函数返回后,内存自动被释放。

大端小端

采用 Little-endian 模式的 CPU对操作数的存放方式是从低字节到高字节,而 Big-endian 模式对操作数的存放方式是从高字节到低字节。

C/C++结构体的一个高级特性——指定成员的位数

在实际工程中,往往碰到这样的情况:那就是要用一个基本类型变量中的不同的位表示不同的含义。譬如一个 cpu 内部的标志寄存器,假设为 16 bit,而每个 bit 都可以表达不同的含义,有的表示结果是否为 0,有的表示是否越界等等。这个时候我们用什么数据结构来表达这个寄存器呢?答案还是结构体!如:

struct xxx{

    成员1类型成员1:成员1位数;

    成员2类型成员2:成员2位数;

    成员3类型成员3:成员3位数;

};

基本的成员变量就会被拆分!这个语法在初级编程中很少用到,但是在高级程序设计中不断地被用到!又例如:

struct student;{

    unsigned int sex:1;

    unsigned int age:2;

};

上述结构体中的两个成员 sex 和 age 加起来只占用了一个 unsigned int 的空间(假设 unsigned int 为 16 位)。基本成员变量被拆分后,访问的方法仍然和访问没有拆分的情况是一样的,例如:

struct student sweek;

sweek.sex=MALE;

sweek.age=20;

虽然拆分基本成员变量在语法上是得到支持的,但是并不等于我们想怎么分就怎么分,例如下面的拆分显然是不合理的:

struct student{

    unsigned int sex:1;

    unsigned int age:12;

};

这是因为 1+12 = 13,不能再组合成一个基本成员,不能组合成 char、 int 或任何类型,这显然是不能“自圆其说”的。

在拆分基本成员变量的情况下,我们要特别注意数据的存放顺序,这还与 CPU 是 Big endian 还是 Little endian来决定。 Little endian 和 Big endian 是 CPU 存放数据的两种不同顺序。对于整型、长整型等数据类型, Bigendian 认为第一个字节是最高位字节(按照从低地址到高地址的顺序存放数据的高位字节到低位字节);而Little endian 则相反,它认为第一个字节是最低位字节(按照从低地址到高地址的顺序存放数据的低位字节到高位字节)

总结:

  1. C/C++语言的结构体支持对其中的基本成员变量按位拆分;
  2. 拆分的位数应该是合乎逻辑的,应仍然可以组合为基本成员变量;
  3. 要特别注意拆分后的数据的存放顺序,这一点要结合具体的 CPU 的结构。

 

C/C++中的近指令、远指针和巨指针

char near *p; /*定义一个字符型“近”指针*/

char far *p; /*定义一个字符型“远”指针*/

char huge *p; /*定义一个字符型“巨”指针*/

 

C/C++中联合体的使用

 

ARM处理器支持7种处理器模式。如下表

处理器模式

描述

User

普通程序执行的模式

FRQ

用于高速数据传输或者通道处理

IRQ

用于通用中断处理

Supervisor

操作系统的保护模式

Abort

用于实现显存或者存储保护

Undefined

支持软件模拟或者硬件协处理

System

运行特权操作系统任务

大部分应用程序都在 User 模式下运行。当处理器处于 User 模式下时,执行的程序无法访问一些被保护的系统资源,也不能改变模式,否则就会导致一次异常。对系统资源的使用由操作系统来控制。User 模式之外的其它几种模式也称为特权模式,它们可以完全访问系统资源,可以自由地改变模式。其中的 FIQ、IRQ、supervisor、Abort 和 undefined 5 种模式也被称为异常模式。在处理特定的异常时,系统进入这几种模式。这 5 种异常模式都有各自的额外的寄存器,用于避免在发生异常的时候与用户模式下的程序发生冲突。

还有一种模式是 system 模式,任何异常都不会导致进入这一模式,而且它使用的寄存器和 User 模式下基本相同。它是一种特权模式,用于有访问系统资源请求而又需要避免使用额外的寄存器的操作系统任务。

 

现在来探讨一下slave模式:所谓的slave模式,就是mplayer在运行过程中能够接收用户的输入命令行,具体支持哪些命令行,能够通过 mplayer -input cmdlist这条命令来得到,在Mplayer源码的slave.txt中也有对这些命令有详细的讲解。Slave模式下工作的Mplayer可以和系 统的标准输入、输出进行信息交互。我们可以用linux C编程来完成对slave模式工作的Mplayer进行控制和信息获取。

如:

mkfifo(“/tmp/fifo”,0777);

可以使用popen()来打开Mplayer

FILE* mp;

mp=popen(“mplyer /home/linux/1.mp3 -quiet –slave –input file=/tmp/fifo,”r”);

可以通过管道/tmp/fifo给mplayer发送命令,通过mp获取mplay的返回数据

如:system(“echo ”mute 0” > /tmp/fifo”);//写命令

fgets(buf,1000,mp);//读取mplay返回数据

 

http://blog.hehehehehe.cn/a/8690.htm

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值