指针相关知识点总结(二)

一、字符指针

重点:区别字符指针和字符数组的本质

#include<stdio.h>
#include<stdlib.h>


int main()
{

	char str1[] = "hello";
	char str2[] = "hello";

	char*p1 = "hello";
	char*p2 = "hello";

	printf("str1=%p\n", str1);
	printf("str2=%p\n", str2);
	printf("p1=%p\n", p1);
	printf("p2=%p\n", p2);

	printf("st1大小:%d\n", sizeof(str1));
	printf("p1大小:%d\n", sizeof(p1));

	system("pause");
	return 0;
}

先上运行结果:

(1)定义了str1和str2两个字符数组,存储的字符串相同。可以看到,输出的str1和str2地址不同。

(2)定义了p1和p2两个字符指针,存储的字符串相同。可以看到,输出的p1和p2地址相同。

为什么会出现这种情况?这里就涉及到字符串的不同定义方式下,存储位置的不同,图解如下

在栈上定义变量时,栈是向下增长的,所以先定义的临时变量地址大,后定义的小(这里可以观察str1h和str2的地址)。对于数组来说,它也在栈上开辟,但是很特别的是:数据开辟空间是整体开辟的,然后把第一个元素的地址作为数组的地址。数组中存储元素的地址是从左到右逐渐增大。str1和str2是字符数组,在栈上开辟空间,我们知道数组开辟空间是一整块的,所以会在栈上开辟一整块的空间来存储字符串

在main函数中定义char*p1="hello",其实是定义了两种变量-->(1)p变量,p是一个字符指针(2)“hello"是在字符常量区开辟空间的。字符常量区的字符是“只读”的,不能被修改。这里char*p1="hello";char*p2="hello",两个hello相同,,所以编译器就认为是同一个字符串,所以p1和p2变量中保存的都是hello的首字符地址,从而输出结果相同

而char str1[]="hello",是在栈上保存的,因为str1是一个数组,在栈上开辟空间,并且,开辟的是一整块空间,用来存储hello。同理,char str2[]="hello"也是在栈上开辟了一整块空间存储hello,str1与str2是两个不同的变量,地址显然不会相同

最后,sizeof(str1)=6,sizeof(p1)=4,也证实了我们的分析:str1开辟了6个字节的空间,存储了hello,而p1是指针的大小,p1中存储的是h的地址。

有了上面的分析,来看这道题-->

#include<stdio.h>
#include<stdlib.h>
int main()
{
	char str1[] = "hello";
	char str2[] = "hello";
	char*p1 = "hello";
	char*p2 = "hello";
	if (str1 == str2)
	{
		printf("aaaaa\n");
	}
	else
	{
		printf("bbbbb\n");
	}

	if (p1 == p2)
	{
		printf("ccccc\n");
	}
	else
	{
		printf("ddddd\n");
	}

	system("pause");
	return 0;
}

运行结果:

二、指针数组,数组指针

开篇重点,筋脉。优先级:()> [  ]>*

指针数组

举例:int*arr[10]

首先:因为优先级【】>*,所以arr先和【】结合,也就决定了它的本质是数组;数组中放的元素是指针,所以它是一个指针数组

举例:char*arr[10]  字符型指针数组

           char**arr[10]  二级字符型指针数组

总结:

step1:先看本质是什么?(优先级决定)

step2:再看数组中放的是什么?放的是整型就是整型数组,放的是指针就是指针数组

三、&arr与arr

#include<stdio.h>
#include<stdlib.h>
int main()
{

	int a = 6;
	int arr[10] = { 0 };
	printf("arr的地址:%p\n", arr);
	printf("&arr的地址:%p\n", &arr);

	printf("arr+1的地址:%p\n", arr+1);
	printf("&arr+1的地址:%p\n", &arr+1);

	system("pause");
	return 0;
}

运行结果:

可以看到,虽然arr与&arr在数值上相同,但是通过+1输出的结果,我们可以看出它俩并不是一个概念。arr是数组名,代表数组首元素地址,但是要注意,这里所说的“元素”并不一定是一个原生的类型(如int,float,char),“元素”指的是数组中放的是什么。我们把所有的数组全部都当成一维数组,一维数组中放一维数组就是二维数组,这个“一维数组里面的一维数组”它的元素是什么?就是数组。所以,这里的“元素”要格外注意。

在大部分情况下,数组名代表数组首元素的地址(尤其是数组名参与运算时,比如arr+1,arr+2);只有在以下两种情况下,数据名代表整个数组

首元素的地址是元素的地址,数组的地址是数组的地址。它俩不一样(如何证明?arr+1和&arr+1)

(1)sizeof(arr)

(2)&arr

 

arr+1 指向的是下一个元素

&arr+1指向的是下一个数组

综上:&arr相当于一个数组指针


再看一道例子,深刻理解类型

int(*p)[10];
int a[10];
p = a;

可以看到,有警告,说明类型不同

int(*p)[arr]的类型是数组指针,arr的类型是元素指针(整型指针),所以类型不一样

改为:p=&a则编译通过,说明两个类型一样了。&a是数组的地址,地址本质也是指针,所以就是数组的指针。这样一来,类型就一样了。

 

如果把数组指针的类型改为以下:

char(*p)[10];
int a[10];
p = &a;

可以看到,出现警告。

数组也有类型,数组指针同理。一个是整型数组指针,一个是char类型数组指针,类型当然不同。所以在定义类型时,必须一样

如果把数组大小改为以下:

int(*p)[9];
int a[10];
p = &a;

综上两个例子,也说明了数组的类型由原生类型+数组的大小共同决定。所以以后在定义数组和定义指针时,数组的大小也是作为数组类型的一部分,一定要注意。

四、二维数组的传参

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.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);
	print_arr2(arr, 3, 5);
	system("pause");
	return 0;
}

观察这两种传参方式

数组在传参时会降维成指向其内部元素类型的指针,这个指针叫做数组指针。

arr[3][5]内部元素是一维数组。我们可以把arr[3][5]看做是有三个元素的一维数组,其中每一个元素又是一个一维数组,它有5个元素。所以里面放的是什么?一维数组;什么一维数组?整型一维数组。所以它里面放的是具有5个元素的整型一维数组。arr[3][5]发生降维是降维成了整型数组指针。

我们可以这样认为。arr[3][5]是一个一维数组,里面的元素是arr[5],在传参时会发生降维,降维成指向其内部元素类型的指针,而内部元素是一个一位数组,所以,降维成了数组指针。

下面一段代码就验证了二维数组在传参时会降维成指针-->

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
void print1(int a[3][5], int r, int c)
{
	printf("%d\n", sizeof(a));
}

void print2(int (*p)[5], int r, int c)
{
	printf("%d\n", sizeof(p));
}
int main()
{
	int a[3][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	print1(a, 3, 5);
	print2(a, 3, 5);
	system("pause");
	return 0;
}

运行结果:

问题二:上面的代码二维数组传参时为什么3可以被忽略,而5不行?

(1)忽略3

void print1(int a[][5], int r, int c)
{
	printf("%d\n", sizeof(a));
}

(2) 忽略5

void print1(int a[3][], int r, int c)
{
	printf("%d\n", sizeof(a));
}

a[3][]其实是一个指针,准确来说是数组指针。对于数组而言,数组的维度也是它类型的一部分。也就是说,如果把第二个数组当中的数字忽略了,那么这个数组指针当中的数组类型其实就不完整了,数组的概念都不完全了,数组指针的类型当然也不明确了。

如果改为-->

void print1(int a[][6], int r, int c)
{
	printf("%d\n", sizeof(a));
}

元素当中的数据也是类型的一部分,所以它的大小也必须一致。

对于一维数组传参,维度其实无所谓,可有可无(对于如下代码,都可以正常编译通过)

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
void fun(int a[200], int num)
{}
int main()
{
	int b[10];
	fun(b, 10);
	system("pause");
	return 0;
}

五、数组参数和指针参数

(1)一维数组传参

void test(int arr[])//正确
{}
void test(int arr[10])//正确
{}
void test(int*arr)//正确
{}

void test2(int*arr[10])//正确,类型一致,都是指针数组,作为参数传递时会降维成int**
{
	printf("%d", sizeof(arr));
}

void test2(int**arr)
{}
int main()
{
	int arr[10] = { 0 };

	int*arr2[20] = { 0 };//指针数组
	test(arr);

	test2(arr2);
	
	system("pause");
	return 0;
}

(2)二维数组传参

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
void test(int arr[][])//错误,只能忽略第一个维度“int (*)[1]”和“int [3][5]”数组的下标不同
{}

void test(int arr[][5])//正确
{}

void test(int*arr)//错误,这里是一个整型指针。“函数”:“int *”与“int [3][5]”的间接级别不同
{}

void test(int* arr[5])//错误,它是指针数组,在传参时会降维成int**。“函数”:“int **”与“int [3][5]”的间接级别不同
{}

void test(int(*arr)[5])//正确,数组指针
{}

void test(int**arr)//错误,它是指针的指针。“int **”与“int [3][5]”的间接级别不同	 
{}

int main()
{
	int arr[3][5] = { 0 };//二维数组,在传参时会降维成数组指针,类型为int*arr[5]
	test(arr);

	system("pause");
	return 0;
}

(3)一级指针传参

当函数的参数部分为一级指针时,函数能够接收的参数-->

void test1(int*p){ } 

可以接收的参数:整型指针

                                整型变量的地址

                                整型数组

void test2(char*p){ }

可以接收的参数:字符指针

                                字符型变量的地址

                                字符数组

(4)二级指针传参

#include<stdio.h>
#include<stdlib.h>
void test(int** ptr)
{
	printf("num=%d\n", **ptr);
}

int main()
{
	int n = 10;
	int *p = &n;
	int**pp = &p;

	test(pp);//传int**变量
	test(&p);//传int*p,把&p传进去

	system("pause");
	return 0;
}

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值