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

数组的本质

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

问题:

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

编程实验: a + 1 的结果是什么?

#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;
}
输出:
a = 0xBFD627C8
a + 1 = 0xBFD627CC
p = 0x0
p + 1 = 0x4

分析:
p + 1 ==> 0 + 1 * sizeof(*p) ==> 0 + 1 * sizeof(int) ==> 0 + 4 ==> 4
a + 1 ==> 0xBFD627C8 + 1 * sizeof(*a) ==> 0xBFD627C8 + 1 * sizeof(a[0]) ==> 0xBFD627C8 + 1 * sizeof(int) ==> 0xBFD627C8 + 1 * 4 ==> 0xBFD627C8 + 4 ==> 0xBFD627CC  


指针的运算

  • 指针是一种特殊的变量,与整数的运算规则为(32位机器
        ○ p+n; <--> (unsigned int)p + n * sizeof(*p)

结论:当指针指向一个同类型的数组的元素时, p+1 将指向当前元素的下一个元素; p-1 将指向当前元素的上一个元素。

  • 指针之间只支持减法运算
  • 参与减法运算的指针类型必须相同
        ○ p1 - p2; <--> ((unsigned int)p1 - (unsigned int)p2) / sizeof(type);

注意:

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

  2. 当两个指针指向的元素不在同一个数组中时,结果未定义。

  3. 以上两条注意事项面对绝大部分的使用情况。

  4. 指针的运算是很灵活的,在特殊情况下,可以通过强制类型的转换,实现不同类型指针间的运算,而得到巧妙的结果。

  5. 已知一个结构体里面的成员地址,可以反推出该结构体的首地址 :

struct _tag
{
    int aa;
    int bb;

    unsigned int* node; 
};

指针的比较

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

实例分析: 指针运算初探

#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);
    // printf("%d\n", p0 + p1);
    // printf("%d\n", p0 - p2);
    // printf("%d\n", p0 - p);
    // printf("%d\n", p0 * p1);
    // printf("%d\n", p0 / p1);

    return 0;
}
输出:
-3

分析:
p0 - p2; 编译无警告,无错误,但大部分情况下,这样操作是没有实际意义的

p0 + p1; error: invalid operands to binary + (have ‘char *’ and ‘char *’) // 操作符不支持 
p0 - p;  error: invalid operands to binary - (have ‘char *’ and ‘int *’)  // 指针类型不同
p0 * p1; error: invalid operands to binary * (have ‘char *’ and ‘char *’) // 操作符不支持
p0 / p1; error: invalid operands to binary / (have ‘char *’ and ‘char *’) // 操作符不支持

实例分析: 指针运算的应用

#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);  // key point
    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\n", *p);
    }
    
    printf("\n");

    return 0;
}
输出:
pBegin = 0xbf9c17af
pEnd = 0xbf9c17b4
Size: 5
H
e
l
l
o

分析:
pEnd = s + DIM(s)  ==>
s + 5 ==> 0xbf9c17af + 5 * sizeof(*s)  ==> 0xbf9c17af + 5 * sizeof(char) ==> 0xbf9c17af + 5 * 1 ==> 0xbf9c17af + 5 ==> 0xbf9c17b4

小结

  • 数组声明时编译器自动分配一片连续的内存空间,空间名为数组名
  • 指针声明是只分配了用于容纳地址值的 4 字节空间
  • 指针和整数可以进行运算,其结果为整数
  • 指针之间只支持减法运算,其结果为数组元素下标差
  • 指针之间支持比较运算,其类型必须相同

补充:以上实验在32位机器中运行。

当指针大小占用 8 字节时(64位机器), 使用 (unsigned int) 强制类型转换将发生数据截断,导致得到不正常的结果。

发布了334 篇原创文章 · 获赞 661 · 访问量 130万+

猜你喜欢

转载自blog.csdn.net/czg13548930186/article/details/87070351