#include<stdio.h>
#define N 10
int main()
{
int a[N] = { 0 }; //定义并初始化数组
return 0;
}
概念:数组是具有相同数据类型的集合。
数组的内存布局
#include<stdio.h>
int main()
{
int a = 10;
int b = 20;
int c = 30;
printf("%p\n", &a);
printf("%p\n", &b);
printf("%p\n", &c);
return 0;
}
我们来看一下运行的结果
- 我们发现,先定义的变量,地址是比较大的,后续依次减小。
- 这是为什么呢?
- a,b,c都在main函数中定义,也就是在栈上开辟的临时变量。而a先定义意味着,a先开辟空间,那么a就先入栈,所以a 的地址最高,其他类似。
#include<stdio.h>
#define N 10
int main()
{
int a[N] = { 0 };
for (int i = 0; i < N; i++) {
printf("&a[%d]: %p\n", i, &a[i]);
}
return 0;
}
- 我们发现,数组的地址排布是:&a[0] < &a[1] < &a[2] < ... < &a[9]。
- 该数组在main函数中定义,那么也同样在栈上开辟空间
- 数组有多个元素,那么肯定是a[0]先被开辟空间啊,那么肯定&a[0]地址最大啊,可是事实上并非如此!
- 数组是整体申请空间的,然后将地址最低的空间,作为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,本质加上其所指向类型的大小。
如果发生类型转化呢?
结论:
- 如果发生强制类型转换,对指针+1,本质加上其所指向强制类型转换的大小。
- 二级指针以以上所有的指针+1,在32位平台上,都是跳过4个字节。
#include<stdio.h>
int main()
{
char arr[10] = { 0 };
printf("%p\n", &arr[0]); //首元素的地址
printf("%p\n", &arr[0] + 1); //第二个元素的地址
printf("%p\n", &arr); //数组的地址
printf("%p\n", &arr + 1); //下一个数组的地址
return 0;
}
数组名使用的时候,只有两种情况代表整个数组
- &arr:数组的地址
- sizeof(arr):单独使用数组名
&arr[0] 和 &arr虽然地址数字一样大,但是类型意义完全不同。
为什么地址数字是一样大的呢???
因为首元素的地址和数组的地址,在地址对应的字节是重叠的,所以地址的数据值相等。
地址对应的字节都是变量开辟的空间中众多字节当中,地址最小的那个。
数组名a做为左值和右值的区别
数组只能整体被初始化,不能整体赋值,如果想要赋值,只能逐个元素进行赋值。
例如:arr[0] = 1; arr[1] = 2;