当前位置: 首页 > news >正文

C语言-指针进阶

一、字符指针

在指针类型中,有一种指针类型为char*。

一般的使用方式:

char ch = 'a';
char* pc = &ch;
*pc = 'w';

还有一种使用方式如下:

const char* p = "abcdef";
//这里的"abcdef"是一个常量字符串,是存储在代码段(常量区)中的
//这里的意思是把"abcdef"首字符的地址放入了p中
//const限制的是*p,也就意味着*p不能被改变,或者说"abcdef"这个常量字符串不能被改变

并且常量字符串不允许被修改,并且只会在常量区存在一份

二、指针数组

指针数组,本质上是一个数组,里面存放的类型是指针。

int* arr[10];		//一级指针数组,数组里面存放的是int*
char* arr1[10];		//一级指针数组,里面存放的是char*
char** arr2[10];	//二级字符指针数组,里面存放的是char**

三、数组指针

3.1 数组指针的定义

数组指针本质上是一个指针,是一个能指向数组的指针。

int arr[10] = { 0 };
//arr的类型是int [10]
int(*p)[10] = &arr;
//p和*结合说明p是一个指针变量,指向的类型是int [10]也就是arr的类型,所以p是一个数组指针
//[]的优先级是要比*的优先级高的,所以*p需要用(),以保证*和p的结合性

3.2 &数组名和数组名

一般来说数组名通常是指数组首元素的地址,但有两个例外sizeof(数组名)和&数组名,这里的两个数组名是指整个数组。

所以说&数组名是指取出整个数组的地址。

实际上:&arr是整个数组的地址,而不是数组首元素的地址。这个例子中&arr的类型是int(*)[10],是一种数组指针类型。&arr和&arr+1的差值是40,跳过的是整个数组的大小。

3.3 数组指针的使用

一般数组指针会经常使用在二维数组的传参上。

一位数组的传参,形参部分可以是数组,也可以是指针。

//数组传参
void test(int arr[10])
{
}
//指针传参
void test1(int* arr)
{
}int arr[10];
test(arr);
test1(arr);

二维数组的传参,形参部分可以是数组,也可以是指针。

//数组传参
void test(int arr[5][10])
{
}
void test1(int arr[][10])		//可以省略行,但不能省略列
{
}
//指针传参
void test2(int(*arr)[10])
{
}int arr[5][10];
test(arr);
test1(arr);
test2(arr);

一维数组和二维数组传参都是传首元素地址,一位数组首元素的地址是一维数组类型的地址,二维数组首元素的地址是一维数组的地址,也就是数组指针。

int arr[5];				//这是一个整型数组,有5个元素
int* parr1[10];			//这是一个指针数组,有10个元素,元素类型是int*
int(*parr2)[10];		//这是一个数组指针,指针指向的类型是int [10]
int(*parr3[10])[5];		//这是一个数组指针数组,有10个元素,元素类型是int [5]

四、数组参数、指针参数

4.1 一维数组传参

void test(int arr[10])
{}
void test1(int arr[])
{}
void test2(int* arr)
{}
void test3(int* arr1[10])
{}
void test4(int** arr1)
{}int arr[10];
test(arr);
test1(arr);
test2(arr);
int* arr1[10];
test3(arr1);
test4(arr1);

4.2 二维数组传参

void test(int arr[5][10])
{}
void test1(int arr[][10])		//可以省略行,但不能省略列
{}
void test2(int(*arr)[10])
{}int arr[5][10];
test(arr);
test1(arr);
test2(arr);

4.3 一级指针传参

void test(int* p)
{}int a = 10;
int* p = &a;
test(p);

当参数为一级指针时,可以接收的参数为:

void test(char* p)
{}
void test1(const char* p)
{}char ch;
char* pc = &ch;
char arr[10];
test(&ch);		//可以传字符的地址
test(pc);
test(arr);		//也可以传字符数组首元素的地址
const char* p = "abcdef";
test1(p);		//也可以传常量字符串的地址,但类型需要匹配

4.4 二级指针传参

void test(char** p)
{}char ch;
char* pc = &ch;
char** ppc = &pc;
test(ppc);

当参数为二级指针时,可以传递的参数为:

void test(char** p)
{}char ch;
char* pc = &ch;
char** ppc = &pc;
test(&pc);			//可以传一级指针的地址
test(ppc);
char* arr[10];
test(arr);			//也可以传一级指针数组的首元素的地址

五、函数指针

函数名是函数的地址

&函数名同样也是函数的地址

函数地址的存储:

int Add(int x, int y)
{return x + y;
}//Add函数的类型是函数去掉函数名后剩余的部分,也就是int (int x,int y)
int(*pf)(int, int) = &Add;		//这里参数可以省略,但参数类型不能省略
//*和pf结合说明pf是一个指针,pf指向的是int (int,int)的类型
//pf就是一个函数指针变量,存放的是函数的地址

注:函数去掉函数名就是函数的类型,和数组一样,数组去掉数组名也是数组的类型。

函数指针的使用:

//函数指针可以通过解引用调用,也可以直接调用
(*pf)(3, 5);		//解引用调用
pf(4, 8);			//直接调用函数指针

数组指针和函数指针的重命名:

//数组指针的重命名,这里的操作是将数组指针int (*)[10]重命名为parr_t
typedef int(*parr_t)[];
//函数指针的重命名,这里的操作是将函数指针int(*)(int,int)重命名为pf_t
typedef int(*pf_t)(int, int);

例如:

(*(void(*)())0)();
//void(*)()是一个函数指针,没有参数,返回值是void类型
//(void(*)())0是将0强转成一个void(*)()类型的函数指针
//(*(void(*)())0)()是指将0强转后然后进行解引用调用void(*signal(int, void(*)(int)))(int);
//signal是一个函数,有两个参数,参数类型分别为int和void(*)(int)
//signal函数的返回类型为void(*)(int)

六、函数指针数组

函数指针数组是一个数组,里面存放的元素类型是函数指针。

函数指针的定义:

//函数指针数组正确的定义方式:
int(*parr[10])();
int Add(int x, int y)
{return x + y;
}
int Sub(int x, int y)
{return x - y;
}
int Mul(int x, int y)
{return x * y;
}
int Div(int x, int y)
{return x / y;
}//Add、Sub、Mul、Div这四个函数类型相同是int (int,int)
//这四个函数的函数指针是int(*)(int,int)
//因为函数名就是函数的地址,那么存放这四个函数的数组就是函数指针数组
int(*p[4])(int, int) = {Add,Sub,Mul,Div};
//p和[4]结合说明p是一个数组,数组元素类型是函数指针int(*)(int,int)

函数指针数组的用途:转移表。

七、指向函数指针数组的指针

数组:

int arr[10];
//数组的类型:int [10]

指针数组:

int* arr[10];
//指针数组的类型:int* [10]

数组指针:

int(*p)[10];
//指针指向的类型:int [10]
//指针的类型:int(*)[10]

指向指针数组的指针:

int* (*p)[10];
//指针指向的类型:int* [10]
//指针的类型:int*(*)[10]

函数:

int Add(int x,int y);
//函数的类型:int (int,int)

函数指针:

int(*p)(int, int) = &Add;
//函数指针的类型:int(*)(int,int)

函数指针数组:

int(*p[10])(int, int);
//函数指针数组的类型:int(* [10])(int,int)
//函数指针数组内元素的类型:int(*)(int,int)

指向函数指针数组的指针:

int(*(*p)[10])(int, int);
//指针指向的类型:int(* [10])(int,int)
//指针的类型:int(*(*)[10])(int,int)

总结:去掉名字就是类型,将名字换成(*p),p就是指向这个类型的指针了

八、回调函数

回调函数就是一个调用函数指针的函数。当把函数指针当做参数传给另外一个函数时,当这个指针被用来调用其指向的函数时,这就是回调函数。回调函数不是由该函数实现方直接调用,而是在特定的事件或条件下由另外一方调用的,用于对该事件的响应。

例如qsort中的回调函数:

#include<stdlib.h>void qsort(void* base,				//数组首元素地址size_t num,				//数组元素个数size_t size,				//数组每个元素的大小,单位字节int (*compar)(const void*, const void*)	//比较函数,需要自己实现,qsort内部会进行调用);
#include<string.h>
#include<stdlib.h>
struct Stu
{char name[20];int age;
};
//比较函数
int cmp_by_name(const void* p1, const void* p2)
{return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);
}
int main()
{//结构体数组struct Stu arr[3] = { {"zhangsan",19},{"lisi",25},{"wangwu",18}};//利用qsort对arr的name进行排序int sz = sizeof(arr) / sizeof(arr[0]);//qsort用比较函数的函数指针作为参数,在内部进行调用qsort(arr, sz, sizeof(arr[0]), cmp_by_name);return 0;
}

按名字排序后的结果:

http://www.dtcms.com/a/440445.html

相关文章:

  • 苏州科技网站建设苏州设计工作室
  • 灵感档案 | 《Celeste》中跳跃手感的仿制版源代码学习
  • 邯郸网站设计应搜韦欣cidun8上词互联网公司排名名单
  • 宣武做网站外包公司一个人头挣多少钱
  • 英一2010年真题学习笔记
  • 锡林郭勒盟工程建设造管理网站邮箱域名可以做网站吗
  • 黑龙江住房建设部网站wordpress博客添加标签
  • 平面设计教程网站有哪些专业的做网站
  • 做网店网站wordpress弹窗网页
  • 网站如何做百度百科c2c模式的特点类似于现实商务世界中的跳蚤市场
  • `epoll_ctl` 函数中,`int fd` 和 `epoll_event.data.fd`的疑问
  • 哪些网站的做的好看的图片广州企业推广网站建设
  • 小说网站开发思路该网站未在腾讯云备案
  • 网站后台建设教程下载现在的网络营销方式
  • 网站实现重庆网站建设仿站
  • 重庆江北营销型网站建设价格上饶营销网站建设
  • wordpress语法高亮搜索引擎优化的定义
  • Python推导式详解与应用
  • 自建网站做跨境电商学网站建设需要什么软件有哪些
  • 安徽建设干部学校网站首页开源软件
  • 网站开发项目实战黄骅港防疫办电话
  • 烟台建设用地规划查询网站云主机 多个网站
  • 网站聊天系统怎么做做一个自适应网站多少钱
  • 华为商城网站设计如何查看网站的服务器位置
  • 公司网站建设费计入什么科目seo优化实训内容
  • 不属于营销型网站的特点山东网站seo开发
  • 网站制作费用大概多少永州网站建设gwtcms
  • 坪地网站建设包括哪些龙岩全网搜系统开发
  • 英国设计网站海南学校网站建设
  • 雅安建设机械网站网店美工的意义