数组的本质
-数组是一段连续的内存空间 -数组的空间大小为sizeof(array_type)* array_size -数组名可看做指向数组第一个元素的常量指针
问题思考:
1. a+1的意义是什么?结果是什么? 2. 指针运算的意义是什么?结果是什么?
代码示例:
#include <stdio.h>
int main()
{
int a[5] = {0};
int* p = NULL;
printf("a = 0x%X\n", (unsigned int)(a));
printf("a + 1 = 0x%X\n", (unsigned int)(a + 1));
printf("p = 0x%X\n", (unsigned int)(p));
printf("p + 1 = 0x%X\n", (unsigned int)(p + 1));
return 0;
}
运行结果:
根据运行结果相信大家已经可以知道上面的问题答案了。
2.指针运算
-指针是一种特殊的变量,与整数的运算规则为:p+n;<==>(unsigned int)p + n * sizeof(*p);
-当指针p指向一个同类型的数组的元素时:p + 1将指向当前元素的下一个元素,p - 1将指向当前元素的上一个元素
-指针之间只支持减法运算
-参与减法运算的指针类型必须相同 : p1-p2; ==> ((unsigned type)p1 - (unsigned type)p2) / sizeof(type)
-只有当两个指针指向同一个数组中的元素时,指针相减才有意义,其意义为指针所指元素的下标差值
-当两个指针指向的元素不在同一个数组中时,结果未定义
3.指针的比较
-指针也可以进行关系运算(<, <=, >, >=)
-指针关系运算的前提是同时指向同一个数组中的元素
-任意两个指针之间的比较运算(==,!=)无限制
-参与比较运算的指针类型必须相同
代码示例:
#include <stdio.h>
int main()
{
char s1[] = {'H', 'e', 'l', 'l', 'o'};
int i = 0;
char s2[] = {'W', 'o', 'r', 'l', 'd'};
char* p0 = s1;
char* p1 = &s1[3];
char* p2 = s2;
int* p = &i;
printf("%d\n", p0 - p1); //-3
printf("%d\n", p0 + p2); //error
printf("%d\n", p0 - p2);
printf("%d\n", p0 - p); //error
printf("%d\n", p0 * p2); //error
printf("%d\n", p0 / p2); //error
return 0;
}
编译结果:
显然和分析结果一致将错误地方代码注释掉再次译结果为:
这里值得注意的是第15行虽然通过了编译符合了c语言的语法但是这个输出结果是没有意义的(两个指针没有同时指向一个数组中的元素)
4.数组的访问方式
-以下标的形式访问数组中的元素
int main()
{
int a[5] = {0};
a[1] = 3;
a[2] = 5;
return 0;
}
-以指针的形式访问数组中的元素
int main()
{
int a[5] = {0};
*(a + 1) = 3;
*(a + 2) = 5;
return 0;
}
5.下标访问形式和指针访问形式之间的差异
-指针以固定增量在数组中移动时,效率高于下标形式
-指针增量为1且硬件具有硬件增量模型时,效率更高
-下标形式与指针形式的转换: a[n] <==> *(a+n) <==> *(n+a) <==> n[a]
-现代编译器的生成代码优化率已大大提高,在固定增量时,下标形式的效率已经和指针形式相当;但从可读性和代码维护的角度来看,下标形式更好一些
代码示例:
#include <stdio.h>
int main()
{
int a[5] = {0};
int* p = a;
int i = 0;
for(i=0; i<5; i++)
{
p[i] = i + 1;
}
for(i=0; i<5; i++)
{
printf("a[%d] = %d\n", i, *(a + i));
}
printf("\n");
for(i=0; i<5; i++)
{
i[a] = i + 10;
}
for(i=0; i<5; i++)
{
printf("p[%d] = %d\n", i, p[i]);
}
return 0;
}
运行结果:
注意代码第23行
6.a和&a的区别
-a为数组首元素的地址
-&a为整个数组的地址
-a和&a的区别在于指针1运算
a + 1 ==> (unsigned array_type)a + sizeof(*a)
&a + 1 ==> (unsigned array_type)(&a) + sizeof(*&a) ==> (unsigned array_type)(&a) + sizeof(a)
代码示例:
#include <stdio.h>
int main()
{
int a[5] = {1, 2, 3, 4, 5};
int* p1 = (int*)(&a + 1);
int* p2 = (int*)((int)a + 1);
int* p3 = (int*)(a + 1);
printf("p1[-1] = %d\n", p1[-1]);
printf("p2[0] = %d\n", p2[0]);
printf("p3[1] = %d\n", p3[1]);
return 0;
}
编译结果:
这里值得注意的事p2[0]输出的值并不是个随机值,它的值受系统的大小端所影响。
7.数组参数
-数组作为参数时,编译器将其编译成对应的指针
--void f(int a[]); <==> void f(int* a);
--void f(int a[5]); <==> void f(int* a);
-一般情况下,当定义的函数中有数组参数时,需要定义另一个参数来标示数组的大小
-函数的数组参数退化为指针
代码示例:
#include <stdio.h>
void func1(char a[5])
{
printf("In func1: sizeof(a) = %d\n", sizeof(a));
*a = 'a';
a = NULL;
}
void func2(char b[])
{
printf("In func2: sizeof(b) = %d\n", sizeof(b));
*b = 'b';
b = NULL;
}
int main()
{
char array[10] = {0};
func1(array);
printf("array[0] = %c\n", array[0]);
func2(array);
printf("array[0] = %c\n", array[0]);
return 0;
}
编译结果:
值得注意第9行和第18行