c语言——指针详解

一、字符指针

int main()
{
	//指针p里存放的是字符串首字符的地址
	const char* p = "abcdef";//"abcdef"是常量字符串//加上const修饰就无法修改*p的值
	//*p = 'w';//程序崩溃:不能通过*p修改值
	printf("%s\n", p);//abcdef
	printf("%c\n", *p);//打印字符a
	return 0;
}

 char* p = "abcdef"  //字符指针p指向的是字符串首字符a的地址

int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "abcdef";
	const char* p1 = "abcdef";//常量字符串,不能被修改,因为都是字符串"abcdef",因此内存中只存了一个
	const char* p2 = "abcdef";
	//打印haha:arr1和arr2的首元素地址不同
	//if(arr1 == arr2)
	//	printf("hehe\n");
	//else
	//	printf("haha\n");
	//打印hehe:p1和p2指向同一块地址
	if(p1 == p2)
		printf("hehe\n");
	else
		printf("haha\n");
	return 0;
}

字符数组名存放的是首字符地址,因此地址不同

字符指针存放的字符串是常量字符串,理论上不能被修改

此时的p1和p12指向的是同一块地址

二、指针数组 (实质是数组,用来存放指针)

int main()
{
	int arr1[] = {1, 2, 3, 4, 5};
	int arr2[] = {2, 3, 4, 5 ,6};
	int arr3[] = {3, 4, 5, 6, 7};
	int* arr[] = {arr1, arr2, arr3};//用数组指针arr存放arr1, arr2, arr3的首元素地址
	int i = 0;
	for(i=0; i<3; i++)
	{
		//定义j:地址依次向后加一位,依次打印元素
		int j = 0;
		for(j=0; j<5; j++)
		{
			printf("%d ", *(arr[i]+j));
		}
		printf("\n");//每一行输出后换行
	}
	return 0;
}

数组名就是首元素地址 !!!

三、数组指针 

int main()
{
	//数组指针的写法
	int arr[10] = {0};
	int (*p)[10]= &arr;//&arr表示整个数组,放到*p,说明将arr的地址放到了指针p中
					  //而*p的类型是数组类型int [10]
	return 0;
}

 老师说一般不这么用,正确用法如下

void print(int (*pa)[5], int x, int y)
{
	int i = 0;
	for(i=0; i<x; i++)
	{
		int j = 0;
		for(j=0; j<y; j++)
		{
			//printf("%d ", pa[i][j]);
			//printf("%d ", *(*(pa+i)+j));//*(首元素地址+i):拿到了第i行的值
			                            //(*(首元素地址+i)+j):第i行第j个元素
			                            //*(*(首元素地址+i)+j):解引用第i行第j个元素
			printf("%d ", (*(pa+i))[j]);//第i行第j列的元素
		}
		printf("\n");
	}
}
int main()
{
	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;
}

看不懂,好绕...还有更绕的:


1、int arr[10];

//表示arr是一个有10个元素的整形数组
2、int *arr1[10];

//[]优先级高,因此arr1是一个数组,该数组有10个元素,
//每个元素的类型是int*,因此arr1是指针数组
3、int (*arr2)[10];

//arr2是一个指针,该指针指向了一个数组,这个数组有10个元素,每个元素的类型是int
//因此arr2是一个数组指针
4、int (*arr3[2])[3];

//()优先级最高,因此先看()里的内容:[]优先级又高
//(*arr3[2]):arr3是一个数组,该数组有2个元素,每个元素是数组指针
//该数组指针指向了一个有3个元素的整形数组
 

四、函数指针(存放函数地址的指针)
 

//函数指针——存放函数地址的指针
int ADD(int x, int y)
{
	int z;
	z = x + y;
	return z;
}
int main()
{
	int a = 10;
	int b = 20;
	//printf("%d\n", ADD(a, b));
	
	//&函数名 和 函数名都是函数的地址
	printf("%p\n", ADD);//函数的地址
	printf("%p\n", &ADD);//函数的地址
}

注:函数名 和 &函数名都是函数的地址

那么,函数指针该如何用呢?函数地址该如何存放呢?看下面的代码 

    int (*p)(int, int) = ADD;
	//p存放函数的地址  *p调用这个函数
	printf("%d\n", (*p)(2, 3));
	printf("%d\n", p(2, 3));

函数指针调用函数规则:函数返回类型(*指针名)(函数参数类型) = 函数名

(*p)(2, 3)和p(2, 3)输出结果一致:*p对函数解引用,而p就等于Add函数 

5、函数指针数组 

简而言之,就是一个数组,能够存放函数的地址 

//函数指针数组
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 (*p[4])(int, int) = {Add, Sub, Mul, Div};
	int i = 0;
	for(i=0; i<4; i++)
	{
		printf("%d\n", p[i](2, 3));
	}
	return 0;
}

函数指针数组的用途:转换器

例(计算器):

void menu()
{
	printf("******************************\n");
	printf("***1、add            2、sub***\n");
	printf("***3、mul            4、div***\n");
	printf("***         0、exit        ***\n");
	printf("******************************\n");
}
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 input = 0;
	int x = 0;
	int y = 0;
	int (*plArr[5])(int, int) = {0, Add, Sub, Mul, Div};
	int ret = 0;
	do
	{
		menu();
		printf("请选择->");
		scanf("%d", &input);
		
		if(input >=1 && input <= 4)
		{
			printf("请输入两个操作数->");
			scanf("%d%d", &x, &y);
			ret = plArr[input](x, y);
			printf("%d\n", ret);
		}
		else if(input == 0)
		{
			printf("退出\n");
		}
		else
		{
			printf("输出有误\n");
		}
	}while(input);
	return 0;
}

对上面的功能,还可以进行这种做法,这里需引入回调函数的概念

回调函数:通过函数指针调用的函数 。当把函数指针(地址)作为参数传给另一个函数,这个指针被用来调用其指向的函数时,就称这个函数为回调函数,如下面的代码:

void print(char* str)
{
	printf("%s\n", str);
}
void test(void (*p)(char*))
{
	p("hello bit");
}
int main()
{
	test(print);
	return 0;
}

 我们称print函数为回调函数

那么上述的计算器也可以使用回调函数进行实现:


void Calc(int (*plArr)(int, int))
{
	int x = 0;
	int y = 0;
	printf("请输入两个操作数->");
	scanf("%d%d", &x, &y);
	printf("%d\n", plArr(x, y));
}
int main()
{
	int input = 0;
	
	do
	{
		menu();
		printf("请选择->");
		scanf("%d", &input);
		switch(input)
		{
		case 1:
			Calc(Add);
			break;
		case 2:
			
			Calc(Sub);
			break;
		case 3:
			
			Calc(Mul);
			break;
		case 4:
			Calc(Div);
			break;
		default:
			printf("输入错误\n");
			break;
		}
	}while(input);
	return 0;
}

 通过封装一个函数Calc(),根据函数指针的地址进行调用函数,从而实现代码

 

 

 

 

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用\[1\]:C语言字节对齐问题详解中提到了C语言中的字节对齐问题。在结构体中,为了提高内存访问的效率,编译器会对结构体进行字节对齐。这意味着结构体的成员在内存中并不是紧凑排列的,而是按照一定的规则进行对齐。具体的对齐规则取决于编译器和编译选项。\[1\] 引用\[2\]:在C语言中,可以使用宏offsetof来获取结构体成员相对于结构体开头的字节偏移量。这个宏非常有用,可以帮助我们计算出每个结构体成员相对于结构体开头的偏移字节数。通过这个宏,我们可以更好地理解结构体的内存布局。\[2\] 引用\[3\]:在C语言中,指针和结构体的组合常常用于处理复杂的数据结构。指针可以指向结构体的成员,通过指针可以方便地对结构体进行操作。指针和结构体的组合可以实现更灵活的数据处理和内存管理。\[3\] 综上所述,C语言中的指针结构体组合可以用于处理复杂的数据结构,而字节对齐问题则是在结构体中为了提高内存访问效率而进行的优化。通过使用宏offsetof,我们可以更好地理解结构体的内存布局。 #### 引用[.reference_title] - *1* *3* [结构体指针C语言结构体指针详解](https://blog.csdn.net/weixin_34069265/article/details/117110735)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [C语言之结构体详解](https://blog.csdn.net/m0_70749276/article/details/127061692)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值