指针和数组(上)
专题四:指针和数组(上)。包括以下章节:
- 指针基础
- 数组基础
- 数组与指针分析
- C语言中的字符串
数组的概念
- 数组是相同类型的变量的有序集合
int a[5] = {1,2};
//1,int指数组存储元素都是int类型的数据
//2,a指数组的名字(这20个字节空间的名字),也代表数组第一个元素起始地址(a是一个指针,存储第一个元素的地址)。
//3,[5]指数组中有5个元素
//4,{1,2}:初始化数组,前两个元素分别是1和2,其他元素默认为0
数组的大小
- 数组在一片连续的内存空间中存储元素
- 数组元素的个数可以显示或隐式指定
2-1.c
#include<stdio.h>
int main()
{
//数组n没有初始化,那么数组n有2个元素,而且是随机值
int n[2];
//显示指定a数组有5个元素。a[2],a[3],a[4]的值都是0。
int a[5] = {1,2};
//隐式定义:编译器看初始化列表{1,2},有2个元素,那么b数组有2个元素。
int b[] = {1,2};
//数组c中有3个元素,初始化的前3个元素是c数组的前3个元素,其他的不是
int c[3] = {1,2,3,4,5};
//数组d有5个元素,且都是0
int d[5] = {0};
int i = 0;
printf("%lu,%lu\n", sizeof(n), sizeof(n)/sizeof(*n));
printf("%lu,%lu\n", sizeof(a), sizeof(a)/sizeof(*a));
printf("%lu,%lu\n", sizeof(b), sizeof(b)/sizeof(*b));
printf("%lu,%lu\n", sizeof(c), sizeof(c)/sizeof(*c));
for(i=0;i<sizeof(n)/sizeof(*n);i++){
printf("n[%d]=%d\n",i, n[i]);
}
for(i=0;i<sizeof(a)/sizeof(*a);i++){
printf("a[%d]=%d\n",i, a[i]);
}
for(i=0;i<sizeof(b)/sizeof(*b);i++){
printf("b[%d]=%d\n",i, b[i]);
}
for(i=0;i<5;i++){
printf("c[%d]=%d\n",i, c[i]);
}
return 0;
}
结果:
数组地址与数组名
- 数组名代表数组首元素的地址
- 数组的地址需要用取地址符&才能得到 //int a[5]; a是数组首元素地址;&a是数组的地址;a的值和&a的值相同
- 数组首元素的地址值与数组的地址值相同
- 数组首元素的地址与数组的地址是两个不同的概念
你家所在的小区和你家的GPS地址是相同的,但是并不代表小区都是你家的…
数组名的盲点
- 数组名可以看做一个指针常量(常量指针:const int* p,* p不变;指针常量:int* const p,p不变)
- 数组名“指向”的是内存中数组首元素的起始位置
- 在表达式中数组名只能作为右值使用(常量只能是右值)
- 只有在下列场合中数组名不能看做常量指针
- 数组名作为sizeof操作符的参数
- 数组名作为&运算符的参数
实例分析2-1:数组和指针并不相同
2-2.c
#include <stdio.h>
extern char p[];
int main()
{
//编译器处理指针和数组不同
//1,处理指针有一次寻址的过程
//2,处理数组没有寻址过程,直接读取内容
//所以本次打印的是p的内容,但她其实是"Hello World!"的内存地址!
//可以这么写:printf("%s\n", (*((unsigned int*)p)));
//1,(unsigned int*)p:把p转换成一个指针
//2,*((unsigned int*)p):使用*运算取值
printf("%s\n", p);
return 0;
}
anther.c
char* p = "Hello World!";
结果:
- 编译器处理指针和数组不同
- 处理指针有一次寻址的过程
- 处理数组无寻址过程,直接读取内容
小结
- 数组是一片连续的内存空间
- 数组的地址和数组首元素的地址意义不同
- 数组名在大多数情况下被当成常量指针处理
- 数组名其实并不是指针,在外部声明时不能混淆