前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【进阶指针一】字符数组&数组指针&指针数组

【进阶指针一】字符数组&数组指针&指针数组

作者头像
MicroFrank
发布2023-01-16 11:35:02
9230
发布2023-01-16 11:35:02
举报
文章被收录于专栏:同步文章1234同步文章1234

目录

1.初阶指针内容回顾

2.字符指针

2-1 字符指针长什么样?

2-2 误区:

2-3  代码一和代码二的异同:

2-4 关于字符常量区:

2-5 一道为了区分栈区和字符常量区&&字符数组和字符指针的面试题:

 3.指针数组

3-1 指针数组长什么样捏?

3-2 初级使用(或者说给你看一下基本使用):

3-3这才是指针数组的正确使用方法!【指针数组模拟打印二维数组】

 4. 数组指针

4-1 区分取地址数组名和数组名(老生常谈了)

4-2 辨析数组指针和指针数组

 4-3 学会了?来看一个小测试题

4-4  来看一个脱裤子放屁的代码【看一看数组指针的使用】

 4-5 这才是数组指针的正确使用方法捏【数组指针模拟打印二维数组】

5 测试题和规律总结

测验1:

测验2:那么指针数组指针?

我总结的一点小规律


1.初阶指针内容回顾

1.内存被划分为小的内存单元,每个内存单元都有一个编号,这个内存编号就是所谓的地址,也被叫做指针(内存编号=地址=指针)。 2.指针变量是一个变量,存放的是地址,地址能够唯一标识一块内存单元。 3.指针变量大小是固定的4/8个字节(32/64位平台上)。 4.指针变量类型决定了(1)指针在+-整数时的跳过多少个字节;(2)指针在解引用的时候访问的权限。

2.字符指针

2-1 字符指针长什么样?

代码语言:javascript
复制
int main()
{
    //代码一:
	char ch = 'a';
	char* p1 = &ch;
	printf("字符'a'的地址:>%p\n", p1);
	printf("对指针解引用得到的是指针指向的那个目标:>%c\n", *p1);
	printf("\n\n\n");

    //代码二:
	char* p2 = "hello world";
	printf("字符'h'的地址:>%p\n", p2);
	printf("对指针解引用得到的是指针指向的那个目标:>%c\n", *p2);
	printf("%s\n", p2);
	return 0;
}

2-2 误区:

误以为代码2中p2指针变量是存放着字符串,实际上p2所指向的是字符串"abcdef”中首个字符'a'的地址(原因有两点:1.指针变量在x86位(32位机器下)是4个字节,但是"abcdef“有7个字符,一个指针只能存放一个地址;2.通过指针解引用打印出'a'),同时因为字符串"abcdef"在内存(字符常量区)中的空间是连续的,所以只要拿到字符串首个元素'a'的地址就可以访问到整个字符串的内容。

2-3  代码一和代码二的异同:

1. 同:

(1)指针类型:p1和p2都是字符指针变量,都是存放的是字符a的地址。 (2)打印字符'a:打印出'a'都是通过拿到字符指针变量内存放的地址进行解引用,得到的都是指针变量所指向的那个变量。

2. 异

(1)字符'a'的存储位置:代码1中的'a'存放在栈区,代码2中的'a'存放在字符常量区(通过下方截图可以证明)

2-4 关于字符常量区:

对于上图的解释3: - 既然位于字符常量区的"abcdef"是不允许修改的 那么在p2指向这块内存空间的时候就会产生隐患,一旦通过解引用试图修改就会造成程序的运行时错误,程序瘫痪; - 因此使用const修饰(也就是const char* p2="abcdef")来阻止对指针解引用试图修改的行为 ,及时给出编译时错误,程序压根编译不通过。

2-5 一道为了区分栈区和字符常量区&&字符数组和字符指针的面试题:

代码语言:javascript
复制
int main()
{
	const char* ptr1 = "abcdef";
	const char* ptr2 = "abcdef";

	char arr1[] = "abcdef";
	char arr2[] = "abcdef";

	if (ptr1 == ptr2)
	{
		printf("ptr1==ptr2\n");//bingo
	}
	else
	{
		printf("ptr1!=ptr2\n");//error
	}

	if (arr1 == arr2)
	{
		printf("arr1==arr2\n");//error
	}
	else
	{
		printf("arr1!=arr2\n");//bingo
	}
	return 0;
}

 解释:

 3.指针数组

其实下面要讲的指针数组和数组指针都是一种类型(类似整型,double,float)

3-1 指针数组长什么样捏?

代码语言:javascript
复制
//整型数组
int arr1[5]={1,2,3,4,5};//存放整型的数组

//字符数组
char arr2[5]={'a','b','c,''d','\0'};//存放字符的数组

//指针数组:指针是修饰,数组是名词,主角
int* arr3[5]={&arr1[0],&arr1[1],&arr1[2] ,&arr1[3] ,&arr1[4]};//存放指针的数组

//指针数组的意义:看着食之无味,弃之可惜
//实际上作用大着捏,我们指针数组的意义和普通数组的意义类似,
//都是对方便对相同类型元素(在这里的类型元素是指针)统一处理:比如修改和打印

3-2 初级使用(或者说给你看一下基本使用):

代码语言:javascript
复制
int main()
{
	//简单用法:
	int a = 10;
	int b = 20;
	int c = 30;

	//没有数组指针的情况
	int* p1 = &a;
	int* p2 = &b;
	int* p3 = &c;
	//有数组指针的情况
	int* arr[3] = { &a,&b,&c };
	for (int i = 0; i < 3; i++)
	{
		printf("%d\n", *(arr[i]));
	}
	return 0;
}

3-3这才是指针数组的正确使用方法!【指针数组模拟打印二维数组】

 这和arr[3][5]的区别在于arr[3][5]在内存中中每一个元素的地址都是连续的,而指针数组模拟的二维数组这种方式的地址不是连续的。

代码语言:javascript
复制
int main()
{
	int arr1[5] = { 1,2,3,4,5 };
	int arr2[5] = { 2,3,4,5,6 };
	int arr3[5] = { 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\t",arr[i][j]);//way1
            //printf("%d\t", *(arr[i] + j));//way2
              printf("%d\t",*(*(arr+i)+j);//way3
            //实际上way1和way2在编译器翻译过程中会自动转换为way3
		}
		printf("\n");
	}
	return 0;
}

 4. 数组指针

代码语言:javascript
复制
int main()
{
	//整型指针-指向整型的指针-存放整型变量的地址
	int a = 10;
	int* pa = &a;

	//整型指针-指向字符的指针-存放字符变量的地址
	char ch = 'w';
	char* pc = &ch;

	//数组指针-指向数组的地址,存放数组变量的地址
	int arr[5] = { 1,2,3,4,5 };

	int(*p)[5]=&arr;//注意这个5不能省略,因为这个5是指针指向的那个数组的类型
}

一个指针只能存放一个地址,只不过这个地址是整个数组的地址 

4-1 区分取地址数组名和数组名(老生常谈了)

4-1-1 sizeof(数组名):求的是该类型所定义的变量在内存中所占空间的大小,单位是字节。

代码语言:javascript
复制
int main()
{
	int arr1[5] = { 1,2,3,4,5 };
	printf("%d\n", sizeof(arr1));//32位平台下:20字节   64位平台下:20字节//类型;int[5]数组
	printf("%d\n", sizeof(arr1+0));//32位平台下:4字节   64位平台下:8字节//已经不是单独的sizeof(数组名)//类型:int*指针
	printf("%d\n", sizeof(arr1[0]));//32位平台下:4字节   64位平台下:8字节//类型:int*指针
	return 0;
}

 4-1-2  &数组名,在没有+-整数时虽然地址是相同的,但是意义不同。

4-2 辨析数组指针和指针数组

 图图我就省略喽!😁😁😁

代码语言:javascript
复制
int main()
{
	//我们知道:去掉变量名剩下的部分就是变量所属的类型
	//(这里可以通过调试得以验证以下猜想)


	//整型数组,数组类型为int [5],从左往右念就是整型数组
	//特别注意:数组索引中的元素个数5也是类型的一部分
	//所以int [4]和int [5]所属的是不同的类型
	int arr[5] = { 1,2,3,4,5 };

	//p1先和方块5(也就是[5])结合,所以整体来看p1是一个数组
	//既然是数组我们关心的就是数组呢存放的是什么?
	//数组内有五个元素,每个元素的类型是int* 类型
	//所以变量p1的类型为int* [5],从左往右念就是指针数组
	int* p1[5] = { arr,&arr[1] ,&arr[2], &arr[3], &arr[4] };

	//p2先和*结合,所以整体来看p2是一个指针
	//既然是指针,我们关心的就是指针指向的是什么?
	//指针指向的是一个数组,数组内有五个元素,每个元素时int类型
	//所以变量p2的类型为int[5]*,从左往右念就是数组指针
	int(*p2)[5] = &arr;
	return 0;
}

 4-3 学会了?来看一个小测试题

4-4  来看一个脱裤子放屁的代码【看一看数组指针的使用】

代码语言:javascript
复制
void Print1(int arr[], int sz)
{
	printf("Print1:\n");
	for (int i = 0; i < sz; i++)
	{
		printf("%d\t", arr[i]);
	}
}

void Print2(int* arr, int sz)
{
	printf("Print2:\n");
	for (int i = 0; i < sz; i++)
	{
		printf("%d\t", arr[i]);
	}
}

void Print3(int(*p)[5],int sz)
{
	printf("Print3:\n");
	for (int i = 0; i < sz; i++)
	{
	   printf("%d\t", *((*p)+i));
	  //printf("%d\t", (*p)[i]);

	}
}

int main()
{
	int arr[5] = { 1,2,3,4,5 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	//写一个函数打印数组内容
	//测试1:
	Print1(arr, sz);
	printf("\n");
	//测试2:
	Print2(arr, sz);
	printf("\n");
	//测试3:
	Print3(&arr,sz);
	return 0;
}

 4-5 这才是数组指针的正确使用方法捏【数组指针模拟打印二维数组】

指针数组虽然可以int(*p)[3]=&arr;其中arr是一个一维数组,但是这样太鸡肋了, 还不如直接int*p arr; 指针数组真正的使用场景是留给二维数组传数组名时,形参用指针数组接收。 

代码语言:javascript
复制
void Print1(int arr[3][5], int row, int col)
{
	printf("Print1:\n");
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			printf("%d\t", arr[i][j]);
		}
		printf("\n");
	}
}

void Print2(int(*p)[5], int row, int col)
{
	printf("Print2:\n");
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			printf("%d\t", (* (p + i))[j]);
			//printf("%d\t", *(*(p+i)+j));
            //printf("%d\t",p[i][j]);
			//p+i是第i行,也就是第i行整个数组的地址
			//*(p+i)也就是对整个数组的地址解引用,就是第i行的数组名,也就是第i行首元素的地址
			//*(p+i)+j就是第i行第j列元素的地址
			//*(*(p+i)+j)也就是对第i行第j列元素的地址解引用,就是第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} };

	//测试1:
	Print1(arr, 3, 5);
	printf("\n");

	//测试2:
	Print2(arr, 3, 5);
	printf("\n");
	return 0;
}

上面目前讲的基本还是数组指针指向的是一个一维数组 其实数组指针还是可以指向一个二维数组: int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} }; int(*p)[3][5]=&arr; 

完结,撒花,等等,要不来一个小测试?

5 测试题和规律总结

测验1:

如果int(*arr[3])[5]; 问题:怎么理解这个代码?

答案:数组指针数组

测验2:那么指针数组指针?

我总结的一点小规律

如果光要给上面这个东西命名的话,只用关注*和[]的出现次序排列就可以了 比如;数组指针数组:[]*[]            指针数组指针*[]*  然后再把[]存的内容和*指向的东西往里面填进去就可以了

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-05-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.初阶指针内容回顾
  • 2.字符指针
    • 2-1 字符指针长什么样?
      • 2-2 误区:
        • 2-3  代码一和代码二的异同:
          • 2-4 关于字符常量区:
            • 2-5 一道为了区分栈区和字符常量区&&字符数组和字符指针的面试题:
            •  3.指针数组
              • 3-1 指针数组长什么样捏?
                • 3-2 初级使用(或者说给你看一下基本使用):
                  • 3-3这才是指针数组的正确使用方法!【指针数组模拟打印二维数组】
                  •  4. 数组指针
                    • 4-1 区分取地址数组名和数组名(老生常谈了)
                      • 4-2 辨析数组指针和指针数组
                        •  4-3 学会了?来看一个小测试题
                          • 4-4  来看一个脱裤子放屁的代码【看一看数组指针的使用】
                            •  4-5 这才是数组指针的正确使用方法捏【数组指针模拟打印二维数组】
                            • 5 测试题和规律总结
                              • 测验1:
                                • 测验2:那么指针数组指针?
                                  • 我总结的一点小规律
                                  领券
                                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档