C语言 — 指针进阶篇(上)

前言

指针基础篇回顾可以详见: 指针基础篇(1)指针基础篇(2)

指针进阶篇分为上下两篇,上篇介绍1 — 4,下篇介绍5 — 6

  1. 字符指针
  2. 数组指针
  3. 指针数组
  4. 数组传参和指针传参
  5. 函数指针
  6. 函数指针数组
  7. 指向函数指针数组的指针
  8. 回调函数

一、字符指针

在基础篇中简单介绍过字符指针

1. 字符指针用法

一般使用

int main()
{
	char ch = 'a';
	char* p = &ch;
	*p = 'c';
	return 0;
}

另一种用法

#include<stdio.h>
int main()
{
	const char* psr = "hello word";
	printf("%s\n", psr);
	return 0;
}

输出结果
在这里插入图片描述

输出的结果是hello word,字符串的首字符的地址存放在字符指针psr
即意思是把一个常量字符串的首字符h的地址存放在指针变量 psr

2. 字符指针面试题

演示代码

#include <stdio.h>
int main()
{
 char str1[] = "hello bit.";
 char str2[] = "hello bit.";
 const char *str3 = "hello bit.";
 const char *str4 = "hello bit.";
 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相同。

二、指针数组

在指针基础篇中提到过,指针数组就是存放指针的数组

1. 不同类型的指针数组

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

三、数组指针

1. 数组指针定义

数组指针区别于上面的指针数组,数组指针是指针指针数组是数组。


数组指针和指针数组是两个不同的概念。

数组指针是指一个指向数组的指针变量。它可以用于访问数组元素,也可以作为函数参数传递,可以定义如下:

int (*arr_ptr)[N]; // 定义一个指向长度为 N 的整型数组的指针

这个指针变量可以指向长度为 N 的整型数组,可以通过以下方式初始化:

int arr[N] = {1, 2, 3, 4, 5};
int (*arr_ptr)[N] = &arr; // 指向数组arr的指针

指针数组是指一个包含指针的数组。它可以用来存储一组指针,也可以通过指针访问数组元素,可以定义如下:

int *ptr_arr[N]; // 定义一个包含N个指向整型的指针的数组

这个数组包含 N 个指针变量,每个指针变量可以指向一个整型变量,可以通过以下方式初始化:

int a = 10, b = 20, c = 30;
int *ptr_a = &a, *ptr_b = &b, *ptr_c = &c;
int *ptr_arr[] = { ptr_a, ptr_b, ptr_c };

以上都是基本的定义和初始化方式,具体使用还需要根据具体场景和需求进行操作。

2. &数组名与数组名的区别

演示代码

#include <stdio.h>
int main()
{
 int arr[10] = {0};
 printf("%p\n", arr);
 printf("%p\n", &arr);
 return 0;
}

输出结果
在这里插入图片描述
结论
总所周知,arr是数组名,数组名是首元素的地址
由输出结果得arr和&arr的输出结果相同,其实这两者是存在区别的


在上面的基础上我们对arr和&arr进行+1操作

演示代码

#include <stdio.h>
int main()
{
	int arr[10] = { 0 };
	printf("arr = %p\n", arr);
	printf("arr+1 = %p\n", arr + 1);
	printf("&arr= %p\n", &arr);
	printf("&arr+1= %p\n", &arr + 1);
	return 0;
}

输出结果

在这里插入图片描述

结论

由代码得,arr和&arr输出的值是一样的,但意义不同
&arr表示的是数组的地址,而不是数组首元素的地址
在这里插入图片描述

arr表示首元素地址,首元素地址+1,就是跳过一个元素的指针,( int是4个字节 )就是+4
&arr表示数组地址,数组地址+1,就是跳过整个数组的大小,所以&arr+1对于&arr的差值是40

3. 数组指针的使用

数组指针指向的是数组,那数组指针中存放的应该是数组的地址。

代码演示

#include <stdio.h>
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };
	int(*p)[10] = &arr;//(*p)表示p是一个指针变量,[10]表示有10个元素
	return 0;
}

(*p)表示p是一个指针变量,[10]表示有10个元素


数组指针的使用举例

#include <stdio.h>
void print_arr1(int arr[3][5], int row, int col)
{
 int i = 0;
 for(i=0; i<row; i++)
 {
 for(j=0; j<col; j++)
 {
 printf("%d ", arr[i][j]);
 }
 printf("\n");
 }
}
void print_arr2(int (*arr)[5], int row, int col)
{
 int i = 0;
 for(i=0; i<row; i++)
 {
 for(j=0; j<col; j++)
 {
 printf("%d ", arr[i][j]);
 }
 printf("\n");
 }
}
int main()
{
 int arr[3][5] = {1,2,3,4,5,6,7,8,9,10};
 print_arr1(arr, 3, 5);
 //数组名arr,表示首元素的地址
 //但是二维数组的首元素是二维数组的第一行
 //所以这里传递的arr,其实相当于第一行的地址,是一维数组的地址
 //可以数组指针来接收
 print_arr2(arr, 3, 5);
 return 0;
}

四、数组传参和指针传参

写代码时(以往写冒泡排序时)需要把数组或指针传给函数,下面介绍函数的参数应该如何设计


1. 一维数组传参

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

总结:以上传参都是正确的


2. 二维数组传参

void test(int arr[3][5])//ok
{}
void test(int arr[][])  //NO  形参部分,行可以省略,列不能省略
{}
void test(int arr[][5])	//ok
{}
void test(int *arr)     //NO
{}
void test(int* arr[5])	//NO  形参是指针数组
{}
void test(int (*arr)[5])//ok   形参是数组指针
{}
void test(int **arr)	//NO  二级指针
{}
int main()
{
 int arr[3][5] = {0};
 test(arr);
}

总结:二维数组传参,函数形参的设计只能省略第一个[ ]的数字。因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。这样才方便运算。


3. 一级指针传参

#include <stdio.h>
void print(int *p, int sz)
{
 int i = 0;
 for(i=0; i<sz; i++)
 {
 printf("%d\n", *(p+i));
 }
}
int main()
{
 int arr[10] = {1,2,3,4,5,6,7,8,9};
 int *p = arr;
 int sz = sizeof(arr)/sizeof(arr[0]);
 //一级指针p,传给函数
 print(p, sz);
 return 0;
}

总结:传过去的参数和形参是匹配的上即可


4. 二级指针传参

#include <stdio.h>
void test(int** ptr)
{
	printf("num = %d\n", **ptr); 
}
int main()
{
	 int n = 10;
	 int*p = &n;
	 int **pp = &p;
	 test(pp);
	 test(&p);
	 return 0;
}

总结:传pp和&p都可以,相互对应即可

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

void test(char **p)
{
 
}
int main()
{
 char c = 'b';
 char*pc = &c;
 char**ppc = &pc;
 char* arr[10];	 
 test(&pc);	 //OK
 test(ppc);	 //OK
 test(arr);   //OK
 return 0;
}

五、总结

本文介绍了字符指针、数组指针、指针数组、数组传参和指针传参。

数组指针是指针,指针数组是数组

arr表示首元素地址,首元素地址+1,就是跳过一个元素的指针,( int是4个字节 )就是+4
&arr表示数组地址,数组地址+1,就是跳过整个数组的大小,所以&arr+1对于&arr的差值是40

二维数组传参,函数形参的设计只能省略第一个[ ]的数字。因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。这样才方便运算。


如这篇博客对大家有帮助的话,希望 三连 支持一下 !!! 如果有错误感谢大佬的斧正 如有 其他见解发到评论区,一起学习 一起进步。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Duck Bro

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值