☀️☀️☀️不会还有人学不会C语言中的指针和数组吧?Chris带你走进并玩转指针与数组的世界(二)☀️☀️☀️


一、🎁数组🎁

数组:数组是具有相同数据类型的集合

简单定义一个数组

#include <stdio.h>
#define NUM 100
int main()
{
	//初始化数组
	int arr[NUM] = { 0 };

	//遍历整个数组
	int i = 0;
	for (i = 0; i < NUM; i++)
	{
		printf("%d\n", arr[i]);
	}

	return 0;
}


数组的内存布局

我们先来看一下整型变量的内存布局

#include <stdio.h>
int main()
{
	int a = 1;
	int b = 2;
	int c = 3;

	printf("%p\n", &a);
	printf("%p\n", &b);
	printf("%p\n", &c);

	return 0;
}

我们发现地址是在递减的

结论:

在main函数中进行变量的定义,都是在栈上开辟的临时空间

先定义a意味着a先在栈上开辟空间,a先入栈,所以a的地址最高

数组在内存空间的布局

#include <stdio.h>
#define NUM 100
int main()
{
	//初始化数组
	int arr[NUM] = { 0 };

	//遍历整个数组
	int i = 0;
	for (i = 0; i < NUM; i++)
	{
		printf("arr[%d]:%p\n",i,&arr[i]);
	}

	return 0;
}

我们发现地址是在递增的

结论:

数组在main函数中定义,也是在栈上开辟空间

数组开辟空间不像单个变量开辟空间一样,先开辟的地址最大

注意!注意!注意!

数组是整体申请空间的,整体开辟空间,整体释放。然后将地址最低的空间,作为a[0]元素



二、🎨理解&a[0]和&a的区别🎨

&a[0]:取出的是首元素的地址

&a:取出的是整个数组的地址

我们先从单个指针变量开始认识

#include<stdio.h>
int main()
{
	char* c = NULL;
	short* s = NULL;
	int* i = NULL;
	double* d = NULL;
	
	printf("%d\n", c);
	printf("%d\n\n", c + 1);
	
	printf("%d\n", s);
	printf("%d\n\n", s + 1);
	
	printf("%d\n", i);
	printf("%d\n\n", i + 1);
	
	printf("%d\n", d);
	printf("%d\n\n", d + 1);
	
	return 0;
}


对指针+1,本质加上其所指向类型的大小(看自身的大小)


#include<stdio.h>
int main()
{
	char* c = NULL;
	short* s = NULL;
	int* i = NULL;
	double* d = NULL;

	printf("%d\n", c);
	printf("%d\n\n",(int*) c + 1);

	printf("%d\n", s);
	printf("%d\n\n",(double*)s + 1);

	printf("%d\n", i);
	printf("%d\n\n",(char*)i + 1);

	printf("%d\n", d);
	printf("%d\n\n",(short*)d + 1);

	return 0;
}


指针遇到强制性转换,+1,本质上是看强转类型的大小


#include<stdio.h>
int main()
{
	int a = 10;
	double* p = (double*)&a;

	printf("%d\n", p);
	printf("%d\n", (short*)p + 1);

	return 0;
}

#include<stdio.h>
int main()
{
	int a = 10;
	double* p = (double*)&a;

	printf("%d\n", p);
	printf("%d\n", p + 1);

	return 0;
}

尽管这里进行强制性转换,但是最后还是看自身的类型大小

如果最终有强转,看的是强转类型的大小


思考!如果是二级指针以上+1,步长为多少呢?

#include <stdio.h>
int main()
{
	int* *q = NULL;//二级指针
	
	int* *******p = NULL;//多级指针
	
	printf("%p\n", q);
	printf("%p\n", q + 1);
	
	printf("%p\n", p);
	printf("%p\n", p+1);

	return 0;
}


一级指针指向的类型是多样化的,可能是char类型,可能是int类型。而二级指针或以上的指针,在32位平台下都是4字节,指向的都是指针类型


数组名在使用时,代表整个数组有两种情况:

1、&arr:取出整个数组的地址

2、sizeof(arr):计算整个数组的大小

#include <stdio.h>
#define NUM 10
int main()
{
	char arr[NUM] = { 0 };

	//数组首元素的地址
	printf("%p\n", &arr[0]);
	printf("%p\n", &arr[0] + 1);

	//整个数组的地址
	printf("%p\n", &arr);//整个数组的地址打印出来的结果和首元素地址一样
	printf("%p\n", &arr + 1);//指向下一个数组

	return 0;
}

结论:

1、&a[0]和&a虽然地址值一样,但是表示的意思完全不一样

2、&a[0]代表的是取出首元素的地址,+1,代表该元素的下一个元素指向的地址

3、&a代表的是取出整个数组的地址,+1,代表该数组的下一个数组指向的地址

我们有没有思考过:为什么&a[0]和&a意思不一样,但是打印出的结果一样呢?

在C语言中取地址,一定是取得众多字节中最低的那个地址,在低址处对应的字节是重叠的,所以,地址数据相等



三、💎数组名arr作为左值和右值的区别💎

数组名作为右值(代表首元素地址)

#include <stdio.h>
#define NUM 10
int main()
{
	char arr[NUM] = { 0 };

	char* p = arr;//数组名可以作为右值,代表首元素的地址

	printf("%p\n", arr);
	printf("%p\n", &arr[0]);

	return 0;
}

数组名作为左值(不可以)

#include <stdio.h>
#define NUM 10
int main()
{
	char arr[NUM] = { 0 };

	//数组名不可以作为左值,只能进行整体的初始化,不能整体赋值
	arr = { 1,2,3,4,5,6 };

	
	return 0;
}



四、🥨指针与数组的关系🥨

指针是指针,数组是数组,这是两个概念

使用指针定义的字符串和用数组定义的字符串有什么不同呢?

const char* str = "chris";
char arr[] = "bumstead";

在变量中访问字符串的方式

#include <stdio.h>
#include <string.h>
int main()
{
	const char* str = "chris";
	//求字符串长度 strlen求字符串长度,遇到\0停止,\0不在计算字符串长度的范围内
	int len = strlen(str);

	int i = 0;
	for (i = 0; i < len; i++)
	{
		//中括号的方式
        printf("%c", str[i]);
	}

	return 0;
}
#include <stdio.h>
#include <string.h>
int main()
{
	const char* str = "chris";
	//求字符串长度 strlen求字符串长度,遇到\0停止,\0不在计算字符串长度的范围内
	int len = strlen(str);

	int i = 0;
	for (i = 0; i < len; i++)
	{
		//指针的方式
        printf("%c", *(str + i));
	}

	return 0;
}

在数组中访问字符串的方式

#include <stdio.h>
#include <string.h>
int main()
{
	char arr[] = "bumstead";
	
	int len = strlen(arr);

	int i = 0;
	for (i = 0; i < len; i++)
	{
		printf("%c", arr[i]);
	}

	return 0;
}
#include <stdio.h>
#include <string.h>
int main()
{
	char arr[] = "bumstead";
	
	int len = strlen(arr);

	int i = 0;
	for (i = 0; i < len; i++)
	{
		printf("%c", *(arr + i));
	}

	return 0;
}

结论:

指针与数组,在访问多个连续元素的时候,可以采用指针解引用方案和[]方案

但是这样就可以说这两者一样了吗???

char arr[] = "bumstead"并没有单独为数组名开辟一块空间,数组名就代表首元素的地址(只有两种情况代表整个数组)。如果要访问b,只需要找到arr(首元素地址),就可以找到b;如果要找到u,只要找到arr,然后+1,指向下一个地址,就可以找到u,看出来这里的数组名可以看成一个字面常量,直接拿地址找到我们想找到的字符

const char* str = “chris”;这是现在栈上面开辟了一块空间用来保存变量str,在字符常量区开辟空间用来存放chris。如果要找到c,通过str可以找到;如果要找到h,通过str+1可以找到。我们得先找到str,拿出里面的内容,然后再找到对应的字符

虽然这里和数组表面上写法一样,但是寻址方式完全不同


为什么C中要把指针和数组这么相似

数组在传参的时候,发生降维,降维成指针变量

#include <stdio.h>

void showarray(int* arr, int len)
{
	printf("showarry:%d\n", sizeof(arr));
	int i = 0;
	for (i = 0; i < len; i++)
	{
		printf("%d ", *(arr + i));
	}
}

int main()
{
	int arr[] = { 0,1,2,3,4,5,6,7,8,9 };
	int len = sizeof(arr) / sizeof(arr[0]);
	printf("main:%d\n", sizeof(arr));

	showarray(arr, len);

	return 0;
}

为什么要降维?

如果不发生降维,数组传过去就是整个数组,发生数组的拷贝,这样就会使用大量的空间,函数调用效率降低

降维发生在哪里?

showarray(arr, len);这里的arr其实是代表数组首元素的地址(只有在两种情况下代表整个数组:&arr和sizeof(arr)),是一个数据,用这个数据去初始化int* arr(指针变量),其实showarray(arr, len)里面的arr已经发生了降维

降维成什么?

所有数组,传参都会降维成指针,降维成为指向其内部元素类型的指针

在C中,任何函数调用,只要有形参实例化,必定形成临时拷贝

#include <stdio.h>
//以指针的方式访问
void showarray(int* arr, int len)
{
	int i = 0;
	for (i = 0; i < len; i++)
	{
		printf("%d ", *(arr + i));
	}
}

int main()
{
	int arr[] = { 0,1,2,3,4,5,6,7,8,9 };
	int len = sizeof(arr) / sizeof(arr[0]);
	int i = 0;

	showarray(arr, len);
	printf("\n");
    //以数组的方式访问
	for (i = 0; i < len; i++)
	{
		printf("%d ", arr[i]);
	}
	
	return 0;
}
  • 假设指针和数组访问元素方式不通用,程序员需要不断在代码片段处,进行习惯的切换,就会增加代码出错的概率和调试的难度
  • 为了让程序员统一使用数组,减少出错概率,数组和指针的访问方式设计成通用,本质上减少了编程难度



五、🎪a 和 &a的区别🎪

#include <stdio.h>

int main()
{
	int a[5] = { 1, 2, 3, 4, 5 };
	int* ptr = (int*)(&a + 1);
	
	printf("%d %d\n", *(a + 1), *(ptr - 1));
	
	return 0;
}


😁😁😁觉得对自己有帮助的小伙伴可以点个赞哦😁😁😁

👉👉👉有误的地方也可以在评论区讨论哦👈👈👈


离开前,别忘了👍关注💡收藏💖
希望本文能够对大家有帮助~!


🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀详解指针与数组篇目🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀

不会还有人学不会C语言中的指针和数组吧?Chris带你走进并玩转指针与数组的世界(一)

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值