指针进阶----C

本文详细介绍了C语言中的指针进阶知识,包括字符指针的使用,如何通过字符指针操作字符串,指针数组的概念及应用,以及数组指针的定义和区别。此外,还讲解了函数指针的用法,包括如何通过函数指针调用函数,并展示了函数指针数组和指向函数指针数组的指针的应用示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

@[TOC]指针进阶

指针回顾*
1.指针就是个变量,用来存放地址,地址唯一标识一块内存空间。
2.指针的大小是固定的4/8个字节(32位平台/64位平台)。
3.指针是有类型,指针的类型决定了指针的+整数的步长,指针解引用操作的时候。
4.指针的运算。
在这里,我们继续探讨指针的高级主题。

1.字符指针

         字符指针就是指向字符的的指针,形式为char*
         **字符指针的使用**
int main()

{
	char ch = 'w';
	char* pc = &ch; //pc就是字符指针
	char* p = "abcdef";//把字符串的首字母存放入p中
	//表达式的值为首字符的地址
	//常量字符串不能修改  
	//*p = 'w';//err
	char arr[] = "hello bit";
	char* p = arr;
	//p指向数组的首元素,arr是数组可以修改
	*p = 'w'; 
	printf("%s", arr);

	return 0;
}

注意:不是把字符串放在p中,本质是把字符串首字符的地址放入p中

典型例题

int main()
{
	char str1[] = "hello bit.";
	char str2[] = "hello bit.";
	//创建两个数组,在内存中占用不同的空间
	//str1,str2数组首元素的地址,不相同
	char* str3 = "hello bit.";
	char* str4 = "hello bit";
	//常量字符串不能修改,在内存中只存一份
	//str3和str4都是字符串首字符的地址,相同
	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");
	}
	return 0;
}


这里str3和str4指向的是一个同一个常量字符串,C/C++会把常量字符串存储到单独的一个内存区域,当几个指针。指向同一个字符串的时候,他们实际会指向同一块内存。但是用相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。所以str1和str2不同,str3和str4不同。

2.指针数组

指针数组就是存放指针的数组

int* arr1[10];//整型指针数组
char* arr2[4];//一级字符指针的数组
char** arr3[5];//二级字符指针数组

举个例子1

//字符指针数组
int main()
{
	char* arr[3] = { "abcdef","hehe","qwer" };
	//arr数组存放的是三个字符串的首元素的地址
	int i  = 0;
	for (i = 0; i < 3; i++)
	{
		printf("%s\n", arr[i]);
	}

	return 0;
}

在这里插入图片描述

3.数组指针

3.1数组指针的的定义

数组指针是指针还是数组?
答案是数组。
我们已经熟悉的
整形指针:int *pint;能够指向整形数据的指针。
浮点型指针: float *pf;能够指向浮点型数据的指针
那数组指针应该是:能够指向数组的指针。

int main()
{
	//指针数组
	char* arr[4];
	//数组指针
	int arr[5];
	int(*p)[5] = &arr;
	return 0;
}
int (*p)[5];
//解释:p先和*结合,说明p是一个指针变量,然后指着指向的是一个大小为10个整型的数组。所以p是一个指针,指向一个数组,叫数组指针。

3.2 数组名 VS &数组名

//arr和&arr的区别
//数组名绝大部分是数组首元素的地址,但有两个例外
//1.sizeof(数组名)-sizeof内部单独放一个数组名,数组名表示整个数组,计算得到数组的总大小
//2.&arr - 这里的数组名表示整个数组,取出的是整个数组的地址,从地址角度来讲和数组首元素的地址一样的,但意义不一样


int main()
{
	int arr[10] = { 0 };
	printf("%p\n", arr);
	printf("%p\n", arr + 1);

	printf("%p\n", &arr[0]);
	printf("%p\n", &arr[0] + 1);

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

	return 0;
}

在这里插入图片描述
根据上面的代码我们发现,其实&arr和arr,虽然值是一样的,但是意义应该不一样的。实际上:&arr表示的是数组的地址,而不是数组首元素的地址。(细细体会下)本例中&arr 的类型是:int(*)[10],是一种数组指针类型
数组的地址+1,跳过整个数组的大小,所以 &arr+1 相对于 &arr 的差值是40.

4.数组传参和指针传参

4.1一维数组传参

一维数组传参,形参可以写成指针,也可以写成数组

//一维数组传参,形参是数组
void print(int arr[10], int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}
//一维数组传参,形参是指针
void print(int* arr, int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", *(arr + i));
		//
	}
	printf("\n");
}
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	print(arr, sz);
	return 0;
}

4.2二维数组传参

二维数组传参,形参可以是数组(行可以省略,列不能省略),也可以是数组指针
补充
二维数组的数组名,也是首元素的地址
二维数组的首元素也是第一行
首元素的地址就是第一行的地址,是一个一维数组的地址

//void print(int arr[3][5],int r,int c)//二维数组传参,参数为数组
void print(int(*arr)[5], int r, int c)//二维数组传参,参数为数组指针
{
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		for (int j = 0; j < 5; j++)
		{
			//printf("%d", arr[i][j]);
			printf("%d", (*(arr + i) + j));
			//(arr+i)相当于&arr[i]
			//*(arr+i)相当于arr[i],是一个一维数组的数组名,相当于第i行,首元素的地址就是地址
			//*(arr+i)+j 相当于第i行,第j个元素的地址
		}
	}
}

int mian()
{
	int arr[3][5] = { 1,2,3,4,5,2,3,4,5,6,3,4,5,6,7 };
	print(arr, 3, 5);
	
	return 0;
}





5.函数指针

函数也有地址,存放函数地址的指针叫函数指针

首先看一段代码

#include<stdio.h>
void test()
{
	printf("hehe\n");
}
int main()
{
	printf("%p\n", test);
	printf("%p\n", &test);
	return 0;
}

在这里插入图片描述

输出的两个地址都是test函数的地址
函数地址和函数值是等价的

int add(int b, int a)
	{
		return a + b;
	}
#include<stdio.h>
int main()
{
	int (*pf)(int, int) = &add;//函数的地址要存起来,就要放到函数指针变量里

	int ret = (*pf)(3, 5);
	printf("%d\n", ret);
		 ret = (**pf)(3, 5);
		 printf("%d\n", ret);

		 ret = (***pf)(3, 5);
		 printf("%d\n", ret);

		 ret = pf(3, 5);
		 printf("%d\n", ret);

		 ret = add(3, 5);
		 printf("%d\n", ret);

	return 0;
}

> 因为函数的值和函数的地址是一样的,这就导致对函数指针解引用n次最后得到的还是函数本身的值也就是函数得地址

阅读两段有趣的代码

 代码一:(*(void (*)())0)();
代码二:void (*signal(int, void(*)(int)))(int);
**解析**
//代码一:1.将0强制转化为void(*)()类型的函数指针
//       2.这就意味着0处放着一个函数,函数没参数,返回值类型是void
//       3.调用0地址处的这个函数
//代码二:1.函数的名字是signal
//       2.signal函数的参数第一个是int类型,第二个是void(*)(int)类型的函数指针
//       3.signal函数的返回值也是一个函数指针,该函数指针的参数是int,返回值类型是void

6.函数指针数组

函数指针数组是一个数组,数组的元素是函数指针
把函数的地址存放到一个数组中,那这个数组就叫函数指针数组

函数指针数组组的用途:转移表
例子:计算器

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;
}
#include<stdio.h>
void menu()
{
	printf("*****************************\n");
	printf("***** 1.add     2.sub *******\n");
	printf("***** 3.mul     4.div *******\n");
	printf("*****  0.exit  **************\n");
	printf("*****************************\n");
}
int main()
{
	int x = 0;
	int y = 0;
	int input = 0;
	int ret = 0;
	int (*PfArr[])(int, int) = { NULL,Add,Sub,Mul,Div };//函数指针数组,存放函数的地址
	do {
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		if (0 == input)
		{
			printf("推出计算器\n");
			break;
		}
		else if (input >= 1 && input <= 4)
		{
			printf("请输入两个操作数:>");
			scanf("%d %d", &x, &y);
			ret = PfArr[input](x, y);//通过函数指针调用函数
			printf("%d\n", ret);
		}
		else
		{
			printf("选择错误\n");
		}

	} while (input);
	return 0;
}

7.指向函数指针数组的指针

指向函数指针数组的指针是一个指针,指针指向的是一个数组,数组元素是函数指针

#include<stdio.h>
void test(const char* str)
{
	printf("%s\n", str);
}
int main()
{
	//函数指针pfun
	void (*pfun)(const char*) = test;
	//函数指针的数组pfunaArr
	void (*pfunArr[5])(const char* str);
	//指向函数指针数组pfunArr的指针ppfunArr
	void(*(*ppfunArr)[5])(const char* str) = &pfunArr;
	return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值