第二章 C语言编程实践

时间:2023-12-11 10:37:56

上章回顾

宏定义特点和注意细节 条件编译特点和主要用处 文件包含的路径查询规则 C语言扩展宏定义的用法

第二章

第二章

C语言编程实践 C语言编程实践

预习检查

异或的运算符是什么 宏定义最主要的特点是什么 请列举条件编译三大用法 在文件包含中<> 和“” 有什么区别 如何取得整数register中的第五位值

本章结构

C语言编程实践 C语言编程实践

嵌入式家园 www.embedclub.comC语言编程调试

上海嵌入式家园-开发板商城 http://embedclub.taobao.com/

位运算 位运算

位段 位段

指针特性 指针特性

C语言关键词 C语言关键词

C语言编程要点 C语言编程要点

C语言常犯错误 C语言常犯错误

C语言编程调试

课程目标

了解位运算的实际应用。 了解C语言几个重要关键词的特性和他们之间区别 熟悉C语言编程要点 熟悉C语言常见的语法错误以及解决方法 了解如何去定位错误以及解决错误

                 2 C语言编程实践

位运算

指针特性

C语言关键词 C语言几个以混淆的概念 C语言编程易犯毛病集合

                  2.1 位运算

按位与运算

按位或运算

按位异或运算

求反运算

左移运算

右移运算

                                                       2.1 位运算 位运算符的含义

位运算是指进行二进制位的运算。 功能:

c语言提供对内存单元的二进制位的操作,使得c语言能够编写系统软件.

位运算符

&: 按位与 |: 按位或 ^: 按位异或 ~ : 取反 <<: 左移 >>: 右移

                            2.1 位运算 要点:

位运算除~以外,均为二目运算; 运算对象只能为整型或字符型数据. 各个位运算符号的使用:

                2.1.1按位与运算

按位与──&

格式:x&y 主要用途:取(或保留)1个数的某(些)位,其余各位置0

规则:对应位均为1时才为1,否则为0:3&9=1。 例如,3&9=1: 0011

& 1001

────

0001=1

                2.1.2 按位或运算

按位或──| 格式:x|y

主要用途:参与运算的两数各对应的二进位相或。只要 对应的二个二进位有一个为1时,结果位就为1

规则: :对应位均为0时才为0 ,否则为1:3|9=11 。 例如,3|9=11: 0011

嵌入式家园 www.embedclub.com1011=11

| 1001

────

上海嵌入式家园-开发板商城 http://embedclub.taobao.com/

                 2.1.2 按位异或运算 按位异或──^

格式: x^y 规则:对应位相同时为0,不同时为1:3^9=10 。

主要用途:使1个数的某(些)位翻转(即原来为1的位变为 0,为0的变为1),其余各位不变

例子:

00001001 ^00000101

00001100 (十进制为12) 嵌入式家园 www.embedclub.com

上海嵌入式家园-开发板商城 http://embedclub.taobao.com/

                2.1.3求反运算 求反── ~

格式:~ y 规则:各位翻转,即原来为1的位变成0,原来为0的位

变成1:在IBM-PC机中,~0=0xffff,~9=0xfff6。 主要用途:间接地构造一个数,以增强程序的可移植性

               2.1.4 左移运算

按位左移──<< 格式:x<< 位数

规则:使操作数的各位左移,低位补0,高位溢出: 5<<2=20。

                                        2.1.5 右移运算

按位左移──>>

格式:x>>位数

规则:使操作数的各位右移,移出的低位舍弃;高位:

对无符号数和有符号中的正数,补0; 有符号数中的负数,取决于所使用的系统:补0的称为“逻辑右移”,

补1的称为“算术右移”。例如,20 >> 2=5 说明

x、y和“位数”等操作数,都只能是整型或字符型数据。除按位取反 为单目运算符外,其余均为双目运算符。

参与运算时,操作数x和y,都必须首先转换成二进制形式,然后再执 行相应的按位运算。

例如,5<<2=20:0101 → 10100, 20 >> 2=5:10100 → 00101。

                2.1.6 位运算例子 例2

题目:从键盘上输入1个正整数给int变量num,输出由8~11位构成的数(从低位、0号开始编号)

基本思路:

(1)使变量num右移8位,将8~11位移到低4位上。 (2)构造1个低4位为1、其余各位为0的整数。 (3)与num进行按位与运算。 /*程序功能:输出一个整数中由8~11位构成的数*/

main()

{ int num, mask;

}

printf("Input a integer number: ");

scanf("%d",&num);

num >>= 8; /*右移8位,将8~11位移到低4位上*/

mask = ~ ( ~0 << 4); /*间接构造1个低4位为1、其余各位为0的整数*/ printf("result=0x%x\n", num & mask);

程序运行情况:

程序运行情况:

Input a integer number:1000 ←┘ Input a integer number:1000 ←┘

result=0x3

result=0x3

程序说明:~ ( ~0 << 4) 程序说明:~ ( ~0 << 4)

按位取0的反,为全1;左移4位后,其低4位为0,其余各位为1; 按位取0的反,为全1;左移4位后,其低4位为0,其余各位为1;

嵌入式家园 www.embedclub.com再按位取反,则其低4位为1,其余各位为0。这个整数正是我们所需要

再按位取反,则其低4位为1,其余各位为0。这个整数正是我们所需要 上海嵌入式家园-开发板商城 http://embedclub.taobao.com/

的。

的。

                  2.1.6 位运算例子 例1

题目:从键盘上输入1个正整数给int变量num,按二进制位输出该数。

#include "stdio.h" main()

{

}

int num, mask, i;

printf("Input a integer number: ");

scanf("%d",&num);

mask = 1<<15; /*构造1个最高位为1、其余各位为0的整数(屏蔽字)*/ printf("%d=" , num);

for(i=1; i<=16; i++)

{

} printf("\bB\n");

putchar(num&mask ? ’1’ : ‘0’); num <<= 1;

if( i%4==0 ) putchar(‘,’);

/*输出最高位的值(1/0)*/ /*将次高位移到最高位上*/ /*四位一组,用逗号分开*/

1000=0000,0011,1110,1000B

程序运行情况:

程序运行情况:

Input a integer number:1000

Input a integer number:1000

1000=0000,0011,1110,1000B

               2.3 指针特性 数据指针

函数指针 数组与动态申请

                           2.3.1 数据指针

指针所具有的对绝对地址单元内容的读写能力

指针直接操作内存多发生在如下几种情况

某I/O芯片被定位在CPU的存储空间而非I/O空间,而且寄存器对应于 某特定地址;

两个CPU之间以双端口RAM通信,CPU需要在双端口RAM的特定单 元(称为mail box)书写内容以在对方CPU产生中断;

读取在ROM或FLASH的特定单元所烧录的汉字和英文字模。

                                          2.3.1 数据指针

实例:

unsigned char *p = (unsigned char *)0xF000FF00; *p=11;

数据指针的运算

指针自增自减操作的结果取决于指针指向的数据类别

int *p = (int *)0xF000FF00; p++(或++p) p = p+sizeof(int), p--(或--p)->p = p-sizeof(int)。

                              2.3.2 函数指针 函数指针的三个问题

C语言中函数名直接对应于函数生成的指令代码在内存中的地址,因此函 数名可以直接赋给指向函数的指针;

调用函数实际上等同于“调转指令+参数传递处理+回归位置入栈”,本 质上最核心的操作是将函数生成的目标代码的首地址赋给CPU的PC寄存

器; 因为函数调用的本质是跳转到某一个地址单元的code去执行,所以可以"

调用"一个根本就不存在的函数实体。

             2.3.2 函数指针 函数指针实例

/* 定义一个无参数、无返回类型的 */ typedef void (*lpFunction) ( );

/* 函数指针类型 */

/*定义一个函数指针,指向*/

lpFunction lpReset = (lpFunction)0xF000FFF0;

/* CPU启动后所执行第一条指令的位置 */ lpReset(); /* 调用函数 */

函数指针练习!

                     2.3.3 数组与动态申请 动态申请内存方式可以用较大的数组替换

尽可能的选用数组,数组不能越界访问;

如果使用动态申请,则申请后一定要判断是否申请成功了,并且

malloc和free应成对出现!

                2.4 C语言关键词

const

typedef 与define define 与 enum static

                                                            2.4.1 const

const --只读

说说以下的区别

const int a;

int const a;

const int *a;

int * const a;

int const * a const;

const的优势 关键字const的作用是为给读你代码的人传达非常有用的信息 防止代码被无意的修改

                                                              2.4.1 const

const --只读

说说以下的区别

const int a;

int const a;

const int *a;

int * const a;

int const * a const;

const的优势

关键字const的作用是为给读你代码的人传达非常有用的信息 通过给优化器一些附加的信息,使用关键字const也许能产生更紧凑的代码 防止便利被无意的代码修改

                                                                       2.4.2 typedef 与define #define

预处理指令 在编译预处理时进行简单的替换, 不作正确性检查 不需要再在内存中分配变量空间 调试程序无法检查用#define说明的常量 用#under指令取消

typedef

声明一个新的类型名代替已有的类型名

在编译时处理的

不实际分配内存空间

                                                           2.4.2 typedef 与define

#define实例

#define your_int int * your_int a, b;

typedef例子

typedef int * your_int; your_int a, b

结果

结果

相当于int *a, b; 只是简单的宏替 换

a, b 都为指向int的指针

int *a;

int *a;

int b;

int *b;

                                    2.4.3 define enum #enum 特点

用enum关键字说明的常量由编译程序自动生成,程序员不需要用手 工对常量一一赋值。

用enum关键字说明常量使程序更清晰易读,因为在定义enum常量 的同时也定义了一个枚举类型标识符。

在调试程序时通常可以检查枚举常量。

需要分配内存来存储常量

             2.4.3 define enum

#enum 例子 enum Error_Code

{

OUT_OF_MEMORY, INSUFFICIENT_DISK_SPACE, LOGIC_ERROR, FILE_NOT_FOUND

};

                             2.4.3 define enum #enum 相对#define的优势

使程序更容易维护

使程序更易读

使程序调试起来更方便

                                               2.4.4 static

static的特点

本地的全局变量

限制变量的作用域

设置变量的存储域

static 的作用 在函数体,一个被声明为静态的变量在这一函数被调用过程中维持

其值不变。

在模块内(但在函数体外),一个被声明为静态的变量可以被模块

内所用函数访问,但不能被模块外其它函数访问。

在模块内,一个被声明为静态的函数只可被这一模块内的其它函数

调用

                  2.5 C语言编程要点

C语言存储空间布局

Heapstack

static特性应用

字符串常量 array_name&array_name的异同 强制数据类型转换的总结

                                     2.5.1 C程序存储空间布局 C语言的组成

正文段

初始化数据段(数据段)

非初始化数据段(bss段) 栈

                          2.5.1 C程序存储空间布局 C语言存储布局

| \|/ /|\ |

堆 未初始化 初始化 正文段

                                      2.5.2 Heapstack Heap与stack

Stack的空间由操作系统自动分配/释放,Heap上的空间手动分配/释 放。

Stack空间有限,Heap是很大的*存储区

内存分配域

malloc函数分配的内存空间即在堆上 程序在编译期对变量和函数分配内存都在栈上进行 程序运行过程中函数调用时参数的传递也在栈上进行

                2.5.3 static特性应用

全局静态变量 局部静态变量 静态函数 static实例应用

                                   2.5.3.1全局静态变量

定义:全局变量之前加上关键字static

特性:

内存中的位置:静态存储区

初始化:未经初始化的全局静态变量会被程序自动初始化为

作用域:从文件定义之处开始到文件结尾。

引用模式

引用头文件的方式 extern关键字

                           2.5.3.1全局静态变量 实例分析:

teststatic1.c

//teststatic2.c

void display();

static int n;

extern int n;

/*定义全局静态变量,自 动初始化为0,仅在本文 件中可见*/

void display()

int main() {

n = 20; printf("%d\n",n); display();

return 0;

{

}} 结果:

结果:

文件分别编译通过,但link的时候teststatic1.c中的变量n找 文件分别编译通过,但link的时候teststatic1.c中的变量n

不到定义嵌入式家园 www.embedclub.com不到定义

上海嵌入式家园-开发板商城 http://embedclub.taobao.com/

n++;

printf("%d\n",n);

                                       2.5.3.1全局静态变量 优势

不会被其他文件所访问,修改

其他文件中可以使用相同名字的变量,不会发生冲突。

静态局部变量和普通局部变量的区别

static局部变量只被初始化一次,下一次依据上一次结果值; static函数在内存中只有一份,普通函数在每个被调用中维持一份拷

                                           2.5.3.2 局部静态变量 定义

特征

特征

局部变量之前加上关键字static 。

内存中的位置:静态存储区 初始化:未经初始化的全局静态变量会被程序自动初始化为0

作用域:作用域仍为局部作用域

局部变量改变为静态变量后是改变了它的存储方式即改变了它的生

存期。

全局变量改变为静态变量后是改变了它的作用域, 限制了它的使用 范围

                                       2.5.3.3 静态函数 定义

在函数的返回类型前加上关键字static 特性

优势

只是在声明他的文件当中可见,不能被其他文件所用

其他文件中可以定义相同名字的函数,不会发生冲突

静态函数不能被其他文件所用

                         2.5.3.3 静态函数 实例分析:

}

printf("staticdis() has been called\n"); }

teststatic1.c

//teststatic2.c

void display();

static void staticdis(); int main()

{

void display() {

display(); staticdis(); renturn 0;

printf("display() has been called \n"); }

结果:

结果:

staticdis();

static void staticdis() {

文件分别编译通过,但是连接的时候找不到函数staticdis()的定义 文件分别编译通过,但是连接的时候找不到函数staticdis()的定义

                2.5.3.4 statics实例 例:统计次数功能

void count(); int main()

{

}

int i;

for (i = 1; i <= 3; i++)

count(); return 0;

结果:

}

void count() {

I have been called 1 times.

static num = 0; num++;

printf("I have been called

%d",num,"times\n");

结果:

I have been called 1 times.

I have been called 2 times.

I have been called 2 times.

I have been called 3 times.

I have been called 3 times.

                                             2.5.4 array_name&array_name的异同 array_name

指向数组中第一个元素的指针

&array_name

例:

指向整个数组的指针

char a[MAX];

/*array of MAX characters*/ /*p为指向数组的指针*/

char *p = a;

/*该语句是不正确的,pa的类型为'char *',而&a的类型为 char (*pb)[MAX] = &a; /*该语句是正确的,pb的类型为'char (*)[MAX]'*/

char *pa = &a; 'char (*)[MAX]’*/

                2.5.4 array_name&array_name的异同 使用例子分析

#include<stdio.h> int main()

{

结果:

}

char a[5] = {'a','b','c','d','\0'}; char *p = a;

1245040

/*运行下面这句后, gcc 提示的错误为:cannot convert

from ‘char (*)[5]’ to ‘char *’,&a的类型应该是指向一个数 abcd

组的指针*/

//char *pa = &a; /*所以,应该定义一个指向相同类型和大小的数组的指针

来获得“&a”的值*/

char (*point_to_str)[5];

point_to_str = &a; printf("%x\n%x\n",&p, &point_to_str); printf("%s\n%s\n", p, point_to_str);

结果:1245044 1245044

1245040

abcd

abcd

abcd

                2.5.4 强制数据类型转换 使用例子分析

main() {

}

unsigned a,b; int i,j; a=65535; i=-1;

结果:

j=a;

b=i; printf("(unsigned)%u(int)%d\n",a,j); printf("(int)%d(unsigned)%u\n",i,b);

(unsigned)65535(int)-1 (unsigned)65535(int)-1

结果:

(int)-1(unsigned)65535 (int)-1(unsigned)65535

                  2.5.5 强制数据类型转换的总结 转换规则

说明:

说明:

横向箭头表示必须的转换

double

float

long

unsigned

int

char,short

横向箭头表示必须的转换

纵向箭头表示当运算符两边的运算数为不同类型时的转换

纵向箭头表示当运算符两边的运算数为不同类型时的转换

低-》高 ==》形式上改变, 数据的实质内容 不变

低-》高 ==》形式上改变, 数据的实质内容 不变

高-》低 ==》可能有些数据丢失

高-》低 ==》可能有些数据丢失

                                         2.5.5 强制数据类型转换的总结 转换规则

把赋值运算符右侧表达式的类型转换为左侧变量的类型

转换细节

浮点型与整型 单、双精度浮点型 char型与int型

int型与long型 无符号整数

阶段小节

常见的位运算符有哪些

什么是数据指针

如何定义一个函数指针

const有那些特点,主要应用在哪些方面

static主要有几种用法,各自特点是什么

C强制数据类型转化要注意哪些方面

                      2.6 C语言编程易犯毛病集合

书写标识符时,忽略了大小写字母的区别

忽略了变量的类型,进行了不合法的运算

将字符常量与字符串常量混淆 忽略了“=”与“==”的区别

忘记加分号

多加分号

输入字符的格式与要求不一致 switch语句中漏写break语句 忽视了while和do-while语句在细节上的区别 定义数组时误用变量

              2.6.1书写标识符时,忽略了大小写字母的区别 分析

main() {

int a=5;

printf("%d",A); }

注意:C认为大写字母和小写字母是两个不同的字符

              2.6.2 忽略了变量的类型,进行了不合法的运算 分析

main() {

float a,b;

printf("%d",a%b); }

注意:整型变量a和b可以进行求余运算,而实型变量 则不允许进行“求余”运算

                                       2.6.3 将字符常量与字符串常量混淆

分析

char c; c= "a";

注意:

字符常量是由一对单引号括起来的单个字符 字符串常量是一对双引号括起来的字符序列 以“\0”作字符串结束标志

                                       2.6.3 将字符常量与字符串常量混淆

分析

char c; c= "a";

注意:

字符常量是由一对单引号括起来的单个字符 字符串常量是一对双引号括起来的字符序列 以“\0”作字符串结束标志

                               2.6.4 忽略了“=”“==”的区别

分析

if (a==3) a=b; if (a=3) a=b;

注意:

“=”是赋值运算符 “==”是关系运算符

            2.6.5 忘记加分号 分析

{

}

a=1 b=2

z=a+b; t=z/100; printf("%f",t);

                                  2.6.6 多加分号 分析

}; 注意:

{

z=x+y;

t=z/100; printf("%f",t);

复合语句的花括号后不应再加分号

for (i=0;i<5;i++); 会出现什么问题??? if (a%3==0);

                  2.6.7 switch语句中漏写break语句 分析

switch(grade) {

}

注意:

case 与 break 的配对

printf("85~100\n"); printf("70~84\n"); printf("60~69\n"); printf("<60\n");

case 'A':

case 'B':

case 'C':

case 'D':

default: printf("error\n");

                            2.6.8忽视了whiledo-while语句在细节上的区别

While语句分析

do-while语句分析

main() {

main()

{ int a=0,i;

注意

int a=0,i; scanf("%d",&i); while(i<=10)

{ a=a+i;

scanf("%d",&i); do{

i++; }

a=a+i; i++;

printf("%d",a); }}

} while(i<=10); printf("%d",a);

while循环是先判断后执行,而do-while循环是先执行后判断 do-while至少执行一次

                              2.6.9 定义数组时误用变量

语句分析

注意

int n; scanf("%d",&n); int a[n];

数组名后用方括号括起来的是常量表达式,可以包括常量和符号常

量。

C不允许对数组的大小作动态定义

                         2.6.10 数组最大下标值

语句分析

main() {

}

注意

static int a[10]={1,2,3,4,5,6,7,8,9,10}; printf("%d",a[10]);

数组其下标值由0开始 a数组有N个元素 ,最大下标是N-1

                2.6.12 同时定义了形参和函数中的局部变量

语句分析

语句改正

int max(x,y) int x,y,z;

{

int max(x,y) int x,y;

{

注意

注意

形参应该在函数体外定义

z=x>y?x:y;

int z;

return(z); }

z=x>y?x:y;

形参应该在函数体外定义

局部变量应该在函数体内定义

局部变量应该在函数体内定义

return(z); }

                2.6.13 误认为形参值的改变会影响实参的值

语句分析

语句改正

main () {

main () {

注意

int x=3,y=4; swap(x,y);

printf ("%d,%d",x,y);

int x=3,y=4;

}

int swap (int x,int y) {

swap(p1,p2);

printf ("%d,%d",x,y);

int swap (int *p1,int *p2) int z; {

z=x;x=y;y=z; }

int z;

注意

实参和形参之间单向传递

实嵌参入和式形家参园之www.embedclub.com 嵌入式家园 www.embedclub.com

上海嵌入式家园-开发板商城 http://embedclub.taobao.com/

}

int *p1,*p2;

p1=&x p2=&y;

z=*p1;*p1=*p2;*p2=z; }

                              2.6.14 函数的实参和形参类型不一致 语句分析

main () {

int x=3,y=4;

int *p1,*p2;

错误分析

p1=&x, p2=&y; swap(p1,p2);

printf ("%d,%d",x,y);

C 要求实参与形参的类型一致 P1 --- 指针参数

P1 --- 整形参数

}

int swap (int p1,int p2) {

int z;

z=p1;p1=p2;p2=z; }

                 2.7 C语言的调试

如果我运行的程序挂起了,应该怎么办 如何检测内存漏洞(leak) 调试程序的最好方法是什么 怎样调试TSR程序 能报告条件失败的程序

                                 2.7.1如果我运行的程序挂起了,应该怎么办 程序挂起的四大原因

程序中有死循环;

程序运行的时间比所期望的长;

程序在等待某些输入信息,并且直到输入正确后才会继续

运行;

程序设计的目的就是为了延迟一段时间,或者暂停执行。

                      2.7.1.1死循环

什么死循环

有几种情况可以造成死循环

分析例子:

解决方式

/* initialize a double dimension array */ for(a=0; a<10;++a)

{

}

for(b = 0; b<10; ++a) {

array[a][b]==0; }

增加对应的调试debug信息

嵌入式家园 www.embedclub.com

上海嵌入式家园-开发板商城 http://embedclub.taobao.com/

                    2.7.1.2 运行时间比期望的时间长

什么时候会出现这种情况

该如何消除

分析例子:

/* A subroutine to calculate Fibonacci numbers */ int fib ( int i)

{

}

解决方式

if (i <3) return 1;

else

return fib( i - 1)+fib( i - 2);

熟悉基本的语法或者算法运算信息

              2.7.1.3 等待正确的输入 原因是:等待正确的输入信息

分析例子:

# include <stdio. h> main()

{

FILE *in = fopen("numbers.dat", "r"); int total = 0, n;

while( fscanf( in, " %d" , &n )! =EOF) {

}

total + = n; }

printf( "The total is %d\n" , total); fclose ( in ) ;

                                       2.7.2 如何检测内存漏洞(leak) 什么是内存漏洞

动态分配的内存单元不再使用却没有被释放

什么情况会容易出现这样的问题

忘记释放分配给临时缓冲区的内存空间

内存漏洞特点

最难检测 最危险

             2.7.2 如何检测内存漏洞(leak) 例子分析

void SayHi( char *name ) {

char * UpName;

int a;

UpName = malloc( strlen( name ) +1);

/ * Allocate space for the name * / for( a =0; a<strlen( name ); ++a)

UpName[a] = toupper( name[a]) ; UpName [a] = '\0‘;

printf("Hello, %si\n", UpName ); }

int main() {

SayHi( "Dave" );

嵌入式家园 www.embedclub.comreturn( 0 );

上海嵌入式家园-开发板商城 http://embedclub.taobao.com/

}

                                     2.7.2 如何检测内存漏洞(leak) 解决方案

小心谨慎地编写程序,充分考虑到内存漏洞的可能性 malloc和free要配对

使用相应的软件包

利用语言的扩展功能

                               2.7.3调试程序的最好方法是什么 调试过程的三个要素

应该用什么工具调试一个程序?

用什么办法才能找出程序中的错误?

怎样才能从一开始就避免错误?

                               2.7.3.1应该用什么工具调试一个程序 调试工具功能

观察程序的运行情况

设置断点

设置监视

                 2.7.3.2 用什么办法才能找出程序中的错误 先调试程序中较小的组成部分,然后调试较大

的组成部分

彻底调试好程序的一个组成部分后,再调试下

一个组成部分

连续地观察程序流(flow)和数据的变化 始终打开编译程序警告选项 并试图消除所有

警告

准确地缩小存在错误的范围

                2.7.3.3 如何从一开始就避免错误

程序中应有足够的注释

函数应当简洁

程序流应该清晰,避免使用goto语句和其它跳 转语句

函数名和变量名应具有描述性

                                                               2.7.4 能报告条件失败的程序

assert()命令 :用于Debug调试版本。

用法:assert(表达式) 如果表达式为true,则继续执行下一条语句; 如果表达式为false,则报错并退出程序;

打印失败条件的内容;

打印发生错误的行号;

打印错误所在的源文件名;

使程序以出错状态结束;

NDEBUG宏定义

#define NDEBUG

#include <assert.h>

在包含头文件assert.h之前,定义NDEBUG宏,则禁 用assert宏定义,一般用于Release版本。

             2.7.4 能报告条件失败的程序 assert()例子

void foo() {

}

char * buffer;

buffer = malloc( 10000 ); assert ( buffer != NULL );

float IntFrac(int Num, int Denom ) {

assert( Denom ! = 0 ) ;

return ((float)Num )/((float)Denom); }

阶段小节

C语言常见的错误

如何调试一个死循环程序   调试程序有哪些最好的方法   怎么实现输出调试错误报告

本章总结

C语言编程实践 C语言编程实践

C语言关键词 C语言关键词

了解const,typedef,define以及 static,enum之间的区别和应用

位运算 位运算

位段 位段

指针特性 指针特性

主要讲述数据指针、函数指针

和动态内存申请细节

C语言编程要点 C语言编程要点

重点讲述static关键字的应用以 及类型转换的特点

C语言常犯错误 C语言常犯错误

讲述C语言编译中常见错误分析

嵌入式家园 www.embedclub.com

了解C语言调试的细节 上海嵌入式家园-开发板商城 http://embedclub.taobao.com/

C语言编程调试 C语言编程调试

了解位运算的基本操作,重点

在与和或运算

了解位段的基本定义

实验1 实验内容

题目:运用typedef 和#define分别定义一个结构体指针,同时对这两个结构体进行数据对换操作,并输出 最终数据体的结果。

实验目的

结构体A数据项有char arr[10]; int value; unsigned char ch; 结构体B数据项有char arr[10]; char value; char ch;

熟悉typedef的运用,深入理解typedef和define之间的差别; 理解强制类型转换引起的差别;

结构体指针的运算操作;

实验分析

typedef定义一个结构体,define宏定义一个结构体指针; 结构体的初始化和数组的初始化以及数值类型初始化; 字符串的拷贝操作和数据类型转换操作。

实验2 题目

从键盘上输入1 个正整数给int 变量num,按二进制位输出该数 ,同时分别对第五 位取反,第六位置一,第七位清零

注意事项

键盘输入操作特点

位操作的运算

用宏定义实现位操作

实验结果

CLEAR SETTING REVER

熟悉位操作基本运算,同时为以后嵌入式数据操作提供基础程序编码。

git@github.com:Kevin-Dfg/Data-Structures-and-Algorithm-Analysis-in-C.git