




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、嵌入式系统程序设计大连理工大学软件学院嵌入式系统工程系赖晓晨C/C+语言摘要C语言的历史和特点预处理程序位运算函数指针C程序的移植一、C语言的历史和特点C语言的历史C语言的特点C语言的优良特性结构化语言,代码数据分离包含指针特性,允许对地址操作语法简洁紧凑,但功能强大编程方便,运行速度快支持分离编译C语言的缺点封装性不如C+,数据安全性上有缺陷 类型检查机制相对薄弱 指针的操作带来许多不安全因素 比其他高级语言较难掌握二、预处理程序C89规定的预处理指令有以下几条#if#ifdef#ifndef#else#elif#endif#define#undef#line#error#pragma#in
2、clude预处理语句的书写规则作用:对源程序编译之前做一些处理,生成扩展C源程序格式:“#”开头占单独书写行语句尾不加分号1. 宏定义不带参数宏定义一般形式: #define 宏名 宏体功能:用指定标识符(宏名)代替字符序列(宏体)1. 宏定义不带参数宏定义一般形式: #define 宏名 宏体功能:用指定标识符(宏名)代替字符序列(宏体)如 #define YES 1 #define NO 0 #define PI 3.1415926 #define OUT printf(“Hello,World”);1. 宏定义不带参数宏定义一般形式: #define 宏名 宏体功能:用指定标识符(宏名)
3、代替字符序列(宏体)宏体可缺省,表示宏名定义过如 #define YES 1 #define NO 0 #define PI 3.1415926 #define OUT printf(“Hello,World”);1. 宏定义不带参数宏定义一般形式: #define 宏名 宏体功能:用指定标识符(宏名)代替字符序列(宏体)定义位置:任意(一般在函数外面)作用域:从定义命令到文件结束#undef可终止宏名作用域 格式: #undef 宏名1. 宏定义不带参数宏定义一般形式: #define 宏名 宏体功能:用指定标识符(宏名)代替字符序列(宏体)定义位置:任意(一般在函数外面)作用域:从定义命令
4、到文件结束#undef可终止宏名作用域 格式: #undef 宏名例 #define ID 1 main() #undef ID #define ID 0 max() YES原作用域YES新作用域如 if(x=YES) printf(“correct!n”); else if (x=NO) printf(“error!n”);展开后: if(x=1) printf(“correct!n”); else if (x=0) printf(“error!n”);1. 宏定义不带参数宏定义一般形式: #define 宏名 宏体功能:用指定标识符(宏名)代替字符序列(宏体)宏展开:预编译时,用宏体替换宏
5、名-不作语法检查定义位置:任意(一般在函数外面)作用域:从定义命令到文件结束#undef可终止宏名作用域 格式: #undef 宏名1. 宏定义不带参数宏定义一般形式: #define 宏名 宏体功能:用指定标识符(宏名)代替字符序列(宏体)宏展开:预编译时,用宏体替换宏名-不作语法检查定义位置:任意(一般在函数外面)作用域:从定义命令到文件结束#undef可终止宏名作用域 格式: #undef 宏名引号中的内容与宏名相同不置换例 #define ID 1 语句printf( ID );会输出ID,而非11. 宏定义不带参数宏定义一般形式: #define 宏名 宏体功能:用指定标识符(宏名)
6、代替字符序列(宏体)宏展开:预编译时,用宏体替换宏名-不作语法检查定义位置:任意(一般在函数外面)作用域:从定义命令到文件结束#undef可终止宏名作用域 格式: #undef 宏名宏定义可嵌套,不能递归例 #define ID ID + 1 ()引号中的内容与宏名相同不置换例 #define DIS1 10 #define DIS2 DIS1+10 var=DIS2*2;宏展开:var= 10+10 *2;1. 宏定义不带参数宏定义一般形式: #define 宏名 宏体功能:用指定标识符(宏名)代替字符序列(宏体)宏展开:预编译时,用宏体替换宏名-不作语法检查定义位置:任意(一般在函数外面)
7、作用域:从定义命令到文件结束#undef可终止宏名作用域 格式: #undef 宏名宏定义可嵌套,不能递归引号中的内容与宏名相同不置换宏定义中使用必要的括号()例 #define DIS1 10 #define DIS2 (DIS1+10) var=DIS2*2;宏展开:var= (10+10) *2; 1. 宏定义不带参数宏定义一般形式: #define 宏名 宏体功能:用指定标识符(宏名)代替字符序列(宏体)宏展开:预编译时,用宏体替换宏名-不作语法检查定义位置:任意(一般在函数外面)作用域:从定义命令到文件结束#undef可终止宏名作用域 格式: #undef 宏名宏定义可嵌套,不能递归
8、引号中的内容与宏名相同不置换宏定义中使用必要的括号()宏体可以省略,表示宏名已被定义过/* ch3_1.c*/#include #define Aint main()#ifdef Aprintf(A has been definedn); #elseprintf(A has not been definedn); #endifreturn 0;输出为:“A has been defined”,即使把宏定义改为:#define A0输出仍旧为:“A has been defined”。带参数宏定义一般形式: #define 宏名(参数表) 宏体例 #define T (m, n) m*n相当于定
9、义了不带参宏T,其宏体为“(m, n) m*n” 宏展开:形参用实参换,其它字符保留例 #define T(m,n) m*n . area=T(3,2);宏展开: area=3*2;不能加空格带参数宏定义一般形式: #define 宏名(参数表) 宏体宏展开:形参用实参换,其它字符保留宏体及各形参外一般应加括号()例 #define T(m,n) m*n . area=T(3,2);宏展开: area=3*2;例 #define CUBE(x) x*x*x a=4; b=6; z=CUBE(a+b);宏展开:z=a+b*a+b*a+b;一般写成: #define CUBE(x) (x)*(x)
10、*(x)宏展开: z=(a+b)*(a+b)*(a+b);#define FUNC(x,y) (x)(y)?(x):(y) .main() int a,b,c,d,t; . t=FUNC(a+b,c+d); 宏展开:t=(a+b)(c+d)?(a+b):(c+d);int func(int x,int y) return(xy?x:y);main() int a,b,c,d,t; . t=func(a+b,c+d); 例 用宏定义和函数实现同样的功能带参的宏与函数区别带参宏函数处理过程不分配内存简单的字符置换分配内存先求实参值,再代入形参处理时间编译时程序运行时参数类型无类型问题定义实参,形参
11、类型程序长度变长不变运行速度不占运行时间调用和返回占时间Embest开发环境中的宏定义44b.h有44B0X中各个特殊功能寄存器的宏定义#define rBWSCON(*(volatile unsigned *)0 x1c80000)#define rBANKCON0(*(volatile unsigned *)0 x1c80004)#define rBANKCON1(*(volatile unsigned *)0 x1c80008)#define rBANKCON2(*(volatile unsigned *)0 x1c8000c)#define rBANKCON3(*(volatile u
12、nsigned *)0 x1c80010)#define rBANKCON4(*(volatile unsigned *)0 x1c80014)#define rBANKCON5(*(volatile unsigned *)0 x1c80018)预定义宏C89规范了五个固有的预定义宏,分别为:_LINE_:行号_FILE_:文件名_DATE_:日期_TIME_:时间_STDC_:1标准C编译器 0非标准C编译器预定义宏#include int main()printf(The current file is: %sn, _FILE_);printf(The current line numbe
13、r is: %dn, _LINE_);printf(today is: %sn, _DATE_);printf(the time is: %sn, _TIME_);if(_STDC_ = 1)printf(this is a standard compilern);else if(_STDC_ = 0)printf(this is not a standard compilern);/exp/pre/macro.c功能:对源程序中的一部分内容只有满足某种条件的情况下才进行编译。2. 条件编译#ifdef 标识符程序段1#else程序段2#endif形式1:当标识符已经被定义过(使用#defin
14、e),则对程序段1进行编译,否则编译程序段2。其中#else部分可以省略。形式1(续):#define IBM-PC 0 /*或 #define IBM-PC */。#ifdef IBM-PC#define INT 16#else#define INT 32#endif可以用来提高程序的可移植性形式1(续):#define DEBUG .#ifdef DEBUGprintf(“x=%d, y=%d”,x,y);#endif可以用来调试程序调试结束后只需将define行删掉即可#ifndef 标识符程序段1#else程序段2#endif形式2:当标识符未被定义过,则对程序段1进行编译,否则编译程
15、序段2。(与形式1正相反)#if 表达式程序段1#else程序段2#endif形式3:当表达式为真时,则对程序段1进行编译,否则编译程序段2。 #include #define CAP 1int main()char string20=I love China;char c; int i=0;c=stringi+;while(c!=0)#if CAPif(c=a&c=z)c-=32;#endifprintf(%c,c);c=stringi+;printf(n);return 0;形式3:(续)3. 文件包含功能:一个源文件可将另一个源文件的内容全部包含进来一般形式: #include “文件名
16、” 或 #include 直接按标准目录搜索“” 先在当前目录搜索,再搜索标准目录可指定路径3. 文件包含功能:一个源文件可将另一个源文件的内容全部包含进来一般形式: #include “filename” 或 #include 处理过程:预处理时,用被包含文件的内容取代该预处理命令,再把“包含”后的文件作为一个源文件编译被包含文件内容源文件(*.c)头文件(*.h)宏定义数据结构定义函数说明等实际上文件名也可以是C源文件,不过这不是良好的编程风格【例3-5】文件包含一下程序包含一个完整的模块(function.c、function.h、test.c) /*funtion.h*/#ifndef
17、 _FUNCTION#define _FUNCTIONvoid f();#endif/*function.c*/#include void f()printf(a example of #include.n);/*test.c*/#include #include function.hint main()f();return 0; 头文件格式4. 其他预处理指令#error指令强制编译器停止编译,主要用于程序调试。#error指令的一般形式为:编译到#error时,会显示相应字符串#error error-message#error举例#define CON10#define CON21#de
18、fine CON3-1int main()#ifCON1#ifCON2#error run to position1#else#error run to position2#endif#else#ifCON3#error run to position3#else#error run to position4#endif#endif明确程序编译位置三、位运算位运算符按位与&取反按位或|左移1. 位与&运算规则:两个位都为1,结果为1,否则为0例如:9&0 x0c结果为82. 位或|运算规则:两个位都为0,结果为0,否则为1例如:9|0 x0c结果为0 x0d3. 按位取反运算规则:1变0,0变
19、1用途:使某位取反3. 按位取反 (续)例:使某数最低位为0int a;a=a&0 xfffffffe3. 按位取反 (续)例:使某数最低位为0思考:有没有隐含的问题int a;a=a&0 xfffffffe3. 按位取反 (续)例:使某数最低位为0/* 16位机 */int a;a=a&0 xffffe/* 32位机 */a=a&0 xfffffffe可移植性变差3. 按位取反 (续)例:使某数最低位为0a=a&1/* 1能自动适应16位机以及32位机 */解决办法4. 按位异或运算规则:判断两位是否相同,同则为0,否则为1用途:使特定位翻转 交换两个值,不用临时变量4. 按位异或 (续)不
20、通过中间变量交换两个变量的值/*ch3_7.c*/#include int main()int a=21;int b=43;a=ab;b=ba;a=ab;printf(a=%d, b=%dn,a, b);return 0; 5. 左移位运算规则:将一个数的全部二进制位右移若干位,移出位舍弃,左侧可能补0或者补1,视计算机系统不同而不同。符号问题:无符号数右移,左侧补0有符号数视计算机系统而定:逻辑右移、算术右移例:循环右移n位10011111110010101001111111001010N位例:循环右移n位(续)#include int main()unsigned a,b,c,n;scan
21、f(a=%x,n=%d,&a,&n);b=an;c=c|b;printf(a=%x,c=%xn,a,c);return 0;输入:a=0 x12345678,n=8显示:a=12345678,c=78123456四、函数指针 每一个函数模块都有一个首地址,称为函数的入口地址。 指向函数的指针称为函数指针,保存的是函数的入口地址(首地址) 函数调用:找到函数入口地址;传递参数 1. 函数指针的定义方法格式:int (*ptr1)(int);定义了函数指针ptr1,此指针只能保存具有一个整型参数的整型函数的首地址。注意 :int *ptr1(int);这是一条函数声明语句,表示一个带有整数参数的函
22、数返回,返回一个整型指针。int (*ptr1)(int); 这个是函数指针例:以0.1为步长,计算特定范围内的三角函数之和。sin(0.1)+sin(0.2)+sin(1.0)cos(0.5)+cos(0.6)+cos(3.0)#include #include double triangle(double(*func)(double), double begin, double end)double step,sum=0.0;for(step=begin;stepend;step+=0.1)sum+=func(step);return sum;int main() double resul
23、t;result=triangle(sin, 0.1, 1.0);printf(the sum of sin from 0.1 to 1.0 is %fn,result);result=triangle(cos, 0.5, 3.0);printf(the sum of cos from 0.5 to 3.0 is %fn,result);return 0;#include #include double triangle(double(*func)(double), double begin, double end)double step,sum=0.0;for(step=begin;step
24、end;step+=0.1)sum+=func(step);return sum;int main() double result;result=triangle(sin, 0.1, 1.0);printf(the sum of sin from 0.1 to 1.0 is %fn,result);result=triangle(cos, 0.5, 3.0);printf(the sum of cos from 0.5 to 3.0 is %fn,result);return 0;2. 用typedef来简化函数指针对参数较多的函数类型,定义相应的函数指针比较烦琐,可用typedef关键字简化
25、例如typedef double (*FUN)(double a, double b);用typedef来简化函数指针(续)格式:int (*ptr1)(int);定义函数指针的别名方法:定义了函数指针ptr1,此指针只能保存具有一个整型参数的整型函数的首地址。double (*fun1)(double a, double b);typedef double (*FUN)(double a, double b);FUN f1; FUN f2; 用typedef来简化函数指针(续)格式:int (*ptr1)(int);定义函数指针的别名方法:(也可以这样)定义了函数指针ptr1,此指针只能保存
26、具有一个整型参数的整型函数的首地址。double (*fun1)(double a, double b);typedef double (FUN)(double a, double b);FUN *f1; FUN *f2; 3函数指针数组声明方式定义了表示定义了一个长度为4的函数指针数组,同时做了初始化。double ( * fp4 ) (double,double)= f1 , f2 , f3, f4 4函数指针应用在嵌入式操作系统中,经常用函数指针来完成任务的调度。例如uC/OS中,任务创建的函数原型为第一个参数为函数指针INT8U OSTaskCreate(void(*task)(voi
27、d*pd), void *pdata, OS_STK *ptos, INT8U prio);五、C程序的移植为一种机器写的程序,经常需要在其他硬件、操作系统的平台上运行,往往需要对此程序进行一些改动,这个过程叫做程序的移植方便移植的程序称为可移植程序程序不可移植,主要是因为有太多硬件相关的代码1、避免使用“魔数”“魔数”(magic number):依赖于系统或处理器的数字。例如表示硬盘缓冲区的大小、屏幕和键盘的特定尺寸等数字。“魔数”的出现使系统可移植性变差。下列代码本质上是不可移植的fread(buf, 256, 1, fp); /缓冲区为256B使用#define替换“魔数”程序中要尽量
28、避免“魔数”的硬编码,可以用宏来取代魔数,使可读性增强,而且移植程序时只要修改宏一处即可#define BUFFER_SIZE 256fread (buf, BUFFER_SIZE, 1, fp); 例一个图形处理程序中,需要不同的颜色执行不同操作例/*恶劣的例子*/void ShowColor(int color)if ( color = 0 )sub_red();else if ( color = 1 )sub_blue();else if ( color = 2 )sub_green();return ; 例(改进后的程序) /*然后在源文件中直接使用这些宏来判断*/#include c
29、olor.hvoid ShowColor(int color)if ( color = RED )sub_red();else if ( color = BLUE )sub_blue();else if ( color = GREEN )sub_green();return ; /* color.h*/#define RED 0#define BLUE 1#define GREEN 22、程序分层不同的操作系统为应用程序提供了不同的支持,例如windows2000应用程序可以有多线程特性,但是windows3.2不可以,应用程序对系统有依赖型。这种依赖性没有具体通用的解决方法,但是可以通过程序
30、分层来解决,把系统相关的代码放到一起。3、注意数据类型的长度16位机中整型数据是2字节,32位机为4字节,这会导致程序的不兼容。可以利用宏来重定义数据类型#define int16 int/16位机int16 a;#define int16 short int/32位机int16 a;注意数据类型的长度(续)16位机中整型数据是2字节,32位机为4字节,这会导致程序的不兼容。使用可适应任何情况的编码方式,例如把一个整型数据写入磁盘:fwrite(&i, 4, 1, fp);/不好fwrite(&i, sizeof(int), 1, fp);/好4、对齐问题某些计算机允许数据边界地址不对齐,把这
31、样的代码移植到ARM上时要小心。ARMv5TE前的处理器都不支持地址不对齐的指针5、大小端问题如果两台计算机的大小端定义不一致,那么代码移植时要做转换6、枚举类型enum是可移植的,但是不同编译器中对enum分配的字节数可能不同。不能在不同的编译器之间对代码进行交叉连接7、减少内嵌汇编C语言的内嵌汇编由C编译器来负责编译,而不使用armasm或gas。内嵌汇编可以提高编程效率,但是会影响到程序的可移植性。C语言与C+语言的区别变量定义位置结构体变量数据类型输入输出动态内存分配其他区别1. 变量定义位置C89要求所有变量都必须定义在块的最前部C+没有这个要求,可以在程序任意位置定义新的变量2.
32、结构体变量在C+中,struct结构体支持成员函数的定义,C中不行。如果在C的struct中定义函数,编译时会显示一个“field function name declared as function”错误2. 结构体变量(续)/*ch3_10.c*/struct Aint a;int b();int main()struct A c;c.a=2;return 0;编译错误:“ch3_10.c:5: error: field b declared as a function”C+标准可以通过编译2. 结构体变量(续)在C语言中,声明一个结构体类型A之后,使用下面的语句来定义结构体变量a:str
33、uct A a;而C+语言中可以省略struct3. 数据类型C+中有bool (或boolean类型);C中没有这样的bool类型,均为数值类型!C编译器不能通过编译,C+编译器可以bool a;a = 1;4. 输入输出 C中使用printf、scanf输入输出使用时不用包含任何头文件但如果使用g+编译时必须加上stdio.h头文件int a;scanf( %d, &a );printf( 您输入的数值是%dn, a );A. scanf()函数功能:从键盘读入指定格式的数据格式:scanf( 控制字符串, 输入项列表 );注意:scanf中各变量一定是表示地址的标识符(加&)控制字符串控
34、制字符串有两部分组成:格式说明形式:%普通字符空格可打印字符格式说明各格式字符及其意义:(详见C教程)d:输入一个十进制整数o:输入一个八进制整数x:输入一个十六进制整数f:输入一个小数形式的浮点数e:输入一个指数形式的浮点数c:输入一个字符s:输入一个字符串空格在多个输入时,一般用空格或回车作为分隔符若以空格作为分隔符,当输入中包含字符类型时,可能产生非预期的结果scanf ( %d%c, &a, &ch );输入:45 q输出:45 空格空格(续)如下语句会有正确输出此处%d后的空格,就可以跳过字符q前的所有空格scanf ( %d %c, &a, &ch );输入:45 q输出:45 q
35、可打印字符看一个例子输入为:1,2,q可以得到 a = 1, b = 2, ch = q输入为:1 2 q除a的值为1外,对b与ch的赋值失败scanf ( %d,%d,%c, &a, &b, &ch );B. printf()函数功能:从缺省输出设备(一般为显示器)输出规定格式的字符串格式:printf( 控制字符串, 输入项列表 );控制字符串控制字符串有两部分组成:格式说明形式:%普通字符空格可打印字符格式说明各格式字符及其意义:(详见C教程)c:按字符型输出o:按八进制输出d:按十进制输出x:按十六进制输出u:按无符号整数输出f:按浮点型小数输出g:按e和f格式中较短的一种输出e:按科
36、学计数法输出普通字符普通字符:可打印字符主要是说明字符,按原样输出,支持汉字输出转义字符(例)不能直接打印,控制产生特殊的输出效果普通字符(续)转义字符示例i = 789,n = 123, a = 92.34567,且i为整型,n为长整型。 printf( %4dt%7.4fnt%lun, i, a, n );输出为: 78992.3457 123C语言输入输出总结输入输出可能是C和C+的最明显的区别C中用scanf(), printf()来完成输入输出操作C+中全局对象cin、cout来输入输出,比C更方便,而且类型检查机制更加完善C+中的使用方式new申请delete释放C中的使用方式ma
37、lloc()申请free()释放5. 动态内存分配函数原型:void *malloc( long size );作用:在对内存中分配size各字节,并返回了指向这块内存首地址的指针如果分配失败,返回NULL返回指针为void*型的,要强制转换A. malloc()函数函数原型:void free( void *FirstByte );作用:将之前用malloc申请的空间归还操作系统否则就导致内存泄漏编译器不会发现内存泄漏这样的错误B. free()函数C. 函数的用法/*例 3-11*/#include #include int main()int* p;if(p = (int*)malloc
38、(sizeof(int) = NULL )printf(动态内存分配失败n);exit(1);C. 函数的用法(续)*p = 100;printf(%dn, *p);free(p);p = NULL;return 0;头文件:malloc和free被头文件stdlib.h包含C+中new和delete为关键字,故无需头文件包含使用:int *p=(int*)malloc(sizeof(int);int *p = new int;与C+的几点区别6. 其他区别常量表示方法不同C语言不支持引用的概念,而C+支持注释不同,C89不支持单行注释(+i)+在C中不合法(a=3)=4在C中不合法不能在fo
39、r循环头部定义变量GNU C扩展64位整型数据类型内联函数attribute关键字单行注释switch case 简写预定义宏结构体和初始化长度为0的数组64位整型数据类型GNU CC(GNU Compiler Collection)定义了64为整型关键字long long,可以直接操作64位数据long long int a = 0 x123;expgnu_clong.c内联函数减少函数调用开销增加类型检查,比宏使用更安全inline是建议而非命令attribute关键字关键字attribute通过向GCC指明有关代码的更多信息来帮助代码优化工作进行的更好。attribute关键字(续)下面
40、代码通过使用_attribute_,当t未被使用时编译器不会警告int main( )float t _attribute_ (unused);return 0;例3-12#include void quit() _attribute_ (noreturn);void quit()exit(1);int test(int n)if ( n 0 )quit();elsereturn 0;int main( )test(1);return 0;函数不返回【例3-13】/*ch3_13.c*/#include struct s int a2 _attribute_ (aligned (8); ;st
41、ruct tchar a;int b2 _attribute_ (packed); int main()int y _attribute_ (aligned (16);y=1;return 0;以8字节对齐单行注释int main()/long long t1;int t4=3;return 0;expgnu_cnote.cswitch case 简写/*ch3_14.c*/#include int main()int t=2;/*传统方式*/switch(t)case 0 :case 1 :case 2 :printf(012n);break;case 3 :case 4 :case 5 :
42、printf(345n);switch case 简写/*GCC扩展方式*/switch(t)case 0 . 2 :printf(012n);break;case 3 . 5 :printf(345n);return 0;expgnu_ccase.cgcc允许这样写。预定义宏/*ch3_15.c*/#include void f(void)printf(This is function %s n, _FUNCTION_);int main( )printf(This is function %s n, _FUNCTION_);f();return 0;expgnu_cfunc.c结构体初始化
43、struct file_operations ext2_file_operations =llseek:generic_file_llseek,read:generic_file_read,write:generic_file_write,ioctl:ext2_ioctl,mmap:generic_file_mmap,open:generic_file_open,release:ext2_release_file,fsync:ext2_sync_file,;expgnu_cfunc.c结构体初始化 struct file_operations driver_fops =owner: THIS_
44、MODULE,read: driver_read,open: driver_open,release: driver_release,;其他的值被初始化为0.expgnu_cfunc.c长度为0的数组GNU还允许结构体的末尾成员是长度为0的数组错误处理机制C语言错误处理机制系统日志文件 一、C语言的错误处理机制C标准中有几个错误处理机制 预定义宏预定义全局变量若干库函数1. assert宏assert宏定义在assert.h中语法格式:作用:计算表达式expression的值,如果为0,那么首先向stderr打印一条出错信息,然后调用abort()函数终止程序运行。assert (expres
45、sion)【例3-16】/*ch3_16.c*/#include#include #includeint main()char *p;p = getenv(HOME);assert(p);printf(HOME=%sn,p);p = getenv(NOTEXIST);assert(p);printf(NOTEXIST=%sn,p);return 0;/exp/error/badptr.cassert的缺点assert的调用会影响程序的运行效率,有时希望程序中的assert不会起作用,这可以通过在程序前部包含assert.h之前定义一个 NDEBUG来实现。【例3-17】/*ch3_17.c*/
46、#define NDEBUG#include#include #includeint main()char *p;p = getenv(HOME);assert(p);printf(HOME=%sn,p);p = getenv(NOTEXIST);assert(p);printf(NOTEXIST=%sn,p);return 0;/exp/error/badptr2.cassert使用注意事项当使用#define NDEBUG来禁用assert宏时,注意assert宏中隐含的某些操作不能完成,而这些代码可能对后面的操作有关键作用。定义NDEBUG,p赋值行未执行,使用p时出错。assert使用
47、注意事项举例#define NDEBUGassert(p=malloc(sizeof(char)*100);free(p);#define NDEBUGp=malloc(sizeof(char)*100);assert(p);free(p);应该按照这种方式来编写P是野指针assert使用讨论assert宏只提供了一种粗糙的终止程序运行的方式,这种方法并不理想。理想的方式是“适当的降级”,在不同层次的出错处理都失败了,别无选择的情况下才终止程序的运行。在不得不警告或者提示用户之前能成功的处理的出错越多,程序就越健壮。2. 位置指示宏C标准定义了两个宏:_LINE_FILE_把这两个宏用于可以更
48、精确的定位程序的出错位置。【例3-18】#include #include int show_environment(char* e, int num, char *name)char* p=getenv(e);if(p!=NULL)printf(the environment is: %sn, p);return 0;elseprintf(error occured at %d of %s.n, num,name);return 1; /exp/error/filefcn.c【例3-18】(续)int main()char* e1=HOME;char* e2=NOTEXIST;int res
49、;res = show_environment(e1, _LINE_, _FILE_);if (res)return 1;res = show_environment(e2, _LINE_, _FILE_);if (res)return 1;return 0;/exp/error/filefcn.c3. 标准库函数本节的“标准库”是指任何支持ANSI/ISO C标准的C环境的一部分变量、宏、函数。本节介绍5个函数和一个变量。stdlib.h:void abort(void);void exit(int status);int atexit(void (*fcn)(void);stdio.hvo
50、id perror(const char *s);string.hchar *strerror(int errnum);errno.hint errno;3. 标准库函数(续)C语言还提供了一些标准库函数和全局变量用来支持错误处理,具体包含5个函数,以及一个全局变量,任何ANSI/ISO C标准的实现都包含这些特性A. errnoLinux系统调用和许多库函数在出错时都要把全局变量errno设置为一个非0值,唯一对应一种出错的情况。在希望通过访问errno以确定错误类型前,要人为的把errno设置为0,因为没有任何一个函数可以把errno设置为0。errno定义在头文件stdlib.h中【例3
51、-19】/*ch3_19.c*/#include #include #include #include int main(void)FILE *fp;errno=0;fp=fopen(notexist,r);if(errno)printf(Open file failed.n);elseprintf(Open file successfully.n);/exp/error/errno.c检测errno值【例3-19】(续)printf(try again. n);errno=0;fp=fopen(notexist,w);if(errno)printf(Open file failed.n);e
52、lseprintf(Open file successfully.n);return 0;检测errno值/exp/error/errno.c清0变量errno的常见值宏值含义EPERM1操作不被允许ENOENT2文件或目录不存在ESRCH3进程不存在EINTR4系统调用中断EIO5I/O错误ENXIO6设备或地址不存在E2BIG7参数太长EBADF9错误的文件号ECHILD10子进程不存在变量errno的常见值(续)宏值含义EAGAIN11重试ENOMEM12没有内存EACCES13没有权限EFAULT14地址错误EBUSY 16设备或资源忙EEXIST17文件存在ENODEV19设备不存在
53、ENOTDIR20不是目录EISDIR21是目录变量errno的常见值(续)宏值含义EINVAL22无效参数EMFILE24打开的文件太多ENOTTY25不是打印机EFBIG27文件太长ENOSPC28磁盘上没有空间EPIPE32管道中断EDOM33数学参数超出函数定义域ERANGE34数学结果不可表示EILSEQ84非法字节序列变量errno的常见值(续)宏值含义ERESTART85中断的系统调用应该重启EUSERS87用户数太多ANSI/ISO CPOSIXB. abort函数abort函数的功能是立刻终止程序运行,并且不会执行由atexit()登记过的函数原型:void abort(vo
54、id);【例3-20】/*ch3_20.c*/#include #include int main(void)printf(Hello everybody.n);abort();printf(you can not get here. n);return 0;/exp/error/boom.cC. exit()函数exit()函数于abort()的区别在于终止程序前会执行由atexit()登记的函数函数原型:#includevoid exit(int status);exit的类型是void,即没有返回值。status是exit函数返回给操作系统的退出码,可以是任何数值,而在stdlib.h中
55、定义了两个宏:EXIT_SUCCESS和EXIT_FAILURED. atexit函数atexit函数登记在程序正常结束时要调用的函数,或者由exit调用或者由main函数返回来调用。函数原型:#includeint atexit(void (*function)(void);传递给atexit的函数不带任何参数,也没有返回值。如果function登记成功,则atexit返回0,否则返回1。如果执行abort函数,则不会调用atexit登记的函数。【例3-21】/*ch3_21.c*/#include #include void test()printf(you can still get h
56、ere. n);int main(void)printf(hello everybody. n);if(atexit(test)!=0)printf(atexit() run failed.n);exit(EXIT_FAILURE);printf(goodbye everybody. n);return 0;E. strerror函数strerror函数返回一个指向字符串的指针,该字符串描述了和errnum相对应的错误信息,如把errnum传递给strerror,则可得到此信息。函数原型:#includechar* strerror(int errnum);F. perror函数perror函
57、数输出系统错误信息。函数原型:#include #include void perror(const char *s);如果系统调用失败,一般会设置errno。perror()函数首先打印字符串参数s,然后添加一个冒号和一个空格,然后是对应于errno的错误信息和一个换行符。F. perror函数(续)以下两行等价:perror( “something error”);printf(“something error: %s”,strerror(errno);perror与strerror的比较前者短小精干,完全能应付一般使用。后者可以对错误信息再编辑,可以应用于需要精心设计的场合【例3-22】
58、/*ch3_22.c*/#include #include #include #include #include int main(void)FILE *fp;char* p;errno=0;fp=fopen(notexist,r);if(errno)perror(Open file);/exp/error/errinfo.c【例3-22】(续)errno=0;fp=fopen(notexist,r);if(errno)p=strerror(errno);fprintf(stderr, Open file: %sn, p);return 0;/exp/error/errinfo.c二、系统日志
59、文件Linux的两个系统守护进程klogd和syslogd提供了集中的系统日志功能。klogd供内核和运行在内核空间的程序,特别是设备驱动程序使用。syslogd控制着来自用户空间的消息的产生。日志消息的优先级写入系统日志的消息由它的级别(level)和功能(facility)来控制。级别指出了消息的重要程度和严重性功能告诉syslogd守护进程是哪个程序发出的消息。一条日志消息优先级由级别和功能共同组成syslog的日志级别:level级别严重性LOG_EMERG系统不可用LOG_ALERT要求立刻处理LOG_CRIT重大错误,比如硬盘故障LOG_ERR错误条件LOG_WARNING警告错误
60、LOG_NOTICE正常但重要的消息LOG_INFO纯粹的通报消息LOG_DEBUG调试跟踪输出syslog的功能值:facility功能消息源LOG_AUTHPRIV私有的安全和授权消息LOG_CRON时钟守护进程(crond和atd)LOG_DAEMON其他系统守护进程LOG_KERN内核消息LOG_LOCAL0-7为本地/站点使用而保留LOG_LPR打印机子系统LOG_MAIL邮件子系统LOG_NEWS新闻组子系统LOG_SYSLOGsyslogd产生的内部消息LOG_USER一般用户级消息(默认)LOG_UUCPuucp子系统发送消息的原则选取与消息内容相适应的级别值。对于用户级程序,
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 火花线切割机床项目投资可行性研究分析报告(2024-2030版)
- 中国医用纯化水设备行业发展监测及发展战略规划报告
- 中国汽车平衡块行业市场全景评估及发展战略规划报告
- 中国电容器用薄膜市场调查报告
- 2025年中国气瓶光面环规行业市场发展前景及发展趋势与投资战略研究报告
- 2025-2030年中国全钢中央台项目投资可行性研究分析报告
- 中国川乌头行业市场深度评估及投资战略规划报告
- 中国回光灯市场发展前景预测及投资战略咨询报告
- 早教班老师培训课件
- 同城配送合同
- 高三家长会班主任发言稿课件
- 学前幼儿园-《快乐的小鼹鼠》教学课件设计
- 3停止间转法教案
- 2022-2023学年重庆市合川市三下数学期末学业质量监测模拟试题含解析
- 全过程造价咨询服务实施方案
- 初二生地会考复习资料全
- 里氏硬度法检测钢材强度范围记录表、钢材里氏硬度与抗拉强度范围换算表
- 四川省宜宾市翠屏区中学2022-2023学年数学八年级第二学期期末检测试题含解析
- 2020-2021成都石室联合中学蜀华分校小学数学小升初模拟试卷附答案
- 某冶金机械厂供配电系统设计
- 《在中亚细亚草原上》赏析 课件
评论
0/150
提交评论