#####################################################
C高级
#####################################################
【1】
宏:__LINE__ __func__ __FILE__ 调试代码
__LINE__ --》行号的宏
__func__ --》函数名的宏
__FILE__ --》文件名的宏
printf("1111111");
printf("2222222");
printf("%s-%s-%d",__FILE__,__func__,__LINE__);
【2】、条件编译
//#define XXXX
#ifdef XXXX //条件编译
代码
#endif
#ifdef XXXX
代码1
#else
代码2;
#endif
#ifndef XXXX
printf("hello TI! \n");
#endif
#ifndef XXXX
printf("hello TI! \n");
#else
代码2;
#endif
#if 0 等价于将代码注释掉
代码
#endif
#define uint31 unsigned int
#define PI 3.14
C语言复习-0
32个关键字集中处理;
1、存储类型
存储类型:4
auto 声明自动变量,缺省时编译器一般默认为 auto
static 声明静态变量
register 声明寄存器变量---皇帝身边的小太监
存寻址访问以提高效率。注意是 尽可能,不是绝对
extern 外部变量声明
面试题:
static关键字和extern关键字的作用?
2、数据类型
数据类型与“模子” 想到数据类型就想到内存
数据类型:7
void char short int
float double long
void,一个神奇的类型
如果函数没有返回值,那么应声明为 void 类型,比如打印类型的函数
void *,强转来进行传参 如果函数的参数可以是任意类型指针,那么应声明其参数为 void *。
注意:无此说法,单存在,void类型的数据默认占有一个字节空间
3、修饰
修饰型:4
signed 声明有符号类型变量
unsigned 声明无符号类型变量
const 声明只读变量
volatile 说明变量在程序执行中可被隐含地改变
我们知道计算机底层只认识 0、1.
任何数据到了底层都会变计算转换成 0、1.那负数(有符号的数)怎么存储呢?
肯定这个“-”号是无法存入内存的,怎么办?很好办,做个标记。
把基本数据类型的最高位腾出来,用来存符号,同时约定如下:
最高位如果是 1,表明这个数是负数,其值为除最高位以外的剩余位的值添上这个“-”号;
如果最高位是 0,表明这个数是正数,其值为除最高位以外的剩余位的值。
面试题:
const关键的作用?
volatile关键字的作用?
4、构造数据类型
构造数据类型: 4
struct 声明结构体变量
union 声明联合数据类型
enum 声明枚举类型
typedef 用以给数据类型取别名(当然还有其他作用)
[1]数组
存储的数据是同一类型
[2]结构体
存储的数据可以是不同类型
[3]共用体
存储的数据可以是不同类型,所有的成员占用是结构体中成员最大的空间
5、控制语句
控制语句:11
if ...else
switch ...case default
do ...while
for
break continue goto
goto(goto少用或禁用,内核例外)
if ... else
连续型特点:范围型
switch 中的表达式可以是:
离散型特点:整型、字符型、或常量表达式或枚举。
注:一般来说 case语句后面的代码尽量不要超过 20 行。
注: 按执行频率排列 case 语句
while 和 do ...while 的差别在于第一次条件是否成立,如果条件不成立,则有区别。
break 语句的作用是跳出当前循环。
如有嵌套的循环,则跳出最近的循环体。
continue:
continue语句的作用是跳出一次循环,----即忽略continue后面的其它语句,紧接着执行下一次循环。
for循环设计扩展:
在多重循环中,如果有可能,应当将最长的循环放在最内层,最短的循环放
在最外层,以减少 CPU 跨切循环层的次数。
优选: 反例:
for(i = 0;i < 5; i ++){ for(j = 0;j < 100; j ++) {
for(j = 0;j < 100; j ++){ for(i = 0;i < 5; i ++){
sum +=a[i][j]; sum +=a[i][j];
} }
} }
不能在 for 循环体内修改循环变量,防止循环失控。
for(i = 0;i < 5; i ++){
sum += i;
i = 100;
}
循环要尽可能的短,要使代码清晰,一目了然。如果你写的一个循环的代码超过一显示屏,那会让读代码的人发狂的。
解决的办法两个:
第一,重新设计这个循环,确认是否这些操作都必须放在这个循环里;
第二,将这些代码改写成一个子函数,循环中只调用这个子函数即可。一般来说循环内的代码不要超过 20行。
把循环嵌套控制在 3 层以内。
国外有研究数据表明,当循环嵌套超过 3 层,程序员对循环的理解能力会极大的降低。
如果你的循环嵌套超过 3 层,建议你重新设计循环或是将循环内的代码改写成一个子函数
6、函数相关
return
7、其他
sizeof() 计算对象所占内存空间大小
【3】.数组(一维) ----- 相同的数据类型的连续存储
问:为什么需要数组?
答:如果有多个相同类型的数据需要连续存储的时候,我们则使用数组;
------------- ---------
下面 重点(地址连续)
char ch1,ch2,ch3;
short s1,s2,s3;
int a1,a2,a3;
datatype a[3];
a[0],a[1],a[2];
||
指针
struct node
union
enum
结构体指针
函数指针
1.数组定义
数据类型 数组名[数组元素的个数];
char a[3];
数组元素的个数: 必须是固定的
....
2.数组成员
例如:
int a[3];
成员:
a[0] a[1] a[2]
问题:如何访问数组成员?
回答:在访问数组成员的时候,实际上数组的首地址 + 成员的偏移量来得到数组成员的地址
--------- --?--
3.数组的首地址 (标识的是一块内存的首地址)
[1]数组名 a
[2]数组的第一个成员地址 &a[0]
问题:数组名是一个什么东东?
回答:数组名它是一个符号(不能修改,也没有内存空间),标识数组的首地址
注意: 数组名不能修改(不能进行++操作):只是一段内存空间首地址的标识
4.数组的初始化 (只能在定义的时候进行,否则需要依次赋值)
-------------------------------------------------------------
示例: ???多少个成员,多少个字节
-------------------------------------------------------------
int a[] = {1,2}; //2个成员,8个字节
int a[10] = {1,2}; //10个成员,40个字节
char b[] = "hello"; //6个成员,6个字节,字符串的结尾是 '\0', b[0]= 'h' b + 1 ,b[1]的地址
char b[10] = {'A','B'}; //字符数组:10个成员,10个字节, b[3] = 0
char b[2] ={'a','b'}; //2个成员,2个字节
特别注意: 数组的初始化一般在内存分配的时候进行赋值,
int a[10];
a[10] = {1,2,3,4,5};//错误
1、在定义的时候初始化 int a[10] = {1,2,3,4,5};
2、手动初始化---遍历
?如何给数组成员赋值及如何访问数组成员:
1、拿到数组的首地址
2、成员对应的数据类型的偏移量
练习1: 定义一个整形数组,有三个成员,第一个成员赋值为10,第二个赋值为20,第三个赋值为30,输出一下。
练习2:一维数组的输入和输出:
将输入和输出封装成函数
int main(int argc, const char *argv[])
{
int a[5];
input_array(&a[0],5);
output_array(a,5);
return 0;
}
5.计算数组成员的个数
数组的大小:本质 整个大小 = (单个体积*数量)
整个大小: sizeof(数组名) : 数组的大小 -----尤其要注意sizeof(array) 这个数组的大小
单个体积: sizeof(第一个成员)
数组个数: sizeof(数组名) / sizeof(第一个成员) : 数组成员的个数
|-----|
| | a[0]
|-----|
| | a[1]
|-----|
| | a[2]
|-----|
定义一个宏:用来表示数组的大小
#define ARRAY_SIZE(a) sizeof(a)/sizeof(a[0])
>>>练习: 搞定数组:
#include <stdio.h>
int main(int argc, const char *argv[])
{
int a[3] = {1,2,3};
//类型分析
printf("a = %p\n",a); //int *
printf("&a[0] = %p\n",&a[0]); //int *
printf("&a = %p\n",&a); //int (*)[3] =--- 数组指针 ===指针指向的是一个有三个int类型的数组
printf("(int)a + 1 = %p\n",(int)a + 1);
printf("a + 1 = %p\n",a + 1); //int *
printf("&a[0] + 1 = %p\n",&a[0] + 1); //int *
printf("&a + 1 = %p\n",&a + 1); //int (*)[3]
return 0;
}
分析:
...
linux@ubuntu:~/day5$ ./a.out
a = 0xbfe8be54.
&a[0] = 0xbfe8be54.
&a = 0xbfe8be54. 注:类型是不一样的
(int)a + 1 = 0xbfe8be55. ()变态强大哥,访问的是地址之后的下一个字节
a + 1 = 0xbfe8be58. (a + 1) - a = 0xbfe8be58 - 0xbfe8be54 = 4 偏移的一个数据类型
&a[0] + 1 = 0xbfe8be58.
&a + 1 = 0xbfe8be60. 0xbfe8be60 - 0xbfe8be54 = 12 = 3 * 4
? 如何验证 &a 是 int (*)[3]类型?(数组指针类型)
int main()
{
int a[3];
printf("&a = %d\n",&a); //argument 2 has type ‘int (*)[3]’
}
warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘int (*)[3]’ [-Wformat=]
printf("&a = %d.\n",&a);
【4】.指针(一级)
面试题:谈谈你对指针的理解?
a = 100;
int *p = &a;
* : 数据类型--》指针类型
int :指针指向的是一个int类型的存储空间中
p :存放的指针指向的内存空间的地址
*p : 指向的内存空间中的内容
指针类型的变量占4个字节空间(32的操作系统)
0.导入:
一级指针 char *p = a;
int a[5] = {1,2,3,4,5};
int *p = a;
a + 3; 往后移动3个数据类型;
问:此时a和p有什么区别
答:
[1]a是一个数组名,它标识数组首地址,不可以改变 (a ++ 错误)
[2]p是一个指针变量,此时p保存的是数组的首地址,在本质上它是一个变量,所以它可以改变(p++ 正确)
总结:
a++; x
p++; √
二级指针 char **p;
1.一级指针变量的数据类型
int *p; 4 p+1 ;移动一个数据类型,4个字节
short *q; 4 p+1 ;移动一个数据类型,2个字节
char *m; 4 p+1 ;移动一个数据类型,1个字节
深刻理解指针的本质:
所有指针变量的大小都是一样,但是指针从一块内存中一次性读写的字节数可能不一样--(取决于指针变量指向的数据类型);
---------------------
结论:32 bit机器指针变量大小就是4byte (因为地址是32bit)
练习:
unsigned int x = 0x12345678;
求x四个字节的累加和(提示:0x12 + 0x34 + 0x56 + 0x78);
int main(int argc,const char *argv[])
{
unsigned int x = 0x12345678;
int i = 0;
int sum = 0;
char *p = &x;
#ifdef METHOD1
for(i = 0;i < 4;i ++){
sum = sum + (x & 0xff);
x >> 8;
}
#elif defined METHOD2
for(i = 0;i < 4;i ++){
sum = sum + p[i];
}
#else
for(p = &x; p < (unsigned char *)(&x + 1); p ++){
sum += *p;
}
#endif
}
unsigned int x = 0x12345678;
4个字节:
12 34 56 78
每次取一个字节,然后累加....
i,j,k 用于循环
m,n 也阔以用于循环
a,b,c
x,y,z 常用于做参数
c,z 常用于做返回值
2.一级指针变量与一维数组
0.导学:
函数传参:
----1、我传递的参数的本质是什么?
2、理清楚传递的参数的类型是什么?
问:为什么需要指针变量?
答:通过指针变量来存放数据的地址,从而操作地址空间对应的数据,
减少大规模的数据拷贝过程,提升效率;
注:指针访问本质上还是访问的数据,但是你要明白我当前的数据是什么。
学习指针:搞清楚两点:
1、我当前的内存在哪里,
全局变量 局部变量 静态变量
2、我内存中的数据是什么
char short int 还是地址(char* short* int*) 结构体类型
====>访问:按照对应的类型访问就行了
--------------------------------------------------------
int a[3];//a[0] a[1] a[2]
数组名是一个符号常量,标示数组的首地址
如何访问数组成员?
数组的首地址 + 成员偏移量
a[i] <=> *(a + i)
int a[3];
int *p = a;
a[i] <=> p[i] <=> *(a + i) <=> *(p + i) //xxxx
问题:指针变量可以当作数组名来使用吗?
回答:可以,指针变量需要保存数组的首地址,指针变量它是一个变量,可以改变自身的值,
而数组名是一个符号常量,不可以改变它的值
练习:
数组输入输出 (四种输出方法),输入输出都封装成函数接口,接口如下:
a[i] <=> p[i] <=> *(a + i) <=> *(p + i) //使用这些方式实现如下接口
int main(int argc, const char *argv[])
{
int a[5];
input_array(a,5);
output_array(a,5);
_output_array(a,5);
__output_array(a,5);
___output_array(&a,5);
//my_output_array(&a,5);
return 0;
}
3.一级指针变量与字符串
字符串:多个字符的集合,最后一个字符是'\0'(ASCII码:0)
char *string = "hello word"; //字符串常量区,是只读的
char buf[] = "hello word"; //有空间
char *str;
char a[10];
*string = 'x';//错误:字符串常量readonly
string ++;//正确
buf[0] = 'x';//正确
buf ++;//错误:数组名是符号常量,不能修改
str = "hello linux";//正确:str保存的是字符串首地址,字符串在常量区
a = "hello linux";//错误:a 是数组名,数组的初始化只能在定义的时候进行
4、吓死人的传参:
本质上传递的都是地址啊,大小是4个字节。
问,, sizeof(p);
sizeof(*(p+100));
#include <stdio.h>
void test(char p[1024])
{
printf("sizeof(p) : %d\n",sizeof(p));
printf("sizeof(*p) : %d\n",sizeof(*p));
}
int main(int argc, const char *argv[])
{
char buf[1024];
test(buf);
return 0;
}
练习:
(1)实现strcpy这个函数
char *my_strcpy(char *dest,const char* src)
{
char *temp = dest;
while(*dest++ = *src++)
;
//栈内存不可以返回
return temp;
}
//如果函数参数只作为输入使用的话,那么为了防止被修改,可以使用const修饰一下。
const 就近原则 (近:?)
const int a = 3; //这两个是一样的,值不可变
int const a = 3;
const int *a = 3; // 值3 不可变,地址a可以变 *a= 是一个值,
int * const a = 3; // 地址:a 不可变,
//栈内存不可以返回
main()
{
char *src = "helloworld";
char dest[1024];
my_strcpy(dest,src,&myargs);
}
5.超级烧脑的面试题练习:
a的首地址是0x20000000
int a[] = {1,2,3,4,5};
int *ptr1 = (int *)((int)a + 1); //本质:指向了下一个字节
int *ptr2 = (int *)(&a + 1); //&a,移动一个类型,整个数组的类型
(1):
int(a)
printf("%#x\n",*ptr1); //0x20000000
printf("%#x\n",ptr2[-1]); //5
(2):
unsigned int a[] = {0x12345678,0x12345678,3,4,5};
printf("%#x\n",*ptr1); //
printf("%#x\n",ptr2[-1]); //
小端存储: 低字节数据存放在低地址,高字节存放在高地址 === x86 ARM(默认) MIPS
unsigned int x = 0x12345678; 数据为从高到低
低地址 0x3000-->---------->-------|>---------高地址-----
| 78 | 56 | 34 | 12 | bf | a1 |...| |
0x3000--->---------->------|----->----高地址-----
0x3000 0x3003
大端存储:低字节存放在高地址,高字节存放在低地址 === POWERPC 网络
低地址 0x3000-->---------->-------|>---------高地址-----
| 12 | 34 | 56 | 78 | bf | a1 |...| |
0x3000--->---------->------|----->----高地址------
0x3003
练习1:
0、验证当前系统的存储方式:
unsigned int x = 0x12345678;
unsigned char *p = &x;
if(*p == 0x78){
printf("little endpoint");
}else{
printf("big endpoint.\n");
}
求解过程分析:
*********************
注意区分大小端的存储:
*********************
内存中的存储:
【5】.二级指针与二维数组
二维数组:多个一维数组组成
int a[3][2];
三个一维数组,每个一维数组两个元素
a[0] : 一维数组的名字
a[0][0] a[0][1]
a[1]
a[1][0] a[1]a[1]
a[2]
a[2][0] a[2][1]
----------------------------------------------------------------------------------
a : 二维数组的数组名,它是符号常量,标示二维数组的首地址, 它是一个数组指针类型
a[0]: 一维数组的数组名,它是符号常量,表示一维数组的首地址,它是普通指针类型
&a[0][0]:获取第一个元素的地址,它是一个普通指针类型
--------------------------------------------------------------------------------
测试:
#include <stdio.h>
int main(int argc, const char *argv[])
{
int a[3][2];
printf("a = %p\n",a);
printf("a[0] = %p\n",a[0]);
printf("&a[0][0] = %p\n",&a[0][0]);
printf("-------------------------\n");
printf("a + 1 = %p\n",a + 1); //移动了一行 ====>一行,2(列) * 4(元素的数据类型int) = 8
printf("a[0] + 1= %p\n",a[0] + 1); //移动了一列的一个元素====>一个元素,4个字节
printf("&a[0][0] + 1 = %p\n",&a[0][0] + 1); //移动了一个 ====>一个元素,4个字节
return 0;
}
分析:
linux@ubuntu:~/day5$ gcc 13_array2.c
linux@ubuntu:~/day5$ ./a.out
a = 0xbf8aaec8.
a[0] = 0xbf8aaec8.
&a[0][0] = 0xbf8aaec8.
a + 1 = 0xbf8aaed0.
a[0] + 1= 0xbf8aaecc.
&a[0][0] + 1= 0xbf8aaecc.
a + 1 - a = 0xbf8aaed0 - 0xbf8aaec8 = 8 //一行 |
a[0] + 1 - a[0] = 0xbf8aaecc - 0xbf8aaec8 = 4 //一列的元素 | ===>类型的大小
&a[0][0] + 1 - &a[0][0] = 0xbf8aaecc - 0xbf8aaec8 = 4 //一行的一个元素 |
int a[3][2]; //二维数组
int (*p)[2]; //数组指针
p = a;
a[i][j] <=> *(a[i] + j) <=> *(*(a + i) + j) <=>p[i][j] <=> *(p[i] + j) <=> *(*(p + i) + j)
----------------------------------------------------------------------------------------------
练习:
int a[2][4] = {1,2,3,4,5,6,7,8};
//输出这个二维数组,用至少三种方式。
输入:
int input_array(int (*p)[4],int n) //这个n常表示行数
{
int i = 0,j = 0;
for(i = 0;i <n;i ++){
for(j = 0;j <4;j ++){
scanf("%d",&p[i][j]);
}
}
}
练习2:二维数组的线性输出测试:
【6】 指针数组
本质:就是一个数组,数组中存放的是地址值,数组成员等价于多个指针变量
int a; //一个整形变量
int a[3]; //一个有三个元素的整形数组
int *p[3];//本质上是数组,数组的成员是指向整形的指针:成员:p[0] p[1] p[2]
p : 数组名,标示是指针数组的首地址,它是int **
p[0] : 数组的成员,它是int *
&p[0] : 获取第一个元素的首地址,它是int **
问:什么时候需要定义指针数组?
答:需要连续存放多个地址的时候 ----连续访问多个连续地址。
练习: 定义一个指针数组,包括三个成员,成员的内容是"hello","world","linux"
#if 0
int main(int argc, const char *argv[])
{
char *p_array[] = {"hello","word","linux",NULL};
show_point_array(p_array);
return 0;
}
#endif
1、实现show_point_array,依次将"hello","world","linux"换行输出
2、实现show_string_char,依次输出"hello"的每一个字母,”world","linux"也是。
扩展:
多级指针:
----------------------------------------------------------------------------
多级指针变量:
<1>一级指针变量保存普通变量的地址
<2>二级指针变量保存一级指针变量地址
<3>三级指针变量保存二级指针变量地址
------------------------------------------------------------------
扩展:---------------------------------------------------------------------------
字符串和字符
[1]字符
'字符' 在内存中存放数据 (ASCII码 [0-127])
'\0' ---- 0
'0' ---- 48
'A' ---- 65
'a' ---- 97
'\n' ---- 10
字符在内存中还是以字符的形式表示。
[2]字符串
本质:
"多个字符" && 特点:一定是有一个结束的字符 '\0'
-----------------------------
字符串以‘\0’结束,以‘\0结束’,以‘\0结束’....
------------------------
练习1:输出ascii码0-9,输出a~f的ascii码
扩展:
1、空指针
如果一个指针不指向任何数据,我们就称之为空指针,用NULL表示
int *p = NULL;
NULL的本质:
#define NULL ((void *)0) (位于stdio.h)
对空指针可读不可写。
if(p == NULL){
}
2、野指针
(1)指针变量没有被初始化。指针变量在创建的同时应当被初始化,
要么将指针设置为NULL。
例如:
----确保指针指向有效的地址
char *p = NULL;
char *str = (char *) malloc(100);
(2)指针p被free之后,没有置为NULL,系统认为这块内存还能被访问。
char *ptr = (char *)malloc(100); //分配内存
......
free(ptr); //释放
ptr = NULL; //指针置空
(3)指针操作超越了变量的作用范围。
char *str = (char *) malloc(5);
strcpy(str, "linux");
野指针的影响:会导致内存泄漏。
【7】.函数
设计/封装规则:
一个函数是完成一个特定功能的代码 ,
调用这个函数的时候,可以给函数传入参数,
同时可以通过函数传出参数,还可以通过函数返回结果。
1.函数的定义(或实现)
数据类型 函数名(数据类型 变量名,....)
{
C语言语句;
}
-----------------------------------------------
注意:
0、一个函数尽可能只实现一个功能;
1、最好通过函数名就可以知道函数的功能,参数同理(通过参数名知道参数是做什么的)
2、尽量避免在参数中使用全局变量,静态变量static等
3、可以给函数传入参数和或借助参数传出结果,且参数个数尽量控制在4个以内
4、如果函数没有返回值,那么应声明为void类型,有则必须按类型返回(必要时可以强转);
5、函数的代码尽可能小于100行(保证代码的可读性)
6、不要返回指向“栈内存”的指针,栈内存在函数体结束时被自动释放
------------------------------------------------
2.函数的声明
***声明是为了告诉编译器(我在外面)
extern 数据类型 函数名(数据类型 变量名,....);
声明是函数的造型[类型](函数类型:函数的返回值类型和函数的参数类型)
3.函数调用
函数名(参数数据或变量名);
不关心函数返回值
4.返回值
变量 = 函数名(参数数据或变量名);
需要获取函数调用结束的时候返回的值
注意:
调用一个函数之前,必须有函数的声明或是已经定义过
---------------------------------------------------------
1、设计一个函数,将一个unsigned int类型数据传递给这个函数,这个函数需要将这个unsigned int类型
的每个字节做累加,然后返回累加的结果.
位操作和指针操作实现;
位操作提示:sum = sum + (x & 0xff);
练习2: 设计一个函数,实现字符串逆置:
//字符串逆置用数组和指针两种方式实现
作业:
作业:
<1>设计一个函数 find_max_min_chr(p_arry,&max,&min); 寻找最大和最小的字符
char *p_arry[] = {"hello","world","linux",NULL};
作业:
练习(可选---全部实现):
<1>写一个函数;strchr()用来找出参数s字符串中第一个出现的参数c地址,
<1>设计一个函数,计算字符串长度
<2>设计一个函数,完成字符串拷贝和字符串前n个字符拷贝(清空目标区)
<2>设计一个函数,完成字符串比较和字符串前n个字符比较
<2>设计一个函数,完成字符串追加和字符串前n个字符追加
---------------------------------------------------------------------------------
【7】 函数指针 (重要:linux驱动开发)
类型 + 变量名或数组名
int a;
int a[3];
int *a[3];
int (*)[3] //数组指针类型
int myadd(int ,int) int (*) (int ,int ) //函数指针类型
二 函数指针类型变量
问:什么时候需要用到函数指针变量?
答:用来存放函数入口地址(函数名即函数入口地址),便于通过函数指针调用这个函数(回调)
问:为什么不直接调用函数,而需要通过函数指针来调用?
答:在一个软件设计中,为了方便别人使用我们的代码;
在我们给别人提供接口的时候,并不清楚别人具体的函数名是什么的,
那为了避免使用者在提供函数接口时候的随意性,我们通过函数指针类型来限定使用者的函数类型。
1.定义
根据函数的类型来定义
int add(int a,int b);
void test1(char *,int a);
void test2(void);
int (*p_fun)(int ,int);
void (*p_fun)(char *,int);
void (*p_fun)(void);
扩展:
.快速完成函数指针的定义:
以mystrcpy函数和add函数为例:
char *mystrcpy(char* dest,const char* src)
int add(int a,int b)
a、这是两个函数,那么函数的类型是什么呢?
答:去掉函数名,去掉参数名,就得到了函数类型
char * (char* ,const char* )
int (int ,int )
b、定义函数指针
答:指针替换掉原来的函数名 :
等价于: *p=mystrcpy *q = add
c、得到函数指针
char * (*p)(char* ,const char* )
int (*q)(int ,int )
d、调用
p=mystrcpy
p(dest,src);
q = add
q(a,b);
练习1: 使用函数指针调用mystrcpy和add函数,并输出结果
练习2: 函数指针的高级使用
#if 0
#include <stdio.h>
int add(int a,int b)
{
return (a + b);
}
int sub(int a,int b)
{
return (a - b);
}
int calc(int a,int b,int (*pfun)(int ,int ))
{
int ret;
ret = pfun(a,b);
return ret;
}
int main(int argc, const char *argv[])
{
printf("10 + 20 = %d\n",calc(10,20,add));
printf("10 - 20 = %d\n",calc(10,20,sub));
return 0;
}
#endif
扩展学习:
函数指针数组
int main(int argc, const char *argv[])
{
int (*p[2])(int ,int);
printf("10 + 20 = %d\n",calc(10,20,add));
printf("10 - 20 = %d\n",calc(10,20,sub));
p[0] = add;
p[1] = sub;
printf("10 + 20 = %d\n",p[0](10,20));
printf("10 - 20 = %d\n",p[1](10,20));
return 0;
}
扩展:实现:
练习3: 遍历函数指针数组,依次完成加减乘除的操作。
提示:
void calc_function_pointer(int a,int b,int (**q)(int ,int))
{
for( ; *q != NULL;*q++)
{
printf("%d\n",(*q)(a,b));
}
}
//定义函数指针数组,实现一个计算器
int (*q[])(int ,int) = {add,sub,mul,div,NULL};
调用:
calc_function_pointer(10,20,q);