项目开发日报表 | |
项目名称 | 苏嵌实训—嵌入式LinuxC第五天 |
今日进度以及任务 | 1:总结今日内容 2:复习链表(单链表) |
本日任务完成情况 | 1:控制语句 if语句 1表达式 (1)条件表达式,一般为逻辑表达式或关系表达式,但也可以是任何数值类型,如整型、实型、字符型、指针型数据等。 (2)语句,由于是C语言的语句,而不是表达式,故最后需要加分号“;“, (3)当if与else配对使用时,else总是与它上面的最近的未配对的if配对。故一般需要大括号 switch语句 1表达式 switch(表达式) { case 常量表达式1:语句1 case 常量表达式2:语句2 ...... default: 语句N+1 } (1)表达式,ANSI标准允许它为任何类型 (2)常量表达式只是起到标号的作用,并不是在该处进行条件判断;即只要进行,它会一直执行其下的语句,直到遇到break,退出switch; (3)每一个case的常量表达式的值必须互不相同,否则就会出现互相矛盾的现象; for语句 for(表达式1;表达式2;表达式3)语句 for(循环变量赋初值;循环条件;循环变量增值)语句 表达式1; while 表达式2 { 语句 表达式1; } 1)表达式1可以省略,即在for语句前进行赋初值; (2)表达式2如果省略,则为一个死循环; (3)表达式3也可以省略,但一定要将其放到语句中; (4)表达式1主要用来赋初值,故也可以为一个逗号表达式(其按自左到右执行,中间用逗号隔开,最后的值为最右的那个);
while语句 while(表达式)语句 do 循环体语句 while(表达式);
break和continue 1break表示终止整个循环的执行,continue只表示结束本次循环; 2break不能用于循环语句和switch语句之外的任何其他语句中 预处理
__LINE__ //当前被编译代码的行号; __STDC__//判断当前编译器是否为标准C,若其值为1表示符号标准C,否则不是标 准C; 1、用于调用和从函数返回的代码很可能比实际执行这个小型计算工作的 代价更大,所以使用宏比使用函数在程序的规模和速度更胜一筹。 2、但是更重要的是,函数的参数必须申明为一种特定的类型,所以它只 能在类型合适的表达式上使用。 反之,上面的宏可以用于整形、长整形、单浮点型、双浮点型以及其他任 何可以使用>操作符比较值大小的类型。 序中,除非宏非常短,否则使用宏可能会大幅度增加程序的长度。 编译链接的整个过程: #define TEXT 2 宏定义指令#define用来定义一个标识符和一个字符串,以这个标识符来代表这个字符串。在程序中每次遇到这个标识符时就用所定义的字符串来替换它; 2、头文件展开: #include<stdio.h> 一般情况下:>如果调用库函数用#include命令来包含相关的头文件,则用尖括号,可以节省查找的时间;如果要包含自己编写的文件,用双撇号,它通常是在当前目录中。 3、去除注释: 将已经注释掉的语句删除。 4、条件编译: 预处理中包含了条件编译,条件编译顾名思义就是源程序中的代码其中一部分内容在满足一定条件时才进行编译。 #if...#endif和#if...#elif...#else...#endif 指针 指针类型 1.普通指针 既然指针是一个32位的数字,那就应该属于“int”类型啊,那怎么会有很多种指针类型呢?这其实是C语言编译器的聪明之处,它把指针这个32位的地址数据,做了一个上层的抽象优化,它使得用指针的人在编程应用的时候变得非常的方便。 比如,现在有个10个数字,它们的数据范围都在0-255范围内,那我们只需分配10个字节的内存单元就可以了。如果从内存0x0000_1000地址开始,那结束地址应该是0x0000_1009。那我现在如果要得到第n个数字的内存地址,计算方法是:0x0000_1000+n。 但是,如果这10个数字是int类型的,那么我们就需要4*10个字节来存放,现在要得到第n个数字的内存地址,计算方法是:0x0000_1000+n*4。 进一步,如果这10个存储的对象是自定义数据类型,每个对象的长度是m字节,现在要得到第n个数字的内存地址,计算方法是:0x0000_1000+n*m。 这样一来,计算过程就会很复杂,因为你必须时刻要搞清楚每个储存对象的类型是什么,才能知道每单元存储对象的字节空间。 所以,C语言就干脆规定:你必须先定义好存储对象的数据类型是什么?也即用一段连续的内存是用来存储的是什么数据类型,那么这段内存地址就叫该数据类型的指针。比如前面的10个对象,我们首先定义它们存储的对象是int类型,首地址用指针来定义就是: int *p=(int *) 0x0000_1000; 那现在要得到第n个对象的内存地址,就不用再考虑对象每个单元的长度了,直接计算出: p+n。 而且每当出现类似p++的时候,p的值就不是简单+1,而是 p=p+sizeof(指向的数据类型)。 稍微解释下,就是定义某种类型指针的固定方法是:在此数据类型变量定义方法定义出来的变量前面加一个标记*号即可。比如定义一个int类型数据类型变量是: int p;那现在只需要在变量p前面加一个*就OK。 接着说:(int *) 0x0000_1000; 由于C语言规定,变量赋值必须要保证是相同类型。因此p已经定定义成了指针,那么等号右边的也必须是同等类型的变量。由于0x0000_1000是个立即数,它不属于任何类型的变量,因此我们首先必须把它进行强制转换。强制转换成指向int型的指针需要用:(int *) 。 所以当你再次看到这样初始化指针的时候,就不会感到奇怪和陌生了: struct mydefine *point=(struct mydefine *) address; mydefine是我们自定义的一种数据类型,指向它的指针定义和初始化与前面的int型指针并无差异。 2.函数指针 如果说普通变量型指针是内存地址还可以理解的话,那指向函数的指针怎么理解呢?难道函数也会有内存地址吗?是的!我们来看一下C语言程序的运行过程就明白了。 可以看到,C语言函数调用在经过汇编之后,就变成了过程调用,汇编程序中每个过程都会有一个标号,具体到上面的汇编程序就是:call 函数标号(add)。而这个函数标号最终在生产机器代码的时候,其实就是一个偏移值。所以C语言的函数指针就相当于汇编程序中的标号偏移,每个C函数经链接程序生成完整的机器代码后,对应的标号偏移其实就是一个逻辑意义上个的内存地址(它是一个相对偏移量的内存地址而不是绝对物理地址),可以表征函数放置在内存中的位置。 函数指针的定义比较特别:函数返回值类型 (* 指针变量名) (函数参数列表);它其实就是将普通“函数声明”中的“函数名”改成“(*指针变量名)”。 如 int (*p)(int, int); 代表含义:定义了一个指针变量 p,该指针变量可以指向返回值类型为 int 型,且有两个整型参数的函数。p 的类型为 int (*)(int,int)。 当定义了这个函数指针p之后,就可以用一个对应的真实函数来对它进行赋值,那么这个p就将取得这个真实函数的首地址,进而指向了具体的函数,后面对这个函数的任何调用都可以通过*p()来运行。 那么,究竟函数指针有什么用呢?一般初级编程者很少用到。函数指针由于指向的是函数,因此它最大的应用之处在于可以作为函数的参数:也即一个把函数本身作为目标函数的参数。 比如,我们有这样一个需求:写出一个函数destfunc,要求是当某值select=1时需执行函数 我们可以进一步优化这种写法,即把函数本身当成参数,由于函数本身是一长串可执行代码,因此要把它做为参数,就只有用各自的指针来引用:
函数指针对于功能相近、但是有细微差别的函数区分调用十分有用。下面我们来看一个非常经典的案例:根据函数指针变量 设计如下函数: 其中,fun_t是一个函数指针,其定义为: 该函数指针 最终,函数指针变量operation调用不同函数的实现方法如下,可以看到函数名本身就是一个指向自己的指针。 实现运算的4个函数很简单,如下。这些函数就具备功能相近、但是有细微差别的特征。 3.指针函数 既然指针是一种数据类型,那么就可以用函数来对它进行返回,包括上面的指向函数的指针。指针函数的定义格式就相当简单了,和指针变量一样,就是在普通函数基础上加一个*符号 (三) 指针数组 指针只是一种数据类型而已,C语言中同种类型的数据就可以组成数组。而数组在内存中和上面的链表不同,它是连续分配空间的: 定义指针数组也是在传统数组的基础上加一个*符号: 例:int *p[10]; |
本日开发中出现的问题总汇 | 无参数的宏 其定义格式如下: #define 宏名 字符串 在以上宏定义语句中,各部分的含义如下: ● #:表示这是一条预处理命令(凡是以“#”开始的均为预处理命令)。 误解指针的用法,将值当做地址赋给指针 |
本日未解决问题 |
|
本日开发收获 | 指针就是内存地址! |
其他 | 上手实践还是容易出错 |