20 数组和指针

1 数组的本质

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

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

  • 数组名可看作指向数组第一个元素的常量指针,但数组名绝不是指针

  • a + 1 的意义

    #include <stdio.h>
    
    int main()
    {
        int a[5] = {0};
        int* p = NULL;
        
        //看作是常量指针的数组名运算
        printf("a = 0x%X
    ", (unsigned int)(a));  // a = 0xBFF51B48
        printf("a + 1 = 0x%X
    ", (unsigned int)(a + 1));  // a + 1 = 0xBFF51B4c
        
        //指针运算
        printf("p = 0x%X
    ", (unsigned int)(p));  // p = 0x0
        printf("p + 1 = 0x%X
    ", (unsigned int)(p + 1));  // p + 1 = 0x4
        
        return 0;
    }
    

2 指针的运算

  • 指针是一种特殊的变量,与整数的运算规则为:p + n; => (unsigned int)p + n * sizeof(*p);

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

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

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

3 指针的比较

  • 指针也可以进行关系运算(<,<=,>,>=)

  • 指针关系运算的前提是同时指向同一个数组中的元素

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

  • 参与比较运算的指针类型必须相同

  • 示例1

    #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
    ", p0 - p1);  //-3
        printf("%d
    ", p0 + p2);  //error
        printf("%d
    ", p0 - p2);  //error
        printf("%d
    ", p0 - p);   //error
        printf("%d
    ", p0 * p2);  //error
        printf("%d
    ", p0 / p2);  //error
    	
        return 0;
    }
    
  • 示例2

    #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
    ", pBegin);
        printf("pEnd = %p
    ", pEnd);
        
        printf("Size: %d
    ", pEnd - pBegin);
    	
        for(p=pBegin; p<pEnd; p++)
        {
            printf("%c", *p);
        }
        
        printf("
    ");
       
        return 0;
    }
    //输出结果
    pBegin = 0xbfac155f
    pEnd = 0xbfac1564
    size = 5
    Hello
    

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;
    }
    
  • 下标形式 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
    ", i, *(a + i));
        }
        
        printf("
    ");
        
        for(i=0; i<5; i++){
            //等价代换公式
            i[a] = i + 10;
        }
        
        for(i=0; i<5; i++){
            printf("p[%d] = %d
    ", i, p[i]);
        }
        
        return 0;
    }
    //输出结果
    a[0] = 1
    a[1] = 2
    a[2] = 3
    a[3] = 4
    a[4] = 5
    
    p[0] = 10
    p[1] = 11
    p[2] = 12
    p[3] = 13
    p[4] = 14
    
  • 数组和指针不同

    //ext.c
    int a[] = {1, 2, 3, 4, 5};
    
    //test.c
    #include <stdio.h>
    
    int main()
    {
        extern int a[];
        
        printf("&a = %p
    ", &a);//数组地址
        printf("a = %p
    ", a);//数组第一个元素的地址
        printf("*a = %d
    ", *a);//取“数组第一个元素的地址”上的值
    
        
        return 0;
    }
    
    • 运行结果

      &a = 0x804a014
      a = 0x804a014
      *a = 1
      
    • 修改:验证数组名与指针是否一样。运行结果分析:

      • ext.c 中定义了一个数组,在内存为:1000 2000 ... 5000(Linux为小端系统) ,一共 20 个字节
      • 该数组在内存中的地址为:0x804a014 ,也就是说在编译后,标识符 a 的意义为一个地址:0x804a014
      • 在编译 test.c 时,当编译到 extern int* a; 时,发现标识符 a 在别处定义
      • 当编译到 printf("&a = %p ", &a); 时,打印标识符 a 的地址,即为:0x804a014
      • 当编译到 printf("a = %p ", a); 时,打印的是一个指针变量 a ,其值保存的是一个地址,那么取 4 个字节的值即为:0x1
      • 当编译到 printf("*a = %d ", *a); 时,*a 是到地址 0x1 取值,而此地址为操作系统所使用,产生段错误
      //test.c
      #include <stdio.h>
      
      int main()
      {
          //数组名改为指针
          extern int* a;
          
          printf("&a = %p
      ", &a);
          printf("a = %p
      ", a);
          printf("*a = %d
      ", *a);
      
          
          return 0;
      }
      //运行结果
      &a = 0x804a014
      a = 0x1
      段错误
      

4 a 和 &a 的区别

  • a 为数组首元素的地址

  • &a 为整个数组的地址

  • a 和 &a 的区别在于指针运算

    • a + 1 => (unsigned int)a + sizeof(*a) =>(unsigned int)a + sizeof(*a[0])
    • &a + 1 => (unsigned int)(&a) + sizeof(*&a) => (unsigned int)(&a) + sizeof(a)
    • 二者加 1 增加的步长不一样
  • 指针运算示例

    #include <stdio.h>
    
    int main()
    {
        int a[5] = {1, 2, 3, 4, 5};
        printf("a = %p
    ",a);  // a = 0xbfa71990
        int* p1 = (int*)(&a + 1);  // p1指向数组a最后一个元素5后的下一个位置
        int* p2 = (int*)((int)a + 1);  // 整数加1是数学运算,结果为整数,p2指向一个地址:0xbfa71990 + 1 = 0xbfa71991,=> p2 = 0x0200 0000 => 十进制数:33554432
        int* p3 = (int*)(a + 1);  //p3指向第2个元素
        
        printf("%d, %d, %d
    ", p1[-1], p2[0], p3[1]);  // 5,33554432,3
        
        return 0;
    }
    

5 数组参数

  • 数组作为函数参数时,编译器将其编译成对应的指针

    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
    ", sizeof(a));
        
        *a = 'a';
        //如果a是数组名的话会报错:数组名不可以赋值
        a = NULL;
    }
    
    void func2(char b[])
    {
        printf("In func2: sizeof(b) = %d
    ", sizeof(b));
        
        *b = 'b';
        
        b = NULL;
    }
    
    int main()
    {
        char array[10] = {0};
        
        func1(array);
        
        printf("array[0] = %c
    ", array[0]);
        
        func2(array);
        
        printf("array[0] = %c
    ", array[0]);
        
        return 0;
    }
    
    // 运行结果
    In func2: sizeof(a) = 4
    array[0] = a
    In func2: sizeof(b) = 4
    array[0] = b
    
原文地址:https://www.cnblogs.com/bky-hbq/p/13646544.html