C语言中一维指针、二维指针和三维指针

指针可以指向一份普通类型的数据,例如 int、double、char 等,也可以指向一份指针类型的数据,例如 int *double *char * 等。

如果一个指针指向的是另外一个指针,我们就称它为二级指针,或者指向指针的指针。

假设有一个 int 类型的变量 a,p1是指向 a 的指针变量,p2 又是指向 p1 的指针变量,它们的关系如下图所示:

将这种关系转换为C语言代码:

cpp 复制代码
int a =100;
int *p1 = &a;
int **p2 = &p1;

指针变量也是一种变量,也会占用存储空间,也可以使用&获取它的地址。C语言不限制指针的级数,每增加一级指针,在定义指针变量时就得增加一个星号*p1 是一级指针,指向普通类型的数据,定义时有一个*p2 是二级指针,指向一级指针 p1,定义时有两个*

如果我们希望再定义一个三级指针 p3,让它指向 p2,那么可以这样写:

cpp 复制代码
int ***p3 = &p2;

四级指针也是类似的道理:

cpp 复制代码
int ****p4 = &p3;

想要获取指针指向的数据时,一级指针加一个*,二级指针加两个*,三级指针加三个*,以此类推,请看代码:

cpp 复制代码
#include <stdio.h>
int main(){
    int a =100;
    int *p1 = &a;
    int **p2 = &p1;
    int ***p3 = &p2;
    printf("%d, %d, %d, %d\n", a, *p1, **p2, ***p3);
    printf("&p2 = %#X, p3 = %#X\n", &p2, p3);
    printf("&p1 = %#X, p2 = %#X, *p3 = %#X\n", &p1, p2, *p3);
    printf(" &a = %#X, p1 = %#X, *p2 = %#X, **p3 = %#X\n", &a, p1, *p2, **p3);
    return 0;
}

运行结果:

cpp 复制代码
100, 100, 100, 100
&p2 = 0X28FF3C, p3 = 0X28FF3C
&p1 = 0X28FF40, p2 = 0X28FF40, *p3 = 0X28FF40
 &a = 0X28FF44, p1 = 0X28FF44, *p2 = 0X28FF44, **p3 = 0X28FF44

*p3 得到的是 p2 的值,也即 p1 的地址;*(*p3) 得到的是 p1 的值,也即 a 的地址;经过三次"取值"操作后,*(*(*p3)) 得到的才是 a 的值。



指针数组

如果一个数组中的所有元素保存的都是指针,那么我们就称它为指针数组。指针数组的定义形式一般为:

cpp 复制代码
dataType *arrayName[length];

[ ] 的优先级高于* ,该定义形式应该理解为:
dataType *(arrayName[length]);

括号里面说明 arrayName 是一个数组,包含了 length 个元素,括号外面说明每个元素的类型为 dataType *

除了每个元素的数据类型不同,指针数组和普通数组在其他方面都是一样的,下面是一个简单的例子:

cpp 复制代码
#include <stdio.h>
int main(){
    int a = 16, b = 932, c = 100;
    //定义一个指针数组
    int *arr[3] = {&a, &b, &c};//也可以不指定长度,直接写作 int *arr[]
    //定义一个指向指针数组的指针
    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;
}

运行结果:

cpp 复制代码
16, 932, 100
16, 932, 100

arr 是一个指针数组,它包含了 3 个元素,每个元素都是一个指针,在定义 arr 的同时,我们使用变量 a、b、c 的地址对它进行了初始化,这和普通数组是多么地类似。

parr 是指向数组 arr 的指针,确切地说是指向 arr 第 0 个元素的指针,它的定义形式应该理解为 int *(*parr) ,括号中的 * 表示 parr 是一个指针,括号外面的 int * 表示 parr 指向的数据的类型。arr 第 0 个元素的类型为 int * ,所以在定义 parr 时要加两个 *

第一个 printf() 语句中,arr[i] 表示获取第 i 个元素的值,该元素是一个指针,还需要在前面增加一个 * 才能取得它指向的数据,也即 *arr[i] 的形式。

第二个printf() 语句中,parr+i 表示第 i 个元素的地址,*(parr+i) 表示获取第 i个元素的值(该元素是一个指针),**(parr+i) 表示获取第 i 个元素指向的数据。

指针数组还可以和字符串数组结合使用,请看下面的例子:

cpp 复制代码
#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;
}

运行结果:

cpp 复制代码
c.biancheng.net
C语言中文网
C Language

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

也只有当指针数组中每个元素的类型都是 char * 时,才能像上面那样给指针数组赋值,其他类型不行。

为了便于理解,可以将上面的字符串数组改成下面的形式,它们都是等价的。

cpp 复制代码
#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;
}


当涉及到多维数据结构时,C语言提供了一维、二维和三维指针来处理这些数据。指针是一种特殊类型的变量,它存储了内存地址,可以用于访问和操作内存中的数据。本文将详细介绍C语言中一维指针、二维指针和三维指针的内存结构,并提供相应的C语言示例来加深理解。

一维指针

一维指针是处理一维数组的重要工具。它存储数组的首个元素的内存地址,并可用于遍历整个数组。一维数组在内存中是连续存储的,因此一维指针可以按顺序访问数组的各个元素。

cpp 复制代码
int arr[5] = {1, 2, 3, 4, 5};
int *ptr = arr;  // 定义一维指针并指向数组的首个元素

for (int i = 0; i < 5; i++) {
    printf("%d ", *(ptr + i));
}

上述示例中,我们定义了一个包含5个元素的整型数组arr,然后定义了一个指向 arr 的一维指针 ptr。通过指针ptr,我们使用指针算术运算来遍历数组,并使用间接引用运算符*访问每个元素的值。

二维指针

二维指针用于处理二维数组,它存储了二维数组每个元素的内存地址。二维数组在内存中以行优先的方式存储,即每一行的元素是连续存储的。二维指针可以通过指向每个一维数组的指针来访问和操作二维数组的元素。

cpp 复制代码
int matrix[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
int **ptr = (int **)malloc(3 * sizeof(int *));

for (int i = 0; i < 3; i++) {
    ptr[i] = matrix[i];
}

for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 3; j++) {
        printf("%d ", *(*(ptr + i) + j));
    }
    printf("\n");
}

free(ptr);

在上述示例中,我们定义了一个包含3行3列的二维整型数组matrix。然后,我们使用二维指针 ptr 动态分配了3个指针的内存空间,并将每一行的首地址赋给二维指针。通过二维指针 ptr,我们使用指针算术运算来访问和操作二维数组的元素。

三维指针

三维指针用于处理三维数组,它存储了三维数组每个元素的内存地址。三维数组在内存中的存储方式比较复杂,它需要使用多级指针来表示。通过多级指针,我们可以访问和操作三维数组的元素。

cpp 复制代码
int cube[2][2][2] = {{{1, 2}, {3, 4}}, {{5, 6}, {7, 8}}};
int ***ptr = (int ***)malloc(2 * sizeof(int **));

for (int i = 0; i < 2; i++) {
    ptr[i] = (int **)malloc(2 * sizeof(int *));
    for (int j = 0; j < 2; j++) {
        ptr[i][j] = (int *)malloc(2 * sizeof(int));
        for (int k = 0; k < 2; k++) {
            ptr[i][j][k] = cube[i][j][k];
        }
    }
}

for (int i = 0; i < 2; i++) {
    for (int j = 0; j < 2; j++) {
        for (int k = 0; k < 2; k++) {
            printf("%d ", *(*(*(ptr + i) + j) + k));
        }
        printf("\n");
    }
    printf("\n");
}

for (int i = 0; i < 2; i++) {
    for (int j = 0; j < 2; j++) {
        free(ptr[i][j]);
    }
    free(ptr[i]);
}
free(ptr);

在上述示例中,我们定义了一个包含2个2行2列的三维整型数组cube。然后,我们使用三维指针 ptr 动态分配了对应的内存空间,并将每个元素的值赋给三维指针。通过三维指针 ptr,我们使用指针算术运算来访问和操作三维数组的元素。

相关推荐
尘浮生2 分钟前
Java项目实战II基于Spring Boot的光影视频平台(开发文档+数据库+源码)
java·开发语言·数据库·spring boot·后端·maven·intellij-idea
明月看潮生23 分钟前
青少年编程与数学 02-003 Go语言网络编程 15课题、Go语言URL编程
开发语言·网络·青少年编程·golang·编程与数学
南宫理的日知录34 分钟前
99、Python并发编程:多线程的问题、临界资源以及同步机制
开发语言·python·学习·编程学习
逊嘘1 小时前
【Java语言】抽象类与接口
java·开发语言·jvm
xinghuitunan1 小时前
蓝桥杯顺子日期(填空题)
c语言·蓝桥杯
Half-up1 小时前
C语言心型代码解析
c语言·开发语言
Source.Liu1 小时前
【用Rust写CAD】第二章 第四节 函数
开发语言·rust
monkey_meng1 小时前
【Rust中的迭代器】
开发语言·后端·rust
余衫马1 小时前
Rust-Trait 特征编程
开发语言·后端·rust
monkey_meng1 小时前
【Rust中多线程同步机制】
开发语言·redis·后端·rust