C语言指针详解

1 篇文章 0 订阅
1 篇文章 0 订阅

1. 字符指针

2. 指针数组

3.数组指针

4. 数组传参和指针传参

5. 函数指针

6. 函数指针数组

7. 回调函数

这次我们要总结的是以下关于指针方面的内容

1. 指针就是个变量,用来存放地址,地址唯一标识一块内存空间。
2. 指针的大小是固定的 4/8 个字节( 32 位平台 /64 位平台)。
3. 指针是有类型,指针的类型决定了指针的 +- 整数的步长,指针解引用操作的时候的权限。
4. 指针的运算。

一, 字符指针

指针类型中我们所熟悉的类型,char*类型的指针

该类型指针的使用是将一个字符的地址传递给char*类型的指针接收

主要的使用为

char ch = 'c';

char* pc = &ch;

*pc = 'w';

这里我们就是通过创建char*类型的指针去接收 ch的地址通过在对pc内存放的ch的地址进行 解引用的方式(*pc)找到了ch里面的内容,在进行修改 ch变量存放的内容,修改为字符 'w' 

当然还有另外一直用法

char* pstr = "hello world";

printf("%s\n",pstr);

这种用法会让人产生一种误会,认为char* ptrs 里面存放的是整个字符串 "holle world"的内容,实际上ptrs里面存放的是字符串 holle world 的首元素地址 这里我们可以把他的地址打印出来让我们更容易去理解

当然,字符串的地址就是首元素的地址,这里可以看到打印出来的地址是一样的,这里我们就可以知道本质是把字符串 hello world. 首字符的地址放到了pstr中。

当然下面我们来看这样一个代码

    char str1[] = "hello world";
    char str2[] = "hello world";
    const char* str3 = "hello world";
    const char* str4 = "hello world";
    if (str1 == str2)
        printf("str1 and str2 are same\n");
    else
        printf("str1 and str2 are not same\n");

    if (str3 == str4)
        printf("str3 and str4 are same\n");
    else
        printf("str3 and str4 are not same\n");

在这个代码中打印的结果是 str3 == str4 而 str1 != str2,这个原因是str3 和 str4 俩个指针指向的是一个常量字符串的地址,而str1 和 str2 是俩个数组,他们在使用的时候是需要向内存申请空间的,如果一块空间已经给了str1 那么 str2 就不能在向内存申请str1所指向的空间了,而数组名代表他们各自申请到的空间所指向的地址,所以这里str1 和str2 是不相等的

二, 指针数组

指针数组是什么,听名字我们就应该知道他是一个数组,是用了存放指针的数组,

所以我们可以这样使用他

int* arr1 [ 10 ]; //存放 整形指针的数组
char * arr2 [ 4 ]; //存放 一级字符指针的数组
char ** arr3 [ 5 ]; //存放 二级字符指针的数组
这些都是我们定义的数组存放的指针类型是根据需求来定义

三, 数组指针

3.1 数组指针的定义

这里我们也可以通过名字来判断他是指针还是数组,看名字,数组指针,我们就知道他是一种指针,指针指向的是什么,是数组的地址,因为指针能存放的不仅限于,int*,float* 和double*类型的指针,也能用来存放一个数组的地址这里我们就要把这个和指针数组进行区分了。

int* p1[5];
int (*p2)[5];

这里的p1 和p2分别是什么

解释:p先和*结合,说明p是一个指针变量,然后指着指向的是一个大小为5个整型的数组。所以p是一个指针,指向一个数组,叫数组指针
当然这里要注意:[ ]的优先级要高于*号的,所以必须加上()来保证p先和*结合。
3.2 &数组名 和 数组名的区别

我们定义一个数组

int arr[5];

那么arr 和 &arr的区别是什么
我们知道 arr 是数组名,数组名表示数组首元素的地址。
那么&arr 取出的就是数组本身向内存申请空间  取的是 arr数组的地址
当然如果我们要去看他们的地址有什么区别我们可以看一下
当然这里我们发现arr 和 &arr  的地址好像是相同的,但是真的是相同的吗,我们对该数据分别进行+1操作在观察一次
这里我们可以看到 对arr数组进行加一操作,他跳过的是4个byt的长度,而对&arr+1跳过的是20个byt的长度,这里我们就可以知道,&arr取出的是整个arr数组的地址,而不是首元素的地址
3.3数组指针的使用

当然现在我们知道了他们的不同,那么如何去用指针存放数组的地址呢

int arr[5] = { 1,2,3,4,5 };

int (*p)[5] = &arr;//把数组arr的地址放在数组指针p里面

当然,一般我们并不会用数组指针来接一维数组的地址的,

当然我们这里可以这俩种方式去接受一个二维数组

用数组的形式来接收

用数组指针的形状来接收

那么我们看一下打印结果吧

可以看到,我们有数组指针的形式去接受一个二维数组,也是可以打印出来的

当然,数组指针也是可以存放在数组里面的,例如

int (*parr[5])[5];

这个我们可以发现,parr先和[ ]结合,因为[ ] 的结合性大于 * 那么parr先和[ ] 结合那么他就是一个数组我们去掉数组的部分,parr[5] 剩下的部分就是他的类型 int(*)[5];

四,数组参数,指针参数

我们一般在书写代码的时候经常需要把数组 或者 指针 传递给函数,那么我们应该怎么去用

4.1 一维数组传参

void test ( int arr [])
void test ( int arr [ 10 ])
void test ( int * arr )
上诉都是我们常用的传参方式
4.2 二维数组传参
void test ( int arr [ 3 ][ 5 ])
{}
void test ( int arr [][]) // 错误的传参方式,二维数组在传参是可以省略行的内容但是不能省略列的内容
{}
void test ( int arr [][ 5 ])
{}
4.3 一级指针传参
一级指针传参我们用二级指针来接收就行

五, 函数指针

函数指针,顾名思义,就是用了存放函数地址的指针,这里我们看一下函数的地址是如何打印的呢

这里我们可以看到,直接打印函数 test 的地址和 &test 的地址打印的地址是一直的,当然可能就会有人问他们这样有区别的,当然是没区别的。直接用函数名去打印地址和 &函数名之后打印的地址是相同的,那么我们应该如何保存这个还是的地址呢,

这当然是根据我们还是的参数类型,和返回类型来定义比如我们定义一个加法还是返回类型是int

俩个参数也是int

int Add(int x,int y)

{
        return x+y;
}

//我们就用一个函数指针去接收这个函数的地址
 #include<stdio.h>
int main()

{
        int (*pf)(int, int) = &test;
        return 0;
}

如果我们运行这段代码

可以看到我们用pf去调用也可以调用

当然也有人看见我没有对指针进行解引用*操作就调用了这个函数

这是我要说的第二个点,如果你用函数指针去调用函数,可以不进行解引用直接调用,如果进行了解引用操作,那么需要加上括号,

什么意思呢就是

(*pf)(3,4);

在调用的时候要把 *pf 用括号括起来

当然也有些人想用typedef 去从定义一下我们的函数名,

这里需要注意的是在用typedef的时候

需要这样写

typedef int(*pf_add)(int,int);

我们需要把重定义的符号写在 * 的旁边这样才能去使用

六, 函数指针数组

这里是存放函数指针的数组,本质还是数组,他的作用是什么呢,如何存放下面我来介绍一下

当然首先我们要自己他要怎么去定义,数组存放的元素要是自己的类型,那么还是指针的类型是什么

这里我们简单定义一个 加法函数,一个加法函数

int Add(int x,inty)

{

        return x + y;

}

int Sub(int x,inty)

{

        return x - y;

}

指针对应如下:

int (*pf)(int ,int) = Add();

这个指针的类型是什么

去掉名称 pf 剩下的就是类型

类型为 int(*)(int,int) 那么我们要用一个数组去存放呢

int (*parr[4])(int,int);//这便是我们创建的函数指针数组

我们可以用他们来存放 上面 俩个函数的地址

int (*parr[4])(int,int) = { Add,Sub};//因为Add 和Sub的参数相同 返回类型相同,所以可以存放在相同的函数指针数组里面

我们可以用这个写一个简单的计算器 能实现加减乘除

#include<stdio.h>
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;
}
int main()
{
	int(*parr[5])(int,int) = { 0,Add,Sub,Mul,Div };
	int n = 0;

	
	int ret = 0;
	do
	{
		printf("请选择你想要个功能\n");
		printf(" 0.退出 1.加法 2.减法 3.乘法 4.除法 :>");

		if (n != 0)
		{
			scanf("%d", &n);
			int x = 0;
			int y = 0;
			printf("输入俩个操作数:>");
			scanf("%d %d", &x, &y);

			ret = (*parr[n])(x, y);
			printf("%d\n", ret);
		}
	} while (n);

	return 0;
}

上述内容就是我们所写的用函数指针数组实现的一个简单计算器

八. 回调函数

这里我们只进行介绍不进行实验

回调函数概念:

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个
函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数
的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进
行响应。
这个就是我上次发布的qsort函数的比较函数,在qsort中调用比较,而不是直接调用,在qsort中使用的就叫回调函数

  • 15
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值