您好,欢迎访问三七文档
当前位置:首页 > 商业/管理/HR > 企业文化 > 第3章 嵌入式Linux C高级用法
第3章嵌入式LinuxC高级用法本章内容3.1预处理3.2C语言中的内存分配3.3程序的可移植性考虑3.4C和汇编的接口本章小结3.1预处理3.3.1预定义3.3.2文件包含3.3.3条件编译3.1.1预定义在C语言源程序中允许用一个标识符来表示一串符号,称为宏,被定义为宏的标识符称为宏名。在编译预处理时,对程序中所有出现的宏名,都用宏定义中的符号串去替换,这称为宏替换或宏展开。预定义符号有一些预处理定义的符号串,它们的值或者是字符串常量,或者是十进制数字常量,它们通常在调试程序时用于输出源程序的各项信息。符号示例含义__FILE__/home/david/hello.c正在预编译的源文件名__LINE__5文件当前行的行号__FUNCTION__main当前所在的函数名__DATE__Mar132009预编译文件的日期__TIME__23:04:12预编译文件的时间__STDC__1如果编译器遵循ANSIC,则值为1#includestdio.hvoidmain(){printf(Thefileis%s\n,__FILE__);printf(Thelineis%d\n,__LINE__);printf(Thelineis%d\n,__LINE__);printf(Thedateis%d\n,__DATE__);printf(Thetimeis%s\n,__TIME__);}宏定义宏定义是由源程序中的宏定义#define语句完成的;而宏替换是由预处理程序自动完成的。(1)无参宏定义无参宏的宏名(也就是标识符)后不带参数,其定义的一般形式为:#define标识符字符串(2)带参宏定义对带参数的宏,在调用中不仅要宏展开,而且要用实参去代换形参。带参宏定义的一般形式为:#define宏名(形参表)字符串其中的#表示这是一条预处理命令。凡是以#开头的均为预处理命令。define为宏定义命令。标识符为所定义的宏名。字符串可以是常数、表达式、格式串等。对于宏定义还要说明以下几点。宏定义用宏名来表示一串符号,在宏展开时又以该符号串取代宏名,这只是一种简单的替换,符号串中可以包含任何字符,可以是常数,也可以是表达式,预处理程序对它不做任何检查。如有错误,只能在编译已被宏展开后的源程序时发现。宏定义不是声明或语句,在行末不必加分号,如加上分号则连分号也一起置换。宏定义的作用域包括从宏定义命名起到源程序结束,如要终止其作用域可使用#undef命令来取消宏作用域,宏名在源程序中若用引号括起来,则预处理程序不对其进行宏替换。宏定义允许嵌套,在宏定义的符号串中可以使用已经定义的宏名,在宏展开时由预处理程序层层替换。习惯上宏名用大写字母表示,以便于与变量区别,但也允许用小写字母表示。对输出格式做宏定义,可以减少编写麻烦由于宏定义非常容易出错,因此,对于带参的宏定义有以下问题需要特别说明。带参宏定义中,宏名和形参表之间不能有空格出现。在带参宏定义中,形式参数不分配内存单元,因此不必做类型定义。在宏定义中的形参是标识符,而宏调用中的实参可以是表达式,在宏定义中,字符串内的形参通常要用括号括起来以避免出错intSQ(inty){return(y*y);}main(){inti=1;while(i=5)printf(%d,SQ(i++));}#defineSQ(y)((y)*(y))main(){inti=1;while(i=5)printf(%d,SQ(i++));}结果为:1491625结果为:1925#defineSQ(y)((y)*(y))main(){inti=1;while(i=5)printf(%d%d\n“,SQ(i++),i);}结果为:1193255宏定义属性#define宏函数处理阶段预处理阶段,只是符号串的简单的置换编译阶段代码长度每次使用宏时,宏代码都被插入到程序中。因此,除了非常小的宏之外,程序的长度都将被大幅增长(除了inline函数之外)函数代码只出现在一个地方,每次使用这个函数,都只调用那个地方的同一份代码执行速度更快存在函数调用/返回的额外开销(inline函数除外)操作符优先级宏参数的求值是在所有周围表达式的上下文环境中,除非它们加上括号,否则邻近操作符的优先级可能会产生不可预料的结果函数参数只在函数调用时求值一次,它的结果值传递给函数,因此,表达式的求值结果更容易预测参数求值参数每次用于宏定义时,它们都将重新求值。由于多次求值,具有副作用的参数可能会产生不可预料的结果参数在函数被调用前只求值一次,在函数中多次使用参数并不会导致多种求值问题,参数的副作用不会造成任何特殊的问题参数类型宏与类型无关,只要对参数的操作时合法的,它可以使用于任何参数类型函数的参数与类型有关,如果参数的类型不同,就需要使用不同的函数,即使它们执行的任务是相同的宏与函数的不同之处3.1.2文件包含文件包含是C语言预处理程序的另一个重要功能,文件包含命令行的一般形式为:#include文件名“对文件包含命令还要说明以下几点:包含命令中的文件名可以用双引号括起来,也可以用尖括号括起来。使用尖括号表示在系统头文件目录中去查找(头文件目录可以由用户来指定);使用双引号则表示首先在当前的源文件目录中查找,若未找到才到系统头文件目录中去查找。用户编程时可根据自己文件所在的位置来选择某一种形式。一个include命令只能指定一个被包含文件,若有多个文件要包含,则需用多个include命令。文件包含允许嵌套,即在一个被包含的文件中又可以包含别的文件。文件包含语句的功能是把指定的文件插入该语句行位置,从而把指定的文件和当前的源程序文件连成一个源文件。在程序设计中,文件包含是很有用的。一个大的程序可以分为多个模块,由多个程序员分别编写。有些公用的符号常量、宏、结构、函数等的声明或定义可单独组成一个文件,在其他文件的开头用包含命令包含该文件即可使用。这样,可避免在每个文件开头都去写那些公用量,从而节省时间,并减少出错。3.1.3条件编译第一种形式第二种形式第三种形式#ifdef标识符程序段1#else程序段2#endif#ifndef标识符程序段1#else程序段2#endif#if常量表达式程序段1#else程序段2#endif例题例题3.2C语言中的内存分配3.2.1C语言程序所占内存分类3.2.2堆和栈的区别3.2.1C语言程序所占内存分类一个由C语言的程序占用的内存分为以下几个部分。栈(stack):由编译器自动分配释放,存放函数的参数值、局部变量的值、返回地址等,其操作方式类似于数据结构中的栈。堆(heap):一般由程序员动态分配(调用mallo()函数)和释放(调用free()函数),若程序员不释放,程序结束时可能由操作系统回收。数据段(data):存放的是全局变量、静态变量、常数。根据存放的数据,数据段又可以分成普通数据段(包括可读可写/只读数据段,存放静态初始化的全局变量或常量)、BSS数据段(存放未初始化的全局变量)。代码段(code):用于存放程序代码。inta=0;char*p1;intmain(){intb;chars[]=“abc”;char*p2;char*p3=“123456”;staticintc=0;p1=(char*)malloc(10);p2=(char*)malloc(20);strcpy(p1,”Helloworld”);}a在可读可写的数据区p1在数据区的bss段b在栈中s在栈中,”abc”在只读数据段的常量区p2在栈中p3在栈中,”123456”常量区c在可读可写的数据区刚分配来的内存在堆区,并且不一定连续“Helloworld”在常量区分析下列变量和常量存在的区域3.2.2堆和栈的区别申请方式栈(stack)是由系统自动分配的;堆(head)需要程序员自己申请,并在申请时指定大小。申请后系统的响应堆在操作系统中有一个记录空闲内存地址的链表,当系统收到程序的申请时,系统就会开始遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序。栈(stack)是由系统自动分配的,例如,声明函数中一个局部变量“intb;”,那么系统自动在栈中为b开辟空间。堆(head)需要程序员自己申请,并在申请时指定大小,使用C语言中的malloc()函数的例子如下所示:p1=(char*)malloc(10);3.2.2堆和栈的区别申请大小的限制堆是向高地址扩展的数据结构,是不连续的内存区域;栈是向低地址扩展的数据结构,是一块连续的内存的区域。申请速度的限制堆是由malloc()等语句分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来很方便。栈由系统自动分配,速度较快,但程序员一般无法控制。堆和栈中的存储内容堆一般在堆的头部用一个字节存放堆的大小,堆中的具体内容由程序员安排。#includestdio.hvoidmain(){inti=0;printf(%d%d%d\n,i++,i++,i++);printf(%d\n,i);}本程序在不同的编译器下,运行结果是不同的,它能反映该编译器对函数调用时的处理。调用过程,形参次序用不同编译器编译本程序看看3.3程序的可移植性考虑3.3.1字长和数据类型3.3.2数据对齐3.3.3字节顺序3.3.1字长和数据类型能够由机器一次完成处理的数据称为字,不同体系结构的字长通常会有所区别。为了解决不同体系结构的不同字长问题,嵌入式Linux中给出了不透明数据类型和透明数据类型;例如FILE等而嵌入式系统经常操作一些硬件设备,必须知道数据长度,嵌入式Linux内核在”asm/tytypes.h”中定义了一些长度明确的类型。类型描述s8带符号字节u8无符号字节s16带符号16位整数u16无符号16位整数s32带符号32位整数u32无符号32位整数s64带符号64位整数u64无符号64位整数3.3.2数据对齐对齐是内存数据与内存中的相对位置相关的话题。一些体系结构对对齐的要求非常严格。通常基于RISC的系统载入未对齐的数据会导致处理器陷入(一种可处理的错误);还有一些系统可以访问没有对齐的数据,但性能会下降。3.3.3字节顺序字节顺序是指一个字中各个字节的顺序,有大端模式和小端模式。大端模式是指在这种格式中,字数据的高字节存储在低地址中,而字数据的低字节则存放在高地址中。小端模式是指与大端存储格式相反,在小端存储格式中,低地址中存放的是字数据的低字节,高地址存放的是字数据的高字节。ARM体系结构支持大端模式(big-endian)和小端模式(little-endian)两种内存模式。3.4C和汇编的接口3.4.1内嵌汇编的语法3.4.2编译器优化介绍3.4.3C语言关键字volatile3.4.4memory描述符3.4.1内嵌汇编的语法汇编语言模板汇编语句模板由汇编语句序列组成,语句之间使用“;”、“\n”或“\n\t”分开。每条指令都必须被双引号括起来。两条指令必须用换行或分号分开。指令中的操作数可以使用占位符引用C语言变量,操作数占位符最多10个,名称如为:%0,%1,...,%9在阅读C/C++原代码时经常会遇到内联汇编的情况,下面简要介绍下ARM体系结构下的__asm__内嵌汇编用法。带有C/C++表达式的内联汇编格式为:__asm__(汇编语句模板:输出部分:输入部分:破坏描述部分)__asm__是GCC关键字asm的宏定义:__asm__或asm用来声明一个内联汇编表达式,所以任何一个内联汇编表达式都是以它开头的,是必不可少的。#define__asm__asm#define__volatile__volatile3.4.1内嵌汇编的语法输出部分输出部分用来指定当前内联汇编语句的输出。输入部分输入部分用来指定当前内联汇编语句的输入,每个操作数描述符由限定字符串和C语言表达式或者C语言变量组成,格式为形如“constraint”(variabl
本文标题:第3章 嵌入式Linux C高级用法
链接地址:https://www.777doc.com/doc-3178225 .html