C指针之指针与数组的天生姻缘

指针与数组的天生姻缘

以指针方式来访问数组元素

  • 数组元素使用时不能整体访问,只能单个访问。访问方式有2种:数组形式和指针形式。
  • 数组格式访问数组元素是:
    • 数组名[下标]; (注意下标从0开始)
  • 指针格式访问数组元素是:
    • *(指针+偏移量);
    • 如果指针是数组首元素地址(a或者&a[0]),那么偏移量就是下标;
    • 指针也可以不是首元素地址而是其他哪个元素的地址,这时候偏移量就要考虑叠加了。
  • 数组下标方式和指针方式均可以访问数组元素,两者的实质其实是一样的。
    • 在编译器内部都是用指针方式来访问数组元素的,数组下标方式只是编译器提供给编程者一种壳(语法糖)而已。所以用指针方式来访问数组才是本质的做法。
int a[5] = {1, 2, 3, 4, 5};

printf("a[3] = %d.\n", a[3]);
printf("*(a+3) = %d.\n", *(a+3));
//等效于:int b = *(a+3); printf("*(a+3) = %d.\n", b);
int *p;
p = a;		// a做右值表示数组首元素首地址,等同于&a[0]
printf("*(p+3) = %d.\n", *(p+3));		// 等同于a[3]
printf("*(p-1) = %d.\n", *(p-1));		// 等同于a[-1]

p = &a[2];
printf("*(p+1) = %d.\n", *(p+1));		// 等同于a[3]
printf("*(p-1) = %d.\n", *(p-1));		// 等同于a[1]
printf("*(p+3) = %d.\n", *(p+3));		// 等同于a[5]

从内存角度理解指针访问数组的实质

  • 数组的特点就是:
    • 数组中各个元素的地址是依次相连的,而且数组还有一个很大的特点(其实也是数组的一个限制)就是数组中各个元素的类型相同。
    • 类型相同就决定了每个数组元素占几个字节是相同的(譬如int数组每个元素都占4字节,没有例外)。
  • 数组中的元素其实就是地址相连接、占地大小相同的一串内存空间。这两个特点就决定了只要知道数组中一个元素的地址,就可以很容易推算出其他元素的地址。

指针和数组类型的匹配问题

  • int *p; int a[5];    p = a;        // 类型匹配
  • nt *p; int a[5];    p = &a;        // 类型不匹配。p是int *,&a是整个数组的指针,也就是一个数组指针类型,不是int指针类型,所以不匹配
  • &a、a、&a[0]从数值上来看是完全相等的,但是意义来看就不同了
    • 从意义上来看,a和&a[0]是数组首元素首地址,而&a是整个数组的首地址;
    • 从类型来看,a和&a[0]是元素的指针,也就是int *类型;而&a是数组指针,是int (*)[5];类型。

指针类型决定了指针如何参与运算

  • 指针参与运算时,因为指针变量本身存储的数值是表示地址的,所以运算也是地址的运算。
  • 指针参与运算的特点是,指针变量+1,并不是真的加1,而是加1*sizeof(指针类型);
    • 如果是int *指针,则+1就实际表示地址+4
    • 如果是char *指针,则+1就表示地址+1
    • 如果是double *指针,则+1就表示地址+8.
  • 指针变量+1时实际不是加1而是加1×sizeof(指针类型)
    • 主要原因是希望指针+1后刚好指向下一个元素(而不希望错位)。
int a[5] = {1, 2, 3, 4, 5};
int *p;
p = a;

printf("*(p+1) = %d.\n", *(p+1));      //2
printf("*(p+1) = %d.\n", *((char *)p+1));           //0
printf("*(p+1) = %d.\n", *(int *)((unsigned int)p+1));    //33554432

char *p2;
p2 = (char *)p;
printf("*(p+1) = %d.\n", *(p2+1));             //0

猜你喜欢

转载自blog.csdn.net/baidu_41388533/article/details/106742802