C语言-第23课 - 数组与指针分析

时间:2022-05-07 16:45:35

第23课 - 数组与指针分析

 

  1. 数组的本质

数组是一段连续的内存空间

数组的空间大小为sizeof(array_type)*array_size

数组名可看做指向数组第一个元素的常量指针

 

  1. 指针的运算

1)指针是一种特殊的变量,与整数的运算规则为:

p+n;<----->(unsigned int)p + n*sizeof(*p);

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

eg:

int a[5] = {1,2}; a[i] = *(a+i);&a[i] = a+i;

#include<stdio.h>

int main()

{

int a[5] = {1, 2, 3, 4, 5};

printf("%d, %d\n",*a,*(a+4));

return 0;

}

运行结果:1,5

2)指针之间只支持减法运算,且必须参与运算的指针类型必须相同。

p1 - p2;<---->((unsigned int)p1 - (unsigned int)p2) / sizeof(type);

注:

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

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

例如:

#include <stdio.h>

#include <malloc.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 + p2);错误,指针不能相加

    printf("%d\n", p0 - p2);

    //printf("%d\n", p0 - p);错误,类型不

    //printf("%d\n", p0 * p2);错误,不能想乘

    //printf("%d\n", p0 / p2);错误,不能相除

    return 0;

}

运行结果: -3                  下标差是-3

12                  s1s2都定义在栈上,根据内存对齐算出来的,但是没有意义

 

 

 

 

 

  1. 数组的访问:

以下标形式访问数组中的元素:

int main()

{

int a[5];

a[1] = 3;

a[3] = 5;

return 0;

}

以指针的形式访问数组中的元素:

int main()

{

int a[5];

*(a+1) = 3;

*(a+3) = 5;

return 0;

}

 

  1. 指针的比较

1)指针可以进行关系比较,下标的比较

< <= > >=

2)指针关系的前提是同时指向一个数组中的元素。

3)任意两个指针之间的比较运算(== !=)无限制。

eg:

#include <stdio.h>

#include <malloc.h>

#define DIM(a) (sizeof(a) / sizeof(*a))   //求数组里面有多少个元素

int main()

{

char s[] = {'H', 'e', 'l', 'l', 'o'};  //一共有六个元素,最后一个终止符

har* pBegin = s;

    char* pEnd = s + DIM(s);

    char* p = NULL;

    for(p=pBegin; p<pEnd; p++)

    {

        printf("%c", *p);

    }

    printf("\n");

    return 0;

}

运行结果:Hello

 

  1. 下标VS指针

1)从理论上讲,当指针以固定增量在数组中移动时,其效率是高于下标产生的代码

2)当指针增量为1且硬件具有硬件增量模型时,表现更佳

注意:

现代编译器的生成代码优化率已经大大提高,在固定增量是,下标形式的效率已经和指针形式相当;但是从可读性和代码维护的角度来看,下标形式更优。例如:

#include <stdio.h>

#include <time.h>

int main()

{

    clock_t start;

    clock_t end;

    int a[10000];

    int b[10000];

    int* pEnd = &a[10000];

    int* pa = NULL;

    int* pb = NULL;

    int i = 0;

    int k = 0;

start = clock();

    for(k=0; k<10000; k++)

{

        for(i=0; i<10000; i++)

        {

            b[i] = a[i];

        }

    }

    end = clock();

    printf("Index Timing: %d\n", end - start);

    start = clock();

    for(k=0; k<10000; k++)

{

        for(pa=a, pb=b; pa<pEnd;)

        {

            *pb++ = *pa++;

        }

    }

    end = clock();

    printf("Pointer Timing: %d\n", end - start);

    return 0;

}

运行结果:

Index Timing:290

Pointer Timing:264

说明指针运行得更快

 

 

  1. a&a的区别

1a为数组是数组首元素的地址。

2&a为整个数组的地址。

3a&a的意义不同其区别在于指针运算。

a+1--->(unsigned int)a + sizeof(*a)    指向开始的第二个位置

&a+1---->(unsigned int)(&a) +sizeof(*&a)  &*时相逆的运算

=(unsigned int)(&a) +sizeof(a)  指向数组的末尾

我们看一下例程:motorola面试题

#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("%d, %d, %d\n", p1[-1], p2[0], p3[1]);

    return 0;

}

运行结果:5335544323

p1指在最后的位置,p1[-1]就会是5

p3指在2的位置,p3[1]指的就是3

对于p2它表示的是,a的绝对值加一,没有指向一个正常的数,是一个字节的1/4,会是乱码。按推理应该死0x02000000,转化为10进制数就会是33554432。若改为nt* p2 = (int*)((int)a + 4);就会打印出2

 

 

  1. 数组参数

1C语言中,数组作为函数参数时,编译器将其编译成对应的指针。

eg:

void f(int a[]);<--->void f(int* a);

void f(int a[5]);<--->void f(int* a);

(2)一般情况下,当定义的函数中有数组参数时,需要定义另一个参数来表示数组的大小。

例如:

#include<stdio.h>

void f(int a[10000])

{

printf("%d\n",sizeof(a));

}

int main()

{

int a[4] = {0};

f(a);

return 0;

}

运行结果:4

 

 

注:指针和数组的对比

1)数组声明时编译器自动分配一片连续内存空间。

2)指针声明时只分配了用于容纳指针的4字节空间。

3)在作为函数参数时,数组参数和指针参数等价。

4)数组名在多数情况可以看做常量指针,其值不能改变。

5)指针的本质是变量,保存的值被看做内存中的地址。