计算机的内存长什么样子:
1、计算机的内存就像是一叠非常厚的"便签",一张便签就相当于一个字节的内存,一个字节有8个二进制位。
2、每一张"便签"的都有自然排序形成的一个编号,计算机根据便签的编号访问、使用"便签"。
3、CPU会有若干个金手指,每根金手指能感知高低两种电流,低电流当作二进制的0,高电流当作二进制的1,我们所说的32位的CPU,指的是CPU有32个金手指用于感知便签的编号:
便签的最小编号 00000000000000000000000000000000 = 0 便签的最大编号 11111111111111111111111111111111 = 4294967295 所以32位的CPU最多能使用 4294967296byte->4194304kb->4096mb->4gb
4、便签的编号也就是内存的地址,是一种无符号整数。(unsigned int\long)
什么是指针:
1、指针(pointer)是一种特殊的数据类型,使用它可以定义指针变量,简称指针。
2、指针变量中存储的是内存的地址,是一种无符号的整数。
3、通过指针变量中记录的内存地址,我们可以读取内存中所存储的数据,也可以向内存中写入数据。
4、一般使用%p以十六进制格式显示内存地址。
如何使用指针:
定义指针变量:
类型* 指针变量名;
int* xxx_p;
1、指针变量中只记录了内存中某个字节的内存地址,当我们把它当做一块连续内存块的首地址使用时,当使用指针变量访问内存时可以连续往后访问多个字节,具体连续访问多少个字节由定义该指针变量时它的类型决定,并无法修改
char* p; // 访问1字节
int* p; // 访问4字节
long* p1; // 访问4/8字节
*(char*)p1 // 访问1字节
*p1 // 依然访问4/8字节
2、普通变量与指针变量的用法有所不同,为了避免混用,一般指针变量以p结尾
3、指针变量不能连续定义,一个*只能定义一个指针变量
int n1,n2,n3; //都是int类型
int *p1,p2,p3; //p1是int* 类型 p2 p3是int
int *p1,*p2,*p3;//都是int*类型
4、与普通变量相同的是,指针变量的默认值是随机的(野指针),为了安全起见,要给指针变量赋初始值,如果不知道该赋什么值,那么可以初始化为NULL(空指针)
int* p = NULL;
5、指针变量占用的内存是 4(32位系统)|8(64位系统)字节
给指针变量赋值:
指针变量 = 内存地址。
所谓的给指针变量赋值,就是给其存储一个内存地址
必须给指针变量赋一个有效地址,如果赋值的是非法地址,可能导致后续对该内存解引用时发生段错误
// 存储栈、data、bss内存段的地址
int num = 10;
int* p = # // &运算符 计算出变量的地址
// 注意:类型要匹配
// 存储堆内存地址
int* p = malloc(4); // 把堆内存申请出来的地址赋值给p
指针变量解引用:
*指针变量
给指针变量赋值就是让指针变量引用某块内存,解引用就是根据指针变量中存储的内存地址,访问对应的内存中的数据,具体连续访问多少个字节由定义指针变量时的类型决定
int num = 88;
// 定义指针变量
int* p = NULL;
// 给指针变量赋值
p = #
*p = 99;
num = 77;
// 对指针变量解引用
printf("%d %d %p %p\n",*p,num,p,&num);
return 0;
说明指针变量中存储的就是无符号整型
#include <stdio.h>
void func(unsigned long addr)
{
*(int*)addr = 99;
}
int main(int argc,const char* argv[])
{
int num = 88;
func(&num);
printf("%d\n",num);
return 0;
}
为什么要使用指针:
1、函数之间需要共享普通变量
函数之间的命名空间是相互独立的,普通变量的传参是单向值传递(拷贝),所以通过传参无法解决普通变量共享的问题
全局变量可以在函数之间共享,但是过多地使用全局变量容易造成命名冲突和内存浪费
可以使用数组能够“址传递”,可以共享,但是使用麻烦并且额外传递数组的长度
综上原因,可以使用指针解决函数之间普通变量共享的问题,虽然函数之间命名空间是独立的,但是使用的内存地址编号是同一套
int Num = 0;
void func(int num)
{
printf("func:%d\n",num);
num = 88;
printf("func:%d\n",num);
}
void func1(int arr[],int len)
{
Num = 77;
arr[0] = 88;
}
void func2(int* p)
{
printf("func2:%p\n",p);
*p = 44;
}
int main(int argc,const char* argv[])
{
int num = 10;
printf("main:%d %p\n",num,&num);
func2(&num);
printf("main:%d\n",num);
/*
//func(num);
int arr[1] = {10};
printf("main:%d\n",arr[0]);
func1(arr,1);
printf("main:%d\n",arr[0]);
*/
// p输出型参数
int func3(int* p)
{
*p = 99;
return 88;
}
int main(int argc,const char* argv[])
{
int num = 10;
printf("main:%d %p\n",num,&num);
func2(&num);
printf("main:%d\n",num);
int ret1 = 0;
int ret2 = func3(&ret1);
printf("ret1=%d ret2=%d\n",ret1,ret2);
2、使用指针变量可以提高函数的传参效率
函数是以赋值的方式来传参的,就是内存的拷贝,当把一个变量传参给另一个函数时,如果该变量的字节数比较大时,传参效率就比较低,而如果传递该变量的地址,只需要拷贝4|8字节即可,可以提高传参效率
#include <stdio.h>
//void func(long double d)
void func(long double* d)
{
}
int main(int argc,const char* argv[])
{
long double d = 3.14;
for(int i=0; i<1000000000; i++)
{
func(&d);
}
return 0;
}
3、使用堆内存时必须与指针变量配合
堆内存无法取名字,标准库、操作系统提供的内存分配接口函数的返回值都是内存地址,因此只能通过指针才能使用堆内存
int* p = malloc(4);
realloc()\calloc()\free()
注意:由于指针变量具有一定的危险性,使用不当容易发生段错误,所以除了以上三种情况外,都不要轻易使用指针
使用指针要注意的问题:
空指针:
如果指针变量中存储的是NULL,该指针称为空指针,如果对空指针解引用必定段错误
作用:
1、用于指针初始化
2、如果函数的返回值类型是指针类型时,当函数执行错误时,可以通过发返NULL表示函数执行错误,作为错误标志存在
int* func(void)
{
if(条件)
{
return NULL; //执行错误
}
}
如何避免空指针产生的段错误?
对来历不明的指针在解引用之前,先判断是否是空指针,再解引用:
1、当你写的函数的参数是指针类型,需要先判断再使用,因为别人调用该函数时,可能会传递空指针
2、当你使用别人写的函数,该函数的返回值是指针类型时,可能会返回空指针,所以使用前也需要先判断
int* p = malloc(4);
if(NULL == p)
{
printf("malloc error\n");
return 0 ;
}
*p = 88;
if(NULL == p) // 正确写法
if(p == NULL) // 容易变成赋值语句
if(!p) // 在大多数系统中NULL就是0,在极个别系统中NULL是1
NULL是定义在stdio.h中的一个宏,要使用NULL的话必须导入stdio.h
野指针:
当指针变量中存储的地址,无法确定具体是哪个合法内存时,该指针就称为野指针
int num;
int* p = #
p = NULL;
int* p1; //野指针
对野指针解引用的后果:
1、一切正常,野指针刚好指向空闲的有效的内存
2、段错误,指向非法内存
3、脏数据,存储的是其它数据的内存
野指针的危害比空指针要更大
1、一旦产生野指针,就无法判断出来,但是空指针可以被判断出来
2、野指针解引用的后果,问题可能是隐藏型的,有可能后面才会暴露
如何避免野指针的危害?
所有的野指针都是程序员制造的,如果严格遵循规则,不制造野指针就不会有危害
如何避免产生野指针?
1、定义指针变量时一定要初始化,要么给合法地址、要么给NULL
2、不要返回局部变量、块变量的地址,当函数执行结束后,局部变量、块变量就被销毁了,所以返回他们的地址,去访问该内存没有任何意义
#include <stdio.h>
int* func(void)
{
int num = 10;
int* p = #
return p;
}
int main(int argc,const char* argv[])
{
int* p = func();
printf("%d\n",*p);
printf("heheheh\n");
printf("%d\n",*p);
return 0;
}
3、当使用堆内存时,如果指向的堆内存已经被释放了,该指针要及时置空
int* p = malloc(4);
*p = 100;
free(p); // 释放了4个字节的堆内存 p的指向没有改变
p = NULL;
练习:
1、实现一个函数,用于交换两个int变量的值。并调用它实现一个数组排序函数
void swap(int* p1,int* p2)
{
int temp = *p1;
*p1 = *p2;
*p2 = temp;
}
void sort(int arr[],int len)
{
for(int i=0; i<len-1; i++)
{
for(int j=i+1; j<len; j++)
{
if(arr[i] > arr[j])
{
swap(&arr[i],&arr[j]);
}
}
}
}
int main(int argc,const char* argv[])
{
int n1 = 10,n2 = 20;
swap(&n1,&n2);
printf("%d %d\n",n1,n2);
int arr[] = {4,6,23,45,6,7,2,45,7,8};
sort(arr,sizeof(arr)/sizeof(arr[0]));
for(int i=0; i<sizeof(arr)/sizeof(arr[0]); i++)
{
printf("%d ",arr[i]);
}
return 0;
}
2、实现一个函数,用于计算两整数最大公约数和最小公倍数。
#include <stdio.h>
int max_min_num(int n1,int n2,int* min)
{
// 计算最大公约数
int max = 1;
for(int i=2; i<=n1; i++)
{
if(0 == n1%i && 0 == n2%i)
{
max = i;
}
}
// 计算最小公倍数
for(int i=n1*n2; i>=n1; i--)
{
if(0 == i%n1 && 0 == i%n2)
{
*min = i;
}
}
return max;
}
int main(int argc,const char* argv[])
{
int n1,n2;
scanf("%d %d",&n1,&n2);
int min = 0;
int max = max_min_num(n1,n2,&min);
printf("max:%d min:%d\n",max,min);
return 0;
}
指针的进步值与指针的运算:
指针变量里面存储的是整数,代表着内存的编号(每个整数都对应着一字节的内存)。
指针的进步值:
指针变量中存储的其实是一个内存块的首地址,内存块的具体大小由指针变量的类型决定,当使用指针变量解引用访问内存时,实际访问的内存字节数叫做指针变量的进步值,也就是指针变量+1后的内存地址的变化。
int main(int argc,const char* argv[])
{
char* p1 = NULL;
short* p2 = NULL;
int* p3 = NULL;
long long* p4 = NULL;
long double* p5 = NULL;
printf("%p %p %p %p %p\n",p1,p2,p3,p4,p5);
// printf("%lu %lu %lu %lu %d\n",p1,p2,p3,p4,p5);
printf("%p %p %p %p %p\n",p1+1,p2+1,p3+2,p4+3,p5+1);
return 0;
}
指针的运算:
指针变量存储就是是整数,理论上整数能使用的运算符,指针变量都可以使用,但只有以下运算才有意义:
指针+n = 指针所代表的整数+进步值*n 指针-n = 指针所代表的整数-进步值*n 指针1-指针2 = (指针1所代表的整数-指针2所代表的整数)/进步值
指针加减整数,就相当于以指针变量的进步值为单位前后移动,指针-指针可以计算出两个指针变量之间相隔多少个元素。
注意:指针+-n之后得到的依然是指针,指针-指针得到整数
#include <stdio.h>
int main(int argc,const char* argv[])
{
int num = 10;
int* p = #
printf("%p %p %p\n",p,p+3,p-1);
p += 10;
printf("%p\n",p);
int* p1 = #
printf("%d\n",p - p1);
int arr[10] = {1,2,3,40,5,6,7,8,9,10};
int* p3 = &arr[2];
int* p4 = &arr[7];
printf("%d\n",*(p3+4));
printf("%d\n",p4-p3);
return 0;
}
注意:
指针-指针运算,它们的类型必须相同,否则编译器会报错。
数组名与指针:
数组名就是指针:
1、数组名就是数组内存块的首地址,它是个常量地址(特殊的指针),所以它作函数的参数时,才能蜕变成指针变量。
#include <stdio.h>
void show_arr(int* arr,int len)
{
for(int i=0; i<len; i++)
{
printf("%d ",arr[i]);
//printf("%d ",*(arr+i));
}
}
int main(int argc,const char* argv[])
{
int arr[10] = {1,2,3,4,5,6,7,8,8,1};
printf("%p %p\n",arr,&arr[0]);
show_arr(arr,10);
return 0;
}
2、指针变量可以使用[]解引用,数组名也可以*遍历,它们是等价的。
int arr[10] = {1,2,3,4,5,6,7,8,8,1};
printf("%p %p\n",arr,&arr[0]);
int* p = arr;
show_arr(arr,10);
printf("\n");
for(int i=0; i<10; i++)
{
// printf("%d ",*(arr+i));
// printf("%d ",*(p+i));
printf("%d ",p[i]);
}
return 0;
注意:如果定义<TYPE> arr[n]数组,数组名arr 就是 TYPE*类型的地址。
int arr1[10]; // arr1是int*类型
double arr2[10]; // arr2是double*类型
char arr3[10]; // arr3是char*类型
数组名与指针的相同点:
1、它们都是地址
2、它们都使用[],*去访问一块连续的内存
数组名与指针的不同点:
1、数组名是常量,而指针是变量
2、指针变量有它自己的存储空间,而数组名就是地址,它没有存储地址的内存。
3、指针变量与它的目标内存是指向关系,而数组名与它的目标内存是映射关系。
通用指针:(万能指针)
一些具备通用性的操作函数,它们的参数可能是任意类型的指针,但编译器规定不同类型的指针不能进行赋值,为了兼容各种类型的指针,C语言中设计了void类型的指针,它能与任意类型的指针互相转换,它能解决不同类型的指针参数的兼容性问题。
void* p1 = NULL;
// void* 可以给任意类型的指针变量赋值
int* p2 = p1;
// 任意类型的指针可以给void*类型的指针赋值
void* p3 = p2;
通用操作的函数:
void bzero(void *s, size_t n);
功能:把内存块s的n个字节,赋值为0。
void *memset(void *s, int c, size_t n);
功能:把内存块s的n个字节,赋值为c(0~255)
返回值:就是s,为了链式调用
void *memcpy(void *dest, const void *src, size_t n);
功能:从src内存块拷贝n个字节的内容到dest内存块
返回值:就是dest,为了链式调用
int memcmp(const void *s1, const void *s2, size_t n);
功能:比较s1和s2内存块的n个字节,每个字节比较,一旦比较出结果立即返回
s1 > s2 返回1
s1 < s2 返回-1
s1 == s2 返回0
注意:
void类型的指针变量的进步值是1。
void类型的指针变量不能解引用 ,必须转换成其它类型的指针才能解引用。
int main(int argc,const char* argv[])
{
char arr[10] = {2,4,5,76,4,32,34,5,6};
char arr1[10] = {1,1,1,1,1,1,1,1,1,1};
void* p = arr;
printf("%d\n",*(char*)p);
//bzero(arr,sizeof(double)*3);
memset(arr,1,10);
printf("memcmp:%d\n",memcmp(arr,arr1,10));
memcpy(arr,arr1,10);
for(int i=0; i<10; i++)
{
printf("%hhd ",arr[i]);
}
return 0;
}
练习3:实现一个 交换任意类型的两个变量的 函数。
#include <string.h>
void swap(void* p1,void* p2,size_t n)
{
if(NULL == p1 || NULL == p2 || 0 == n)
return;
char temp[n];
memcpy(temp,p1,n);
memcpy(p1,p2,n);
memcpy(p2,temp,n);
}
int main(int argc,const char* argv[])
{
double n1 = 10,n2 = 20;
swap(&n1,&n2,sizeof(n1));
printf("%lf %lf\n",n1,n2);
return 0;
}
练习4:实现自定义的bzero、memset、memcpy、memcmp函数
#include <stdio.h>
void my_bzero(void* s,size_t n)
{
if(NULL == s || 0 == n)
return;
for(int i=0; i<n; i++)
{
//s[i] = 0;
*((char*)s+i) = 0;
}
}
void* my_memset(void* s,int c,size_t n)
{
if(NULL == s || 0 == n) return NULL;
for(int i=0; i<n; i++)
{
*((char*)s+i) = c;
}
return s;
}
void* my_memcpy(void* dest,const void* src,size_t n)
{
if(NULL == dest || NULL == src || 0 == n)
return NULL;
for(int i=0; i<n; i++)
{
*(char*)(dest+i) = *(char*)(src+i);
}
return dest;
}
int my_memcmp(const void* s1,const void* s2,size_t n)
{
if(NULL == s1 || NULL == s2 || 0 == n)
return 0xffffffff;
char* p1 = (char*)s1;
char* p2 = (char*)s2;
for(int i=0; i<n; i++)
{
if(p1[i] > p2[i]) return 1;
if(p1[i] < p2[i]) return -1;
}
return 0;
}
int main(int argc,const char* argv[])
{
int n1 = 10,n2 = 20,n3 = 100;
my_memset(&n1,1,4);
printf("%d\n",n1);
my_memcpy(&n2,&n1,4);
printf("%d\n",n2);
printf("%d\n",my_memcmp(&n1,&n3,4));
return 0;
}
int main(int argc,const char* argv[])
{
double arr[10] = {24,3,6,6,34,5,6,45,43};
my_bzero(arr,80);
for(int i=0; i<10; i++)
{
printf("%lf ",arr[i]);
}
return 0;
}
const与指针:
就近原则 只需要关注const右边紧跟着的是* 还 p
const int* p;
功能:保护指针变量所指向的内存不被*p修改,或者说不能对 *p 赋值
int const *p;
功能:同上
int* p1 = p; // 编译时会有警告
由于指针的使用存在一定的风险,所以函数的参数只要是指针,并且函数没有修改指针所指向的内存的需求,我们就应该给指针变量加上const 类型* 指针变量。
void func(const double* d)
{
//*d = 10; // 防止d的内存被修改导致死循环
}
int main()
{
for(double i = 0; i<100000000; i++)
{
func(&i);
}
return 0;
}
int * const p;
功能:保护指针变量p不被修改
const int * const p;
功能:既保存指针变量p不被修改,也保护指针变量指向的内存*p不被修改
int const * const p;
功能:同上
const int num;
// 指向const修饰的变量时,指针变量要用const修改,否则编译会有警告
const int* p = #
当使用数组作函数的参数时,数组就蜕变成了指针变量,为了防止指针改变指向导致数组无法使用,理论上我们应该使用 类型* const 指针变量 防止指针变量改变指向。
void show_arr(int* const arr,int len)
{
printf("%p\n",arr);
arr = NULL;
printf("%p\n",arr);
}
int main(int argc,const char* argv[])
{
int arr[10] = {};
printf("main:%p\n",arr);
show_arr(arr,10);
printf("main:%p\n",arr);
}
与堆内存配合的指针变量也应该从一而终,这样定义 类型* const 指针变量 防止指针变量改指向,从面导致堆内存无法释放(防止产生内存泄漏)。
int* const p = malloc(4);
int num = 10;
//p = # 报错
free(p)
二级指针:
什么是二级指针:
一级指针存储的是普通变量的内存地址,二级指针存储的是指针变量内存地址。
int num;
int* p = #
定义二级指针:
类型* 一级指针;
类型** 二级指针;
注意:二级指针在使用方法上与一组指针不同,所以一般以pp结尾,让使用者从变量名上就能区别一级指针与二级指针。
二级指针的赋值:
二级指针 = &一级指针;
注意:给二级指针赋值的一级指针,它们的类型必须相同,否则编译时就会报错。
二级指针解引用:
二级指针 = &一级指针;
*二级指针 此时它等价于一级指针
**二级指针 此时它等价于 *一级指针
int num = 10;
int* p = #
int** pp = &p;
//*pp == p == &num
//**pp == *p == num
**pp = 88;
printf("%d %d %d\n",num,*p,**pp);
printf("%p %p %p",*pp,p,&num);
return 0;
二级指针的用处:
只有一个情况适合使用二级指针,那就是跨函数共享一级指针变量。
#include <stdio.h>
int Num = 100;
void func(int** pp)
{
printf("%p %p\n",&Num,*pp);
*pp = &Num;
printf("%p %p\n",&Num,*pp);
}
int main(int argc,const char* argv[])
{
int* p = NULL;
func(&p);
printf("%p %p\n",&Num,p);
return 0;
}
练习5:实现一个函数,能够交换两个指针变量的指向
int n1 = 10,n2 = 20;
int* p1 = &n1, *p2 = &n2;
swap(xxx);
p1->n2, p2->n1
n1 n2值不能变,只能变p1 p2的指向
void swap_p(int** pp1,int** pp2)
{
int* temp = *pp1;
*pp1 = *pp2;
*pp2 = temp;
}
int main(int argc,const char* argv[])
{
int n1 = 10,n2 = 20;
int *p1 = &n1, *p2 = &n2;
printf("%p %p %d %d\n",p1,p2,*p1,*p2);
swap_p(&p1,&p2);
printf("%p %p %d %d\n",p1,p2,*p1,*p2);
return 0;
}
指针数组与数组指针:
什么是指针数组:
由指针变量构成的数组,也可以说它的身份是数组,成员是类型相同的指针变量。
定义指针数组:
类型* 数组名[n];
就相当于定义了n个类型相同的指针变量。
int* arr[10]; // 定义了由10个int*类型指针变量组成的数组
// 10个野指针
int* arr[10] = {}; // 10个NULL指针
指针数组的用处:
1、构建不规则二维数组。
int main(int argc,const char* argv[])
{
int arr1[] = {4,1,2,3,4};
int arr2[] = {7,2,2,3,4,5,6,7};
int arr3[] = {1,8};
int arr4[] = {5,9,2,3,4,5};
int* arr[4] = {arr1,arr2,arr3,arr4};
printf("size:%d\n",sizeof(arr[2]));
for(int i=0; i<4; i++)
{
for(int j=1; j<arr[i][0]+1; j++)
{
printf("%d ",arr[i][j]);
}
printf("\n");
}
return 0;
}
2、构建字符串数组。
什么是数组指针:
专门指向数组的指针变量,它的进步值是整个数组的字节数。
定义数组指针:
类型 (*指针变量名) [n];
类型和n决定了 数组指针 指向的是什么样的数组。
int (*arrp)[10];//专门指向长度为10类型为int的数组的数组指针
数组指针的用处:
#include <stdio.h>
// 使用数组指针可以把一块连续的内存当作二维数组使用,特别是与堆内存配合效果更佳
int main()
{
int arr[20] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20};
int (*p)[5] = (void*)arr;
for(int j=0; j<4; j++)
{
for(int i=0; i<5; i++)
{
//printf("%d ",*(*(p+j)+i));
printf("%d ",p[j][i]);
}
printf("\n");
}
}
#include <stdio.h>
int main(int argc,const char* argv[])
{
int arr[5] = {1,2,3,4,58};
int (*p)[5] = &arr;
printf("%d\n",*((int*)(p+1)-1));
// 结果是58
// p+1 指向了58的后面
//(int*)(p+1)-1 往后退4字节 访问58
return 0;
}
数组指针可以用于函数之间传递二维数组:
函数之间传递二维数组的方式:
1、void func(int arr[行数][列数])
//行列数要固定
2、void func(int arr[][列数],int x)
//列数不能省略
3、void func(int (*arr)[列数],int x)
//一样缺乏泛用性
4、void func(int* arr,int x,int y)
{
printf(“%d ”,*(arr+i*y+j)); //使用麻烦
arr+1*5+3 == *(arr+8)
int arr[3][5];
func((int*)arr,3,5);
}
5、void func(int x,int y,int arr[x][y]) // 建议
数组名、指针、数组指针:
int arr[n];
arr int*
*arr int
&arr[0] int*
&arr int (*)[n]
int (*p)[n] = &arr;
*p <=> arr
arr[i] <=> *(arr+i)
int arr2[r][c];
arr2 int (*)[c]
*arr2 int*
**arr2 int
arr2[i][j] int
&arr2[0] int (*)[c]
&arr2[0][1] int*
函数指针:
函数名是什么:
函数就是一段具有某项功能的代码,它会被编译器编译成二进制指令存储在text内存段,函数名就是它在text内存段的首地址。编译器认为函数名就是一个地址(整数)
什么是函数指针:
专门存储函数地址的指针变量叫函数指针。
定义函数指针:
1、先确定指向的函数的格式(函数声明)。
2、照抄函数声明。
3、用小括号包含函数名。
4、在函数名前加*
5、在函数名末尾加_fp,防止命名冲突。
, 6、用函数名给函数指针赋值后,函数指针就可以当作函数调用了。
#include <stdio.h>
void func(void)
{
printf("我是函数func,我被调用了...\n");
}
int main()
{
void (*func_fp)(void) = func;
func_fp();
}
函数指针的用处:
函数指针可以让函数像数据一样在函数之间传递。
当我们实现一个数组的排序函数时,那么排序函数内部需要调用数组元素的比较函数,由于我们不知道待排序的数组是什么类型,也就无法自己实现数组元素的比较函数,那么我们可以在排序函数的参数列表中预留一个函数指针,当有人调我们的排序函数时,他就需要提供一个数组元素比较函数供我们调用,排序函数就可以为它的数组进行排序。
函数的这种调用模式就叫回调模式。
void qsort(void *base,
size_t nmemb,
size_t size,
int (*compar)(const void *, const void *));
功能:为数组进行排序
base:数组的首地址
nmemb:数组的长度
size:数组成员的字节数
compar:调用者需要提供的数组元素的比较函数 回调函数
#include <stdio.h>
#include <stdlib.h>
int cmp_int(const void* p1, const void* p2)
{
int n1 = *(int*)p1;
int n2 = *(int*)p2;
if(n1 > n2) return -1;
if(n1 < n2) return 1;
return 0;
}
int cmp_double(const void* p1, const void* p2)
{
return *(double*)p1 - *(double*)p2;
}
int main(int argc,const char* argv[])
{
int arr[10] = {5,5,2,46,7,-100,3,5,67,99};
double arr_d[] = {3.11,7.232,2.11};
qsort(arr,10,sizeof(arr[0]),cmp_int);
qsort(arr_d,3,8,cmp_double);
for(int i=0; i<10; i++)
{
printf("%d ",arr[i]);
}
for(int i=0; i<3; i++)
{
printf("%lf ",arr_d[i]);
}
return 0;