数组指针 指针数组 用法 区别

2,143 阅读4分钟

声明

  • int *a[4]; //指针数组

指针数组就相当于一次声明了多个指针。数组的每一个元素都是一个指针。

[ ]的优先级高于*,表示a是一个数组,元素为int * ,个数为4。

  • int (*p)[4]; //数组指针

数组指针就相当于一次声明了一个指针。只不过这个指针指向很特别,是一个数组。

()的优先级高于[],表示p是一个数组,类型是为int [4] ,步长为4。

下标区别:指针数组的下标表示数组元素个数,数组指针的表示指针步长

指针数组

#include <stdio.h>
int main(){
    int a = 16, b = 932, c = 100;
    //定义一个指针数组
    int *arr[3] = {&a, &b, &c};//也可以不指定长度,直接写作 int *parr[]
    //定义一个指向指针数组的指针
    int **parr = arr;
    printf("%d, %d, %d\n", *arr[0], *arr[1], *arr[2]);
    printf("%d, %d, %d\n", **(parr+0), **(parr+1), **(parr+2));
    return 0;
}

特别地,字符数组 str 中存放的是字符串的首地址,不是字符串本身,字符串本身位于其他的内存区域,和字符数组是分开的。

故字符串指针数组可以如下赋值:

#include <stdio.h>
int main(){
    char *str[3] = {
        "c.biancheng.net",
        "C语言中文网",
        "C Language"
    };
    printf("%s\n%s\n%s\n", str[0], str[1], str[2]);
    return 0;
}

以下代码等价:

#include <stdio.h>
int main(){
    char *str0 = "c.biancheng.net";
    char *str1 = "C语言中文网";
    char *str2 = "C Language";
    char *str[3] = {str0, str1, str2};
    printf("%s\n%s\n%s\n", str[0], str[1], str[2]);
    return 0;
}

数组指针与二维数组

二维数组在概念上是二维的,有行和列,但在内存中所有的数组元素都是连续排列的,它们之间没有“缝隙”。以下面的二维数组 a 为例

int a[3][4] = { {0, 1, 2, 3}, {4, 5, 6, 7}, {8, 9, 10, 11} };

内存排布如下:

数组首地址如下:

定义一个指向 a 的指针变量 p:

int (*p)[4] = a;

对指针进行加法(减法)运算时,它前进(后退)的步长与它指向的数据类型有关,p 指向的数据类型是int [4],故 p 指向的是数组的第一行。

*(p+1)单独使用时表示的是第 1 行数据,放在

  • 表达式中:因为使用整行数据没有实际的含义,编译器遇到这种情况都会转换为指向该行第 0 个元素的指针 这里是第 1 行第 0 个元素的地址
  • 定义时或者和 sizeof、& 一起使用时: 才表示整行,类似地,一维数组的名字这样使用时表示整个数组。

有以下关系

a+i == p+i
a[i] == p[i] == *(a+i) == *(p+i)
a[i][j] == p[i][j] == *(a[i]+j) == *(p[i]+j) == *(*(a+i)+j) == *(*(p+i)+j)

验证如下【Compile and Execute C++11 Online (GNU GCC v7.1.1)】

#include <stdio.h>
int main(){
    int a[3][4] = { {0, 1, 2, 3}, {4, 5, 6, 7}, {8, 9, 10, 11} };
    int (*p)[4] = a;
    printf("sizeof(p): %d\n", sizeof(p));
    printf("sizeof(*p): %d\n", sizeof(*p));
    printf("p: %d\t==\ta: %d ?\t%d\t\n", p, a, p == a);
    printf("p: %d\t==\ta+1: %d ?\t%d\t\n", p, a+1, p == a+1);
    printf("*p: %d\t==\t*a: %d ?\t%d\t\n", *p, *a, *p == *a);
    printf("*p: %d\t==\t*a+1: %d ?\t%d\t\n", *p, *a+1, *p == *a+1);
    printf("*p: %d\t==\t*(a+1): %d ?\t%d\t\n", *p, *(a+1), *p == *(a+1));
    printf("*(p+1): %d\t==\t*(a+1): %d ?\t%d\t\n", *(p+1), *(a+1), *(p+1) == *(a+1));
    printf("p[1]: %d\t==\ta[1]: %d ?\t%d\t\n", p[1], a[1], p[1] == a[1]);
    printf("*a: %d\n", *a+1);
    printf("p[1][2]: %d\n", p[1][2]);
    printf("%d\n", *(*p+2));
    printf("%d\n", sizeof(p));
    printf("%x\n", p);
    printf("%d\n", sizeof(a[0][0]));
    printf("%d\n", sizeof(a+1));
    return 0;
}

输出如下:

sizeof(p): 8
sizeof(*p): 16
p: -1938207696	==	a: -1938207696 ?	1	
p: -1938207696	==	a+1: -1938207680 ?	0	
*p: -1938207696	==	*a: -1938207696 ?	1	
*p: -1938207696	==	*a+1: -1938207692 ?	0	
*p: -1938207696	==	*(a+1): -1938207680 ?	0	
*(p+1): -1938207680	==	*(a+1): -1938207680 ?	1	
p[1]: -1938207680	==	a[1]: -1938207680 ?	1	
*a: -1938207692
p[1][2]: 6
2
8
8c794c30
4
8

占用内存

int *(p1[5]);  //指针数组,可以去掉括号直接写作 int *p1[5];
int (*p2)[5];  //二维数组指针,不能去掉括号

指针数组是数组,占用空间为 单个元素字节数 × 元素个数,p1在32位环境下占4×5 = 20个字节;

数组指针是指针,占用空间为指针占用空间,p2在32位环境下占用4个字节。

参考:

  1. C语言指针数组(每个元素都是指针)
  2. C语言指针与二维数组
#include <typeinfo>
// #include <stdio.h>
// #include <stdlib.h>
#include <iostream>
using namespace std;

int main()
{

   int (*num)[2];
 
   cout << typeid( (num+1)[0] ).name() << endl;  //  打印: int 
   cout << typeid( 8.16 ).name() << endl; // 打印: double
   
   return 0;
}