【C】29.指针和数组分析

问题:

数组名可以当作常量指针使用,那么指针是否也可以当作数组名使用?

下标形式访问元素

int a[10] = {0};
a[1] = 3;
a[2] = 5;

指针形式访问元素

int a[10] = {0};
*(a+1) = 3;
*(a+2) = 5;

 

下标形式 VS 指针形式

  • 指针以固定增量在数组中移动,效率高于下标形式
  • 指针增量为 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;
}

运算结果:

a[0] = 1
a[1] = 2
a[2] = 3
a[3] = 4
a[4] = 5

p[0] = 11
p[1] = 12
p[2] = 13
p[3] = 14
p[4] = 15

分析:

指针可以当作数组名使用,能否成功的用上取决与这个指针是否指向合法的数组,上述代码指针就是指向合法的数组

数组和指针不同案例分析

// flie1.c
#include <stdio.h>

int main()
{
    //extern int* a;
    extern int a[];
    printf("&a = %p\n", &a);
    printf("a = %p\n", a);
    printf("*a = %d\n", *a);
       
    return 0;
}


// file2.c
int a[] = {1, 2, 3, 4, 5};
&a = 0x601040
a = 0x601040
*a = 1

上述输出了期望的结果,下面对程序稍作修改

// flie1.c
#include <stdio.h>

int main()
{
    extern int* a;
   // extern int a[];
    printf("&a = %p\n", &a);
    printf("a = %p\n", a);
    printf("*a = %d\n", *a);
       
    return 0;
}


// file2.c
int a[] = {1, 2, 3, 4, 5};

在64位机器运行结果:

&a = 0x601040
a = 0x200000001
Segmentation fault (core dumped)

原因分析

  • 声明一个标识符a,已经在 file2.c  中定义了,此时标识符a 有了内存地址值了(即标识符a在编译后转化为地址值(0x601040 )
  • 在(0x601040 )对应内存上去取指针的内容一个字节,对应的值为0x1
  • 取指针的内容上的值,即访问0x1的地址值上的内容,所以会产生错误。

小小结:

在某些场景下数组名可以看作指针,指针又可以当作数组使用。但有些场景(数组就是数组,指针就是指针)

补充:

内存中是没有变量名和标识符的,所以编译之后变量名和表示符都变成对应的地址值

a 和 &a 的区别

  • a为数组首元素的地址
  • &a为整个数组的地址
  • a 和 &a 的区别在于指针运输
a+1   >>>>>>>>>     (unsigned int)a+sizeof(*a)
&a+1  >>>>>>>>>     (unsigned int )a +sizeof(*&a)
      >>>>>>>>>     (unsigned int)a + sizeof(a)  
siezeof(a) 表示整个数组的长度,所以递增的长度不一样

指针运算经典题目

#include <stdio.h>

int main()
{
    int a[5] = {1, 2, 3, 4, 5};
    int* p1 = (int*)(&a + 1);    // p1[-1] >>> *(p1 - 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;
}

数组参数

数组作为函数参数时,编译器讲其编译成对应的指针(函数的的数组参数退化为指针

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));
    // 如果为数组参数时,这边的大小应该为 5
    *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;
}

输出结果

In func1: sizeof(a) = 8
array[0] = a
In func2: sizeof(b) = 8
array[0] = b

小结

  •    数组名和指针在一些情况下使用方式相同,但是始终要明确
  1.       数组名的本质不是指针
  2.       指针的本质不是数组
  • 数组名并不是数组的地址,而是数组首元素的地址
  • 函数的数组参数退化为指针
发布了84 篇原创文章 · 获赞 0 · 访问量 756

猜你喜欢

转载自blog.csdn.net/zhabin0607/article/details/103339795