【指针的进阶(1)】指针的类型、数组传参和指针传参


一、指针是什么?

  • 含义:内存被划分为一个个小的内存单元,每个内存单元都有一个编号,编号也称为地址,地址在C语言中被称为指针。
    指针(地址)需要存储起来,存储在变量中,这个变量就被称为指针变量。
int a=10;
int* pa=&a;

比如要存储变量a到pa里,&a就是a的地址,pa就是指针变量,还要说明pa的类型是int*。

  • 指针的大小:在32位平台上是4个字节,在64位平台上是8个字节

二、字符指针

顾名思义是用来存放字符的地址

2.1 字符指针的两种使用方法

在初阶指针,学习了字符指针的一般使用方式:

#include<stdio.h>
//字符指针
int main()
{
    
    
	char ch= "w";
	char* pc =&ch;
	pc = "a";
	return 0;
}

方法思路:改变ch的字符,可以直接用ch来改,但是字符指针pc的好处就是可以代替ch去改,把ch的地址存到pc,pc就可以通过地址找到ch,改变ch的字符,有种借刀杀人的意思。

本章继续学习另一种使用方法

int main()
{
    
    
	//创建arr字符数组,用abcdef给arr初始化
	//相当于在内存中创建数组,里面放了[a b c d e f \0] 
	char arr[] = "abcdef";

	//在x86环境下,p指针是4个字节,abcdef是放不完的
	//不是把abcdef放进p指针
	//思路:给p一个地址,向后打印字符串
	//就是是把首元素a的地址赋给p,让p找到字符串的起始位置
	//注意:字符串是常量字符串,不能被改,在*左边加上const,限制p不能改字符串
	const char* p ="abcdef";

	//打印字符串,要指定格式%s,还要给p一个起始地址,从这个地址向后打印字符串
	printf("%s\n", p);

	//给p解引用,打印字符串的首字符a
	printf("%c", *p);
	return 0;
}

三、指针数组

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

类比:

  • 整型数组:存放整型的数组
  • 字符数组:存放字符的数组
int main()
{
    
    
	int arr[10];//存放整型数组

	int* arr[10];//存放整型指针数组

	char* arr[5];//一级字符指针数组
	
	char* arr[6];//二级字符指针的数组
	
	return 0;
}

四、数组指针

4.1数组指针的定义

是指针,是指向数组变量的指针,存放数组变量的地址的指针变量

类比:

  • 整型指针:指向整型变量的指针,存放整形变量的地址的指针变量
  • 字符指针:指向字符变量的指针,存放字符变量的地址的指针变量

指针数组和数组指针的区别
int* p1[10];
p1是数组,元素有10个,每个元素的类型是int*

int(* p2)[10];
p2和*结合,p2是指针,p2外边是数组int[10],说明p2指向的是数组,所以p2是数组指针变量

4.2 &数组名 VS 数组名

数组名的理解

int main()
{
    
    
	//arr和&arr[0]的地址一样
	//说明数组名是数组首元素的地址
	int arr[10] = {
    
     0 };
	printf("%p\n", arr);
	printf("%p\n", &arr[0]);

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

	return 0;
}

两个例外
1.sizeof(数组名),这里的数组名不是数组首元素的地址, 而是代表整个数组,sizeof(数组名)计算整个数组的大小, 单位是字节
2.&数组名,这里的数组名表示整个数组,&数组名取出的是整个数组的地址 单纯打印出来和首元素地址一样,没有区别,但调试起来发现它们的类型不一样

验证第二点:

int main() 
{
    
    
	int arr[10] = {
    
     1,2,3,4,5,6,7,8,9,10 };
	printf("%p\n", arr);
	printf("%p\n", arr + 1);//跳过一个元素,4个字节
	printf("%p\n", &arr[0]);
	printf("%p\n", &arr[0] + 1);//跳过一个元素,4个字节
	printf("%p\n", &arr);
	printf("%p\n", &arr + 1);//跳过整个数组,40个字节
	return 0;
}

在这里插入图片描述

4.3 数组指针的使用方法

  1. 普通的二维数组传参
void print(int arr[4][5], int r, int c)//数组,行,列接收
{
    
    
	int i = 0;
	for (i = 0; i < r;i++)//第i行
	{
    
    
		int j = 0;
		for (j = 0; j < c; j++)//循环打印第i行的每个元素
		{
    
    
			printf("%d ", arr[i][j]);
		}
		printf("\n");//换行
	}
}
int main()
{
    
    
	int arr[4][5] = {
    
     1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7, 4,5,6,7,8 };
	print(arr, 4, 5);
		return 0;
}
  1. 用数组指针传参

思路:
在这里插入图片描述

void print(int(*p)[5], int r, int c)//用数组指针,行,列接收
{
    
    
	int i = 0;
	for (i = 0; i < r; i++)//第i行
	{
    
    
		int j = 0;
		for (j = 0; j < c; j++)//循环打印第i行中的每个元素
		{
    
    
		
			//p是数组第一行的地址,相当于首元素的地址,p+i是找到数组第i行的地址
			//*(p+i) 是找到第i行中的每一个元素的地址
			//*(p+i)+j 是找到第i行第j列的地址
			//*(*(p + i) + j) 是给地址解引用,找到第i行第j列的那个元素
			printf("%d ", *(*(p + 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;
}

五、数组参数,指针参数

5.1 一维数组传参

  • 一维数组传参可以是数组,也可以是指针
  • 形式上两种都可以,但本质上数组传参就是指针传参,只不过写成数组更直观
void test1(int arr[5], int sz)
{
    
    

}
void test2(int* p, int sz)
{
    
    

}
int main()
{
    
    
	int arr[5] = {
    
     0 };
	test1(arr,5);
	test2(arr, 5);
	return 0;

}

5.2二维数组传参

二维数组传参可以是数组,也可以是指针

void test3(char arr[3][5], int r, int c)//行可以省略,[3][5]可以写成[][5],但不能像数组指针那样写成[5]
{
    
    

}
void test4(char(*p)[5], int r, int c)//行可以省略,[3][5]可以写成[5]
{
    
    

}

int main()
{
    
    
	char arr[3][5] = {
    
     0 };//char或int初始化都是0
	test3(arr, 3, 5);
	test4(arr, 3, 5);
	return 0;
}

需要注意的是:
可以用数组指针传参,也可以用数组传参,但是不能用指针数组传参
错误写法:用指针数组传参:void test (int* arr[5])

5.3一级指针传参

void print(int* p, int sz)
{
    
    
	int i = 0;
	for (i = 0; i < sz; i++)
	{
    
    
		printf("%d ", *(p+i));
	}
}
int main()
{
    
    
	int arr[10] = {
    
     1,2,3,4,5,6,7,8,9,10 };
	int* p = arr;//把首元素的地址赋给指针p
	int sz = sizeof(arr) / sizeof(arr[0]);//计算数组大小
	print(p, sz);//传一级指针p和数组的大小给函数
	return 0;
}

思考:
当一个函数的的参数部分是一级指针时,函数可以接收什么参数?

void test(char* p)
{
    
    

}
int main()
{
    
    

  return 0;
}

思路: 传的实参类型要跟形参类型完全匹配上。

  1. char ch = '2'; //单引号里面只能放一个任何类型的字符
    test(&ch); //传字符变量的地址

  2. char* ptr = &ch;//把ch的地址取出来放进ptr里

    test(ptr);//ptr是char类型的,参数也用char类型的指针接收它

  3. char arr[] = "abcdef";//字符数组

    test(arr);//传字符数组的首元素地址,参数用char*类型接收

5.4二级指针传参

void test(int **ptr)//用二级指针接收
{
    
    

}
int main()
{
    
    
	int n = 10;
	int* p = &n;
	//把n的地址放进指针变量p里,p是一级指针
	
	int** p1 = &p;
	//对一级指针p取地址赋给p1,p1是二级指针
	
	test(p1);
	//第一种传参
	//传二级指针p1,因为p的地址赋给了p1
	
	test(&p);
	//第二种传参
	//传p的地址,因为p接收了n的地址,再传p的地址,&p就是二级指针
	return 0;
}

思考:
当一个函数的的参数部分是二级指针时,函数可以接收什么参数?

void test(char** ptr)
{
    
    

}
int main()
{
    
    
	char n = 'a';
	return 0;
}
  1. char* p = &n;
    test(&p); //把n的地址放进指针变量p里,p是一级指针,再传p的地址,&p就是二级指针

  2. char** p1 = &p;//对一级指针p取地址,p1是二级指针
    test(p1);//可以传二级指针p1, 因为p的地址赋给了p1

  3. char* arr[5];//arr本身是首元素地址,它的元素类型又是char*类型
    test(arr);//所以arr是二级指针,可以用arr传参

    总结

    指针的进阶第一章内容就写到这,如果对你有帮助的话,不妨来一个关注吧!

猜你喜欢

转载自blog.csdn.net/2301_76496134/article/details/131751846