【C语言】指针进阶1

我们首先回顾指针的一些概念:

  1. 指针就是个变量,用来存放地址,地址唯一标识一块内存空间。
  2. 指针的大小是固定的4/8个字节(32位平台/64位平台)。
  3. 指针是有类型的,指针的类型决定了指针的±整数的步长,指针解引用操作的时候的权限。
  4. 指针的运算。

字符指针

int main()
{
	char c='w';
	char* pc=&c;//pc就是字符指针
	const char* p="abcdef";//把字符串首字母地址存到指针变量p,或者表示为p指向字符串“abcdef”,并且要求是常量字符串,不能被改,所以最好加上const
	printf("%d\n",*pc);
	printf("%s\n",p);
	printf("%c",*p); 
	return 0; 
}

结果
在这里插入图片描述
分析下列代码:

int main()
{
	char str1[]="hello";
	char str2[]="hello";
	const char* str3="hello";
	const char* str4="hello";
	
	if(str1==str2)
		printf("1=2\n");
	 else
	 	printf("1!=2\n");
	if(str3==str4)
		printf("3=4");
	 else
	 	printf("3!=4");	 	
	return 0; 
}

在这里插入图片描述
解析:
分别创建两个数组str1和str2,str1和str2分别是两个数组名,而数组名是数组首元素的地址,这两个数组是两块独立的空间,所以地址不可能相同。
const 一个常量字符串,开辟一块空间存放该字符串,然后有两个字符指正str3 和str4都指向该常量字符串的首字母地址,因为是同一快空间,所以两个值相同。

指针数组

整形数组 - - 存放整形的数组
字符数组 - - 存放字符的数组
指针数组 - - 存放指针的数组 ( 指针数组是数组 )

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

指针数组模拟二维数组的实现:

int main()
{
	int arr1[]={1,2,3,4,5};//arr1(数组名代表首元素地址)---> 类型是int*
	int arr2[]={2,3,4,5,6};
	int arr3[]={3,4,5,6,7};
	//指针数组
	int* arr[3]={arr1,arr2,arr3};
	for(int i=0;i<3;i++)
	{
		for(int j=0;j<5;j++)
		{
			printf("%d ",arr[i][j]);
		}
		printf("\n");
	}
	return 0;
}

结果
在这里插入图片描述

数组指针

整型指针 - - 指向整型变量的指针,存放整型变量的地址
字符指针 - - 指向字符变量的指针,存放字符变量的地址
数组指针 - - 指向数组的指针,存放数组的地址 ( 数组指针是指针 )

指针数组和数组指针
int* p1[10];//指针数组--存放int*类型的数组
int (*p2)[10];//()让*先与p2结合,说明p2是指针,指向的是一个大小为10个整型的数组
  1. 数组名的理解
    数组名是数组首元素的地址,但是又两个例外:
    (1)sizeof( 数组名),这是的数组名表示整个数组,计算的是整个数组的大小,单位是字节
    (2)&数组名,这里的数组名表示整个数组,&数组名 取出的是整个数组的地址
    除此之外所有的数组名都是数组首元素的地址
int main()
{
	int arr[10]={0};
	printf("%p\n",arr);
	printf("%p\n\n",arr+1);
	
	printf("%p\n",&arr[0]);
	printf("%p\n\n",&arr[0]+1);
	
	printf("%p\n",&arr);
	printf("%p\n",&arr+1); 
	return 0;
}

结果:
在这里插入图片描述
解析:
第一组:arr数组名代表数组首元素地址,arr+1地址加1,每个地址是4个字节,所以结果加4
第二组:&arr[0]取出数组第一个元素的地址,&arr[0]+1第一个元素地址加1,每个地址是4个元素,所以结果加4
第三组:&arr,这里的arr代表整个数组,&arr取出的是整个数组,这个数组中由10个元素,每个元素是四个四节,所以最终结果加40

计算验证:
十六进制转化为十进制:(0xDF0)=13×16²+15×16¹+0×16º=3568
十六进制转化为十进制:(0xE18)=14×16²+1×16¹+8×16º=3608
所以:(0xDF0)-(0xE18)=3608-3568=40

使用数组指针访问数组内元素:
int main()
{
	int arr[10]={0,1,2,3,4,5,6,7,8,9};
	int (*p)=arr;
	for(int i=0;i<10;i++)
	{
		printf("%d ",*(p+i));
	}
	return 0;
}
但是这样比直接用数组更麻烦,那么数组指针在什么情况下使用?
数组指针一般在二维数组上才方便
二维数组的每一行都可以理解为二维数组的一个元素,每一行又是一个一维数组,所以,
二维数组其实可以是一维数组的数组

//二维数组传参,形参是数组形式 
void Print1(int arr[3][5],int r,int c)
{
	int i=0;
	for(i=0;i<3;i++)
	{
		int j=0;
		for(j=0;j<5;j++)
		{
			printf("%d ",arr[i][j]);
		}
		printf("\n");
	}
}
//二维数组传参, 形参是指针形式
void Print2(int (*p)[5],int r,int c)
{
	int i=0;
	for(i=0;i<r;i++)
	{
		int j=0;
		for(j=0;j<c;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};
	//arr是数组名表示数组首元素地址 
	Print1(arr,3,5);
	printf("\n");
	Print2(arr,3,5);
	return 0;
}

数组传参和指针传参

1.一维数组传参

void test(int arr[])//正确
{}
void test(int arr[10])//正确
{}
void test(int *arr) //正确
{}
void test1(int *arr[20])//正确
{}
void test1(int **arr)//正确,传来指针数组元素的地址,第一个解引用对应第一个数组指针类型元素的地址,第二个解引用是对指针类型的元素
int main()
{
	int arr[10]={0};
	int *arr2[20]={0};
	test(arr);
	test1(arr2);
}

2.二维数组元素传参

void test(int arr[3][5])//正确
{}
void test(int arr[][])//错误
//二维数组传参,函数形参的设计只能省略第一个[]的数字
//因为对一个二维数组,可以不知道有多少行,但是必须知道有多少列,这样才方便运算
{}
void test(int arr[][5])//正确
{}
void test(int *arr)//错误
//arr是首元素的地址,是一行的地址,绝对不是一个整形的地址,所以肯定错误
{}
void test(int *arr[5])//错误
//这个形参是指针数组,但是形参只能是二维数组或者指针
{}    
void test(int (*arr)[5])//正确
{}
void test(int **arr)//错误
//不能是二级指针
{}
int main()
{
	int arr[3][5]={0};
	test(arr);
}

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]={0,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;
 } 

在这里插入图片描述
思考:
当一个函数的参数部分为一级指针的时候,函数能接收什么参数?

void test(char *p)
{}
char ch='2';
test(&ch);//正确
char *ptr=&ch;
test(ptr);//正确
char arr[]="abcdef";
test(arr);//正确

4.二级指针传参

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;
}

思考:
当一个函数的参数部分为二级指针的时候,函数能接收什么参数?

void test(char** p)
{}
char n='a';
char* p=&n;
char **pp=&p;
char* arr[5];
test(&p);
test(pp);
test(arr);//都正确

函数指针

数组指针–指向数组的指针
函数指针–指向函数的指针

int Add(int x,int y)
{
	return x+y;
}
int main()
{
	printf("%p\n",&Add);//&函数名是函数的地址
	printf("%p\n",Add);//函数名也是函数的地址

	int (*pf)(int, int)=&Add;//pf是函数指针类型
	//int (*)(int, int)是函数指针类型
	
	return 0;
}

在这里插入图片描述

同理,求出下列函数指针:

void test(char* pc,int arr[10])
{}
int main()
{
	pf?=&test; 
	return 0;
}

答:void (pf)(char,int[10])=&test;

函数指针的调用

int Add(int x,int y)
{
	return x+y;
}
int main()
{
	int r=Add(3,5);//函数调用
	printf("%d\n",r);
	
	int (*pf)(int, int)=&Add;
	int m=(*pf)(4,5);//函数指针调用
	
	return 0;
}
  • 8
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值