【C语言进阶剖析】28、指针和数组分析(上)

1 数组的本质

  • 数组是一段连续的内存空间
  • 数组的空间大小为sizeof(array_type) * array_size
  • 数组名可看作指向数组第一个元素的常量指针

现在有两个问题:

  1. a + 1 的意义是什么?结果是什么?
  2. 指针运算的意义是什么?结果又是什么?

下面我们实际用编译器实现一下,看下 a + 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;
}
$ gcc 28-1.c -o 28-1
$ ./28-1
a = 0xC0919D70
a + 1 = 0xC0919D74
p = 0x0
p + 1 = 0x4

可以看到 a 与 a + 1 相差为 4,p 和 p + 1 相差也为 4,为什么会出现这个结果呢,请接着向下看指针的运算

2 指针的运算

2.1 指针与整数的运算

指针是一种特殊的变量,与整数的运算规则如下:
在这里插入图片描述
结论:当指针 p 指向一个同类型的数组元素时,p + 1 将指向当前元素的下一个元素,p - 1 将指向当前元素的上一个元素

根据这个规则,我们手动计算一下上面的 a + 1 和 p + 1

a + 1
= 0xC0919D70 + 1 * sizeof(*a)
= 0xC0919D70 + 1 * sizeof(int)
= 0xC0919D70 + 1 * 4
= 0xC0919D74

p + 1
= 0x0+ 1 * sizeof(*p)
= 0x0+ 1 * sizeof(int)
= 0x0+ 1 * 4
= 0x4

2.2 指针之间的运算

  • 指针之间只支持减法运算
  • 参与减法运算的指针类型必须相同

两个指针相减的运算可以用下面这个表达式来表示:
在这里插入图片描述
指针相减的结果不是地址差,是地址差再除以参数的长度,得到是元素个数

!!注意:

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

下面来初探一下指针运算,从下面的代码中找错:

// 28-2.c
#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("%ld\n", p0 - p1);
    printf("%ld\n", p0 + p2);
    printf("%ld\n", p0 - p2);
    printf("%ld\n", p0 - p);
    printf("%ld\n", p0 * p2);
    printf("%ld\n", p0 / p2);
    return 0;
}

分析:p0 指向数组 s1 的首元素, p1 指向数组 s1 的第 4 个元素,p2 指向数组 s2 的首元素,p 是 int 型的指针

首先指针之间只支持减法,所以第 14,17,18 行错误,参与减法运算的指针类型必须相同,所以第 16 行错误,编译一下,看和我们的分析结果是否相同。

在这里插入图片描述
根据编译结果,和我们的分析完全一致

将第 14,16,17,18 行注释,再次编译,运行,结果如下:

$ gcc 28-2.c -o 28-2
$ ./28-2
-3
-5

p0 指向数组 s1 的首元素, p1 指向数组 s1 的第 4 个元素,所以相减是 -3,表示下标差。p2 与 p0 指向不同的数组,结果是未定义的,这里的 -5 表示什么意义我也不知道

2.3 指针运算的应用

#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 = %ld\n", pEnd - pBegin);
    for (p = pBegin; p < pEnd; p++)
    {
        printf("%c", *p);
    }
    printf("\n");
    return 0;
}

DIM(a) 是 求数组的长度,数组首地址加数组长度指向数组最后一个元素后面的位置,这是合法的,并且经常使用,可以利用这个指针遍历数组。

$ gcc 28-3.c -o 28-3
$ ./28-3
pBegin = 0x7ffc7b77e213
pEnd = 0x7ffc7b77e218
Size = 5
Hello

3 指针的比较

  • 指针也可以进行关系运算(<, <=, >, >=)
  • 指针关系运算的前提是同时指向同一个数组中的元素
  • 任意两个指针之间的比较运算(==, !=)无限制
  • 参与比较运算的指针类型必须相同

4 小节

1、数组声明时编译器自动分配一片连续的内存空间
2、64 位系统指针长度为 8 字节,32 位系统为 4
3、指针和整数可以进行运算,其结果为指针
4、指针之间只支持减法运算,其结果为数组元素下标差
5、指针之间支持比较运算,其类型必须相同

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页