1 回顾–数组的本质

在之前的文章,已经学习了数组的本质分析。下面再来回顾一下:

  • 数组是一段连续的内存空间
  • 数组名可以看做是指向数组第一个元素的常量指针

那么数组名加1的意义是什么?指针运算呢?

看一下代码:

  • 28-1.c
#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;
}
  • 编译运行的结果如下:

从以上的运行结果来看:

  • 数组名加1得到的结果是数组元素首地址的值,加上一个元素类型的大小。如上代码是数组首元素地址加上int类型的大小4
  • 对于指针的运算:指针是一种特殊的变量,与整数的运算规则与数组名很类似,如下图公式(指针p):

当指针p指向一个同类型的数组时:

  • p+1 将指向当前元素的下一个元素
  • p-1 将指向当前元素的上一个元素

2 指针的运算

下面两点非常重要,务必记住;

  • 指针之间只支持减法运算。其他运算例如加法是不支持的
  • 参与减法运算的指针类型必须相同

注意,指针虽然支持减法运算,但是:

  1. 只有当两个指针指向同一个数组中元素的时候,指针相减才有意义。其意义在于指针所指向元素的下标差
  2. 当两个指针所指向元素不在同一个数组中时,指针相减结果未定义。

2.1 指针运算代码案例分析

  • 代码28-2.c
#include <stdio.h>

int main(){

    char s1[] = {'H','e','l','l','o'};
    int i = 0;    
    char* p0 = s1;  //相当于 cgar* p0 = &s1[0];
    char* p1 = &s1[3];    
    char s2[] = {'W','o','r','l','d'};
    char* p2 = s2;
    
    int* p = &i;
    
    printf("%d\n", p0 - p1);
    //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;
}
  • 编译运行结果为:
    -3
    5
  • 上述代码注释掉的部分编译会出错。说明指针不支持除减法以外的运算
  • 减法运算的意义在于指针所指向元素的下标差
  • 不是指向同一个数组元素的指针相减,结果未定义。如上面的p0-p2=5,这的结果是未定义的.

3 指针的比较

  • 指针也可以进行关系运算(<,<=,>,>=)
  • 指针的关系运算,只有在指针指向的数组元素在同一个数组内才有意义。否则虽然编译不会报错,但是没有什么意义。
  • 参与比较运算的指针类型必须相同,否则编译会给出警告。且这种比较也没有什么意义。

3.1 指针运算的应用代码案例分析

  • 28-3.c
#include <stdio.h>

#define DIM(a) sizeof(a) / sizeof(*a)

int main(){
 
    char s[] = {'H','e','l','l','o'};
    char* pBegin = s;
    char* pEnd = s + DIM(s);
    char* p = NULL;
    
    printf("pBegin = %p\n", pBegin);
    printf("pEnd = %p\n", pEnd);
    
    printf("Size = %d\n", pEnd - pBegin);
    
    for(p=pBegin; p<pEnd; p++){
        printf("%c", *p);
    }    
    
    printf("\n");

    return 0;
}
  • 编译运行结果为:

以上代码比较简单,这里就不再分析了。

4 总结

  • 数组声明时,编译器自动为其分配一片连续的内存空间
  • 指针声明时,只为指针分配了容纳地址值的4字节空间(32位系统)。这足以看出数组并不等于指针,两者差异很大
  • 指针可以和整数进行运算,其结果仍然为指针
  • 两个指向同一个数组内元素的指针的减法运算,代表数组元素的下标差
  • 指针之间的比较运算需要指针的类型相同。