C语言之指针进阶(上)

目录

前言

字符指针

 指针数组和数组指针

指针数组

 数组指针

数组指针的应用

数组参数

一维数组传参

 二维数组传参

指针参数

一级指针传参

二级指针传参


前言

函数?

嗯,我懂。

数组?

嗯,我懂。

指针?

嗯,我也懂。

字符指针?整形指针?数组指针?指针数组?函数指针?函数指针数组?指向函数指针数组的指针?

。。。。。。

我知道你很急,但你先别急,这期,咋们来一一探讨 。

字符指针

什么是字符指针,简单来说,字符指针仍然是一个指针变量,只不过指针变量指向的变量的类型是字符型。 

int main() {

	int a = 0;
	int* p = &a; //p是一个整形指针,指向的变量的类型是整型
	char b = 'A';
	char* p1 = &b;	//p1是一个字符指针,指向的变量是字符型

}

上面是字符指针最常用的一种应用,下面这种是不经常实用的:

int main() {
	
	char ch[10] = "helloworld";
	char* p3 = "helloworld";
	printf("%s\n",p3);
	printf("%c\n",*p3);
	return 0;

}

暂且不讨论运行结果,有些人认为指针变量p3指向了后面的整个字符串,实质上是这样吗?实际上并不是这样,p3是一个字符指针,我们规定p3指向了后面的字符串的第一个元素'h'。

 通过运行结果不难发现,p3确实指向了字符串的第一个元素。

这里需要注意:后面的字符串"helloworld"是一个常量字符串,他在内存中只开辟了一块空间,也就意味这不管创建了多少个指针变量,且这些指针变量都指只向了"helloworld"的第一个元素'h'所在的同一块空间。

 指针数组和数组指针

指针数组

指针数组我们初阶已经讲过,简单回顾一下:

指针数组,指针可以理解为定语,数组可以理解为主语,所以指针数组本质上就是一个数组,只不过指针数组的每个元素就是一个指针变量。

int main() {
	int* arr1[10]; //整形指针的数组
	char* arr2[4]; //一级字符指针的数组
	char** arr3[5];//二级字符指针的数组
	return 0;
}

 数组指针

数组指针,数组是定语,指针是主语,所以数组指针本质上就是一个指针变量。我们之前学过整型指针,字符指针,整形指针存放的是整型变量的地址,字符指针存放的是字符变量的地址,同理,数组指针存放的就是数组的地址。但我们需要注意数组指针的写法:

int main() {
	int arr[10] = { 1,2,3,4,5,6,7,8 };
	int(*p)[10] = &arr;    //数组指针
	return 0;

我们这里需要区分一下数组指针和指针数组的写法:

 int* arr1[10]; 

因为[]的优先级高于*,所以arr1先和[]结合,表明arr1本质上是一个数组,然后又跟*结合,这就表明,上述代码表明arr1是一个指针数组。

int(*p)[10] = &arr;    

p先于*结合,表明p是一个指针变量,然后又与[]结合,表明p指向了一个长度是10的数组,且数组的每个元素的类型都是int型

数组指针的应用

我们之前学过了二维数组的遍历,下面我们用数组指针的方式进行二维数组的遍历。

void print_arr1(int arr[3][5], int row, int col)
{
	int i = 0;
	for (i = 0; i < row; i++)
	{
		int j = 0;
		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++)
	{
		int j = 0;
		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);
	print_arr2(arr, 3, 5);
	return 0;
}

运行结果: 

解析:在数组那期我们讲过,数组名表示数组首元素的地址,但是有两种情况除外,sizeof(数组名)和&数组名,这两种情况下,数组名都表示整个数组。

int main(){

    int arr[10]={1,2,3,4,5};
    arr; //表示数组首元素的地址
    &arr[0]; //表示数组首元素的地址
    &arr; //表示整个数组的地址


}

这三者的区别在数组那一期我们讲过,今天我们便不再过多阐述,现在我们要讨论得的是二维数组首元素的地址:

二维数组不像一维数组,以为数组首元素的地址是数组的第一个元素的地址,但是二维数组跟一维数组是有区别的,在二维数组中,我们规定二维数组首元素的地址就是第一行数组的地址,即就是第一行数组的地址,切记,是第一行一维数组的地址,所以,二维数组的数组名就是它首元素的地址,即就是第一行的一维数组的地址,所以如果将它传给了形参,应该用数组指针去接收。这便是上述数组指针应用的解释。

数组参数

一维数组传参

什么是数组参数呢?

我们当时在函数调用时讲过,函数调用涉及到两个参数,形参和实参,数组参数也是一样的,此时的数组就是我们的实参,将数组传给形参之后,这个形参的类型应该是什么,这便是我们接下来要讨论得问题? 我们先分析下面这块代码:

void test(int arr[])	//1
{}
void test(int arr[10])	//2
{}
void test(int* arr)	//3
{}
void test2(int* arr[20])	//4
{}
void test2(int** arr)	//5
{}
int main()
{
	int arr[10] = { 0 };
	int* arr2[20] = { 0 };
	test(arr);
	test2(arr2);
	return 0;
}

解析对于1,2,3。main函数中我们调用test函数时,实参传的是arr,即数组名,即数组首元素的地址, 当我们理解为数组名时,可以理解为传过去了整个数组,所以test函数中的形参,我们可以定义两个数组去接收,当我们理解为数组首元素的地址时,因为数组的首元素是int类型,所以test函数中的形参我们应该用int*类型的指针去接收。

            对于4,5,arr2是一个指针数组,main函数中我们调用test2函数时,实参是arr2,即数组名,即数组首元素的地址,当我们理解为数组名时,可以理解为传过去了整指针个数组,所以test2函数中的形参,我们可以定义一个指针数组去接收,当我们理解成数组首元素的地址时,因为数组的首元素是int*类型,所以形参我们应该用int**类型的指针去接收。

 二维数组传参

分析下列代码:

void test(int arr[3][5])    //1
{}
void test(int arr[][])    //2
{}
void test(int arr[][5]    )//3
{}
void test(int *arr)    //4
{}
void test(int* arr[5])    //5
{}
void test(int (*arr)[5])    //6
{}
void test(int **arr)    //7
{}
int main()
{
int arr[3][5] = {0};
test(arr);
}

解析 :main函数中调用test函数时实参是arr可以理解为数组名,也可以理解为数组首元素的地址:

             当实参arr被理解成数组名时,1,3可以作为形参去接收arr,但是2不行,因为二维数组的行可以省略,但是列不能省略。不清楚的小伙伴可以去数组那一期去回顾一下。

             当实参arr被理解成数组首元素的地址时,上面我们刚刚分析过,二维数组首元素的地址其实就是数组第一行的一维数组的地址,即二维数组首元素的地址就是一个数组的地址,所以实参就是一个数组的地址,所以形参我们进行接收时,实参必须是一个数组指针去接收,所以只有6可以,4,5,7是不行的。

指针参数

指针参数其实就是当实参是指针时,形参该是什么类型。

一级指针传参

先看下列代码:

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]);
	print(p, sz);
	return 0;
}

解析: 指针传参其实相当于数组传参,其实更容易理解,上面的数组进行遍历时,实参传的是指针变量p,所以形参进行接收时,形参变量的类型自然可以使用与实参变量相同的int*类型去接收。

二级指针传参

先看下列代码:

void test(int **p) {
	printf("%d\n",**p);
}
int main() {

	int a = 10;
	int* p = &a;
	int** pp = &p;
	test(pp);
	test(&p);
	return 0;
}

解析: main函数中的函数进行调用时,实参分别是二级指针和一级指针的地址,当实参为二级指针时,当然形参的类型可以是二级指针。当实参为一级指针的地址时,当然也可以用二级指针进行接收。

指针进阶到此还没结束,欲知后事如何,请看下回分解,如果一次讲完,就会:

 好了本期内容到此结束!^_^

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

棠~

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

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

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

打赏作者

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

抵扣说明:

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

余额充值