C语言-指针(2)

--------------------------------------------------------------

------------------------------------------

---------------------

---------

目录

数组名的理解

 指针数组

指针数组模拟二维数组

指针访问数组

 一维数组传参的本质

 二级指针

 字符指针变量

 指向同一字符串的指针(地址)相同

 数组指针

 数组指针的初始化

数组指针的运用 


数组名的理解

1.当定义一个一维数组时,其数组名就是该数组的首地址。以下我们做了个实验每个数组的元素都有地址,我们使用&arr[0]取数组的首地址,在跟arr进行对比,我们发现数组名就是首地址。

 2.数组名就是首元素地址,有两个例外:

1.sizeof:通常被我们使用来计算数组的长度(sizeof(arr)/sizeof(arr[0])),而sizeof括号里面的arr表示的不是数组名而是整个数组计算的是数组的大小,单位是字节。

2.&arr:这里的取地址数组名表示的是整个数组的地址

除此之外的数组名arr都表示的是首元素的地址。

我们可以发现sizeof(arr)存储的是40个字节的空间,所以sizeof表示的是整个数组;而&arr[0]和arr增一后内存为其多开辟了4个字节的空间,而&arr则多了40个字节跳过了整个数组的长度,所以&arr表示的是整个数组的地址

 指针数组

1.定义:存放指针的数组(数组中的每一个元素都是指针类型);

2.形式:int*arr[3],数组存放三个元素,每个元素都是整形指针

指针数组模拟二维数组

#include<stdio.h>
int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 2,3,4,5,6 };
	int arr3[] = { 3,4,5,6,7 };
	int* arr4[] = { arr1,arr2,arr3 };
	//数组名是首元素的地址,类型是int*的
	//就可以存放在arr4数组中
	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 5; j++)
		{
			printf("%d ",arr4[i][j]);
		}
		printf("\n");
	}
	return 0;
}

arr4[i]访问arr4数组的元素,arr4[i]找到了数组元素指向的一维数组,arr4[i][j]就是数组一维数组中的元素。 

以上是模拟的二维数组,但并不是真正的二维数组,因为每行不是连续的

int* arr4[] = {arr1,arr2,arr3},因为数组名是首元素的大小,所以arr[i]先访问的是首元素地址,在经过循环+j将数组的行数依次打印出来 

指针访问数组

#include<stdio.h>
int main()
{
	int arr[10] = { 0 };
	int* p = arr;                          //取首元素的地址
	int rs = sizeof(arr) / sizeof(arr[0]); //计算数组长度
	for (int i = 0; i < rs; i++)
	{
		scanf_s("%d", p + i);              //利用for循环输入,现在的p存储的就是首元素的地址
		                                   //也可以这么写arr+i
	}                                      //首地址+i相当于跳过i个整形字节并找到对应数组下标的元素
	for (int i = 0; i < rs; i++)
	{
		printf("%d ",*(p+i));              //*(p+i)相当于*(arr+i),而*(arr+i)可以写成arr[i]
	}                                      //那么*(p+1)也可以写成p[i],因为加法是满足交换律的还可以写成
	                                       //i[p],i[arr],不过最常用的还是*(p+i)或者arr[i]
	return 0;
}

数组元素的访问在编译器处理的时候,也是换成首元素地址arr+偏移量i求出数组元素下标地址,然后解引用访问。

 一维数组传参的本质

我们之前都是在函数外部计算数组元素个数,那我们可以把函数传给一个函数后,函数内部求数组元素个数吗?

函数内部没有正确的获得数组元素的个数。

数组传参的本质就是传递的是数组首元素的地址。 

函数形参理论上应该使用指针变量来接收首元素的地址

而我们在函数内部我们写sizeof(arr)计算的是一个地址的大小而不是数组的大小

正因为函数的参数部分本质是指针,所以函数内部是没有办法求数组的元素个数的

void test(int arr[])
void test(int* arr)

 一维数组传参既能写成数组形式也能写成指针形式


 二级指针

指针变量也是变量,是变量就有地址,二级指针的作用就是用来存放一级指针变量的地址,相当与我们数学的二次求导

#include <stdio.h>
int main()
{
	int a = 10;
	int* pa = &a;
	int** paa = &pa;
	printf("%d",**paa);
	return 0;
}

 

我们通过对**paa进行解引用我们找到的是*pa,而*pa访问的是a

所以**paa解引用的结果是10

*ppa前面的*说明ppa是一个指针变量

int*说明paa指向的类型int*

---------------------------------------------

------------------------------

-----------------------

-----------------

-----------

 字符指针变量

1.在指针类型中我们知道一个指针类型char*,我们可以用来存储单个字符:

int main()
{
 char ch = 'a';
 char *pc = &ch;      //pc就是字符指针
 printf("%c\n",*pc);
 return 0;
}

 但是如果指针char*类型存储一个字符串存储的是整个字符串的地址吗?

#include<stdio.h>
int main()
{
	char* p = "helloworld";
	printf("%c\n", *p);
	return 0;
}

我们发现char*类型不是把字符串“helloworld”放入p中,而是将字符串的首地址放进p中存储

我们可以将字符串想象成一个字符数组,可以写成“helloworld”[3],此时访问的也就是'l'。

想象成的常量字符数组是不能修改的。

#include<stdio.h>
int main()
{
	char* p = "helloworld";
	printf("%c\n", *p);
	printf("%c\n", "helloworld"[3]);
	//因为p存储的"helloworld"的首元素地址,我们前面说到数组名就是首元素地址
	//所以还可以这么写
	printf("%c\n", p[3]);
	return 0;
}

 

我将想象的字符数组的下标3的元素修改成a,但是编译器打印不了,所以是不可修改的。

为了避免这种情况可以在 char*类型前面加上const

 指向同一字符串的指针(地址)相同

1.指向相同元素的指针地址是相同的(指针类型相同的字符串只会保留一个地址);

2.而面对相同的字符串,数组会在开辟一个空间进行存储

#include<stdio.h>
int main()
{
	char ch1[] = "hello world";
	char ch2[] = "hello world";
	const char* ch3 = "hello world";
	const char* ch4 = "hello world";
	if (ch1 == ch2)
	{
		printf("地址相同\n");
	}
	else
	{
		printf("地址不同\n");
	}
	if (ch3 == ch4)
	{
		printf("地址相同\n");
	}
	else
	{
		printf("地址不同\n");
	}
	return 0;
}

通过编译器的监视我们可以发现:ch3和ch4的地址是相同的ch1和ch2的地址是不相同的。 

 数组指针

定义:数组指针就是存放数组的指针

与指针数组的区别:指针数组是数组,存放指针的数组

int *p1[10];
//p1是数组,数组类型10个元素,每个元素的类型是int*,所以p1是指针数组
int (*p2)[10];
//p2是指针,指针指向的是数组,数组有10个元素,每个元素的类型是int,所以p2是数组指针

 1.[ ]的优先级要高于*号,所以必须加上()来保证p先和*结合,变成指针类型。p先和*结合,说明p是一个指针变量。

 数组指针的初始化

#include<stdio.h>
int main()
{
	int arr[5] = { 1,2,3,4,5 };
	&arr;   //得到数组的地址
	int(*arr)[5] = &arr;
	//将数组的地址放在数组指针中
	return 0;
}
int (*p)[5] = &arr
 |    |  |
 |    |  |
 |    |  p指向的数组元素个数
 |    |p是数组指针变量名
 |p指向的数组元素类型

数组指针的运用 

数组指针主要运用在多维数组中:

二维数组传参的本质就是传递一维数组的指针

#include<stdio.h>
void test(int arr[3][5], int a, int b)
{
	for (int i = 0; i < a; i++)
	{
		for (int j = 0; j < b; j++)
		{
			printf("%d ",arr[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} };
	test(arr, 3, 5);
	return 0;
}

根据数组名是数组首元素地址的这一规则,二维数组的数组名表示的就是第一行的地址,是一维数组的地址。根据上面的例子,第一行的一维数组的类型就是int [5],所以第一行的地址类型就是数组的指针类型int(*)[5]。二维数组的传参本质就是传递了一个一维数组的指针。 

#include<stdio.h>
void test(int (*p)[5], int a, int b)
{
	for (int i = 0; i < a; i++)
	{
		for (int j = 0; j < b; 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} };
	test(arr, 3, 5);
	return 0;
}

 所以可以把传参的数组写成指针的形式:int(*p)[5]。

----------------------------------------------------------

-----------------------------------

-------------------------

-----------------

-----------

------

 -------

------------------

-------------------------

----------------------------------------

------------------------------------------------------

感谢老铁支持!

猜你喜欢

转载自blog.csdn.net/2301_79201049/article/details/134390126
今日推荐