C语言-指针进阶

首先,我们可以回顾一下指针的基础知识:
①指针是用来存放地址的变量,地址唯一标识一块内存空间。
②指针的大小取决于操作系统,在32位平台是4个字节,64位平台是8个字节。
③指针有不同的类型,指针可以进行运算。

接下来,就可以进行指针进阶内容的学习了

字符指针

指针类型中,有一种指针的类型是char *
可以这样使用:

#include <stdio.h>
int main()
{
char ch='a';
char *p=&ch;
*p='a';
return 0;
}

还有一种使用方法是这样的:

# include <studio.h>
int main()
{
char *p="abcdefg";
*p='w';
printf("%s\n",p);
return 0;
}

需要注意的是:这里是将字符串“abcdefg”的首字符的地址存放在了字符指针p中,而不是将整个字符放在p中。
那么我们的*p='w’这句代码就出现了问题,为了避免这个问题,我们可以将代码进行改进:

#include <studio.h>
int main()
{
const char*p="abcdefg";
return 0;
}

代表字符指针p指向的内容是不可更改的,从而减少错误代码的产生。

指针数组

指针数组是指存放指针的数组。
可以类比整型数组,存放整型的数组

#include <studio.h>
int main()
{
int *arr1[10];    /*一级整型指针的数组*/
int **arr2[10];   /*二级整型指针的数组*/
return 0;
}

由于操作符*的优先级小于[ ],所以arr先和[ ]结合,确定了arr是一个数组。
再和 *结合,确定数组元素的类型是整型指针。

数组指针

数组指针的定义

首先应该明确,数组指针是一种指针。
数组指针是能够指向数组的指针
可以类比整型指针,是指向整型数据的指针

通过两行代码来辨别数组指针与指针数组:

int *arr1[10];
int (*arr2)[10];
/*这样两句代码有什么区别?*/

在操作符的相关知识中,我们了解到:[ ]的优先级是要高于 *号的。
当加上括号()以后,改变了p的结合顺序
p先和 *结合,说明p是一个指针变量,这个变量指向了大小为10个整型的数组。
所以p是一个指针,指向了一个数组,叫做数组指针。

&数组名和数组名

int arr[10];

通过前面的学习,我们了解到:
arr是数组名,数组名表示首元素的地址。

那么&arr又代表了什么?
看一段代码:

#include <stdio.h>
int main()
{
	int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
	printf("%p\n", arr);
	printf("%p\n", &arr);
	return 0;
}

运行结果:

008FF904
008FF904

数组名和&数组名打印的地址相同

再来一段代码:

#include <stdio.h>
int main()
{
	int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };

	printf("%p\n", arr);
	printf("%p\n", &arr);

	printf("%p\n", arr + 1);
	printf("%p\n", &arr + 1);
	return 0;
}

运行结果:

012FF908
012FF908
012FF90C
012FF930

通过代码的运行结果,我们可以发现:

当&arr+1时,相较于&arr的差值是40
数组arr有10个元素,整型元素在内存中占4个字节。
看到&arr+1,跳过了整个数组的大小

所以,&arr表示的是数组的地址,而不是数组首元素的地址
从而,&arr 就与arr有了本质的区别

数组参数和指针参数

一维数组传参

以代码为例:

#include <stdio.h>
void test(int arr[])
{}
void test(int arr[10])
{}
void test(int *arr)
{}
void test2(int *arr2[20])
{}
void test2(int **arr2)
{}
int main()
{
int arr[10]={0};
int *arr2[20]={0};
test(arr);
test2(arr2);
return 0;
}

上面代码所提供的传参方法是否都可以呢?

void test(int arr[ ])数组传参,数组接收,这样的传参方法自然是正确的
void test(int arr[10])相比第一种传参方法,这里指定了数组的大小,自然也是正确的。因为整个传参过程并不会创建数组,只是需要传入参数的类型和参数名
void test(int *arr)由于传入的形参是数组名,它代表了首元素的地址,那么用指针来接收自然也没有问题。
void test2(int *arr[20])与第一种传参方法一样,传入指针数组,用指针数组接收,正确
void test2(int **arr2)数组名arr2的首元素类型是int *,是一个一级指针,最后用二级指针来接收,正确

二维数组传参

依然以代码为例:

#include <stdio.h>
void test(int arr[3][4])
{}
void test(int arr[][])
{}
void test(int arr[][4])
{}
void test(int *arr)
{}
void test(int* arr[4])
{}
void test(int(*arr)[4])
{}
void test(int **arr)
{}
int main()
{
	int arr[3][4] = { 0 };
	test(arr);
	return 0;
}

上面代码所提供的传参方法是否都可以呢?

void test(int arr[3][4])数组传参,数组接收,这样的传参方法自然是正确的
void test(int arr[ ][ ])当二维数组的行列都不确定时,是不足以成为一个数组的,所以这种方法是错误的
void test(int arr[ ][4])给定了二维数组的列,这个数组就是可以确定的,正确
void test(int *arr)数组名代表首元素的地址,对于二维数组而言,数组名相当于第一行元素的地址,地址用指针接收,它的类型就应该是 int ( *)[4]。所以这种传参方法及下一行代码的传参方法就是错误的。
void test(int(*arr)[4])这样的二维数组传参方法自然就是正确的。
void test(int **arr)这里是一个二级指针,而只有传递一级指针的地址时才需要二级指针,所以它也是错误的。

一级指针传参

首先是一种最简单的情况:

#include <stdio.h>
void test(int* p)   //用指针接收
{}
int main()
{
	int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
	int* p = arr;
	test(p);      //将一级指针p传入
	return 0;
}

那么当一个函数的参数部分为一级指针的时候,函数又可以接收什么参数呢?
例如,当函数是这样时:

void test1(int *p)

可能传入的参数是:

int main()
{
int a=10;
int *p=&a;
int arr[10]={0};
test1(&a);
test1(p);
test1(arr);
return 0;
}

二级指针传参

来看一段代码:

#include <stdio.h>
void test(int** t)
{}
int main()
{
	int n = 10;
	int* p = &n;
	int** pp = &p;
	test(pp);
	test(&p);
	return 0;
}

可以发现,二级指针可以用二级指针来接收;一级指针的地址也可以用二级指针来接收

那么当函数的参数为二级指针的时候,可以接收什么参数呢?

#include <stdio.h>
void test(char** t)
{}
int main()
{
	char ch = 'a';
	char* p = &ch;
	char* pp = &p;
	char* arr[10];
	test(&p);
	test(pp);
	test(arr);
	return 0;
}

可见,当函数的参数为二级指针时,可以接收二级指针,一级指针的地址,指针数组

函数指针

类比数组指针的概念,函数指针就是指向函数的指针

有了函数指针,自然就代表函数是有地址的,那么函数的地址怎么表示呢?

#include <stdio.h>
int Add(int x, int y)
{
	return x + y;
}
int main()
{
	int a = 10;
	int b = 20;
	int c = Add(a, b);
	printf("%p\n", &Add);
	printf("%p\n", Add);
	return 0;
}

运行结果如下:

004E13B1
004E13B1

两句代码的运行结果是一样的,代表这样两种方法都是可以得到函数的地址的

那么函数指针又怎么表示呢?

#include <stdio.h>
int Add(int x, int y)
{
	return x + y;
}
int main()
{
	int a = 10;
	int b = 20;
	int c = Add(a, b);
	int (*pf)(int,int)=&Add;
	return 0;
}

int (*pf)(int,int)=&Add; pf就是一个函数指针,指向函数Add的指针

接下来,怎么使用函数指针呢?

#include <stdio.h>
int Add(int x, int y)
{
	return x + y;
}
int main()
{
	int a = 10;
	int b = 20;
	int c = Add(a, b);
	int (*pf)(int,int)=&Add;
	int sum=(*pf)(a,b);
	int sum2=(pf)(a,b);      //可见, *在这里是可以省略的
	printf("%d\n",sum);
	printf("%d\n",sum2);
	return 0;
}

看运行结果:

30
30

需要注意的是,这里的举例只是为了学习函数指针的使用方法,并不代表实际的应用场景。在真正的编程过程中,还是要以方便,简洁,高效为主。

函数指针数组

首先什么是函数指针数组呢?

数组是存放相同类型数据的存储空间。
和指针数组的定义一样,把函数的地址存放到一个数组中,这个数组就是函数指针数组

如何定义函数指针数组呢?

int (*parr[10])();

parr首先和[ ]结合,确定了数组;
剩下来的int ( *) ( )就是数组的数据类型,int ( *) ( )类型的函数指针

指向函数指针数组的指针

如何理解指向函数指针数组的指针这样一个概念呢?

首先,指向函数指针数组的指针无疑是一个指针
这个指针指向了一个数组
这个数组的元素是函数指针

指向函数指针数组的指针的定义方法:

#include <stdio.h>
int Add(int a, int b)
{
	return a + b;
}
int main()
{
	int (*pa)(int, int) = Add;   /*函数指针*/
	int* ppa[5](int, int);   /*函数指针数组*/
	int (*(*pppa)[5]) (int, int) = &ppa;   /*指向函数指针数组的指针*/
	return 0;
}

回调函数

回调函数就是一个通过函数指针调用的函数;
当我们把函数的地址(指针)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们称其为回调函数。

回调函数不是由该函数的实现方法直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

以上就是C语言指针进阶的全部内容啦,
主要是指针进阶概念的理解和基本定义方法啦!

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值