指针和数组对比分析

  1. 数组的本质

    -数组是一段连续的内存空间
    -数组的空间大小为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行

猜你喜欢

转载自blog.csdn.net/Conquer_ALL/article/details/82724457