详解指针与数组(😉学不会退学费😉)
一、🎁数组🎁
数组:数组是具有相同数据类型的集合
简单定义一个数组
#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;
}
😁😁😁觉得对自己有帮助的小伙伴可以点个赞哦😁😁😁
👉👉👉有误的地方也可以在评论区讨论哦👈👈👈
离开前,别忘了👍关注💡收藏💖
希望本文能够对大家有帮助~!
🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀详解指针与数组篇目🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀