亿迅智能制造网
工业4.0先进制造技术信息网站!
首页 | 制造技术 | 制造设备 | 工业物联网 | 工业材料 | 设备保养维修 | 工业编程 |
home  MfgRobots >> 亿迅智能制造网 >  >> Industrial programming >> C语言

C 语言指针与多维数组深度掌握:从一维到三维的内存布局与访问

C 语言中,数组是存储在连续内存位置的同类型值集合。一维或多维数组中的每个元素由一个或多个唯一整数索引标识。

指针存储变量地址。数组中第 0 个元素的地址即数组的指针。可使用“解引用运算符”访问指针指向的值。

C 语言可声明一维、二维或多维数组。“维度”指标识集合中元素所需的索引数量。

指针与一维数组

一维数组中每个元素由单个整数标识:

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

此处“1”位于第 0 索引,“2”位于索引 1,以此类推。

存储第 0 个元素地址的变量即其指针:

int *x = &a[0];

简单说,数组名也指向第 0 个元素地址。所以也可用:

int *x = a;

示例

指针值按数据类型大小递增,x++ 移动指针到数组下一元素。

#include <stdio.h>
int main(){
    int arr[] = {1, 2, 3, 4, 5};
    int length = sizeof(arr) / sizeof(arr[0]);
    int i = 0;
    int *ptr = arr;
    while (i < length){
        printf("arr[%d]: %d \n", i, *(ptr + i));
        i++;
    }
    return 0;
}

输出

arr[0]: 1
arr[1]: 2
arr[2]: 3
arr[3]: 4
arr[4]: 5

指针与二维数组

一维数组像元素列表,二维数组像表格或矩阵。

二维数组元素可视为逻辑上按行列排列。任意元素位置由两个索引决定:行号和列号。行列索引均从 0 开始。

int arr[2][2];

此类数组逻辑表示为:

Col0Col1Col2
Row0arr[0][0]arr[0][1]arr[0][2]
Row1arr[1][0]arr[1][1]arr[1][2]
Row2arr[2][0]arr[2][1]arr[2][2]

注意表格排列只是逻辑表示。编译器分配连续字节块。C 语言数组分配采用行优先方式,即按行顺序读入数组。

声明三行四列的二维数组(第一个方括号数字始终指行数):

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

编译器按行优先顺序为上述二维数组分配内存。假设数组首元素地址为 1000,int 类型大小 4 字节,数组元素获得以下内存位置:

Row 0Row 1Row 2
Value1 2 3 45 6 7 89 10 11 12
Address1000 1004 1008 10121016 1020 1024 10281032 1036 1040 1044

用取地址 & 运算符将数组首元素地址赋给指针 ptr:

int *ptr = &arr[0][0];

示例 1:线性遍历二维数组

指针每加 1 移动到下一地址。“34”数组所有 12 个元素可在循环中访问:

#include <stdio.h>
int main(){
    int arr[3][4] = {
        {1, 2, 3, 4},
        {5, 6, 7, 8},
        {9, 10, 11, 12},
    };
    // 指针 ptr 指向数组 num
    int *ptr = &arr[0][0];
    int i, j, k = 0;
    // 通过指针 ptr 打印数组 num 元素
    for (i = 0; i < 3; i++){
        for (j = 0; j < 4; j++){
            printf("%d ", *(ptr + k));
            k++;
        }
        printf("\n");
    }
    return 0;
}

输出

1 2 3 4
5 6 7 8
9 10 11 12

一般可用以下公式获取数组任意元素地址:

ith 行 jth 列元素地址 = baseAddress + [(i * no_of_cols + j) * sizeof(array_type)]

在我们的 3×4 数组中:

arr[2][2] 地址 = 1000 + (2*4 + 2)*4 = 1044

可参考上图,确认 arr[2][2] 地址为 1044(原文 arr[3][4] 系索引越界笔误,应为 arr[2][2])。

示例 2:用解引用指针按公式遍历数组

#include <stdio.h>
int main(){
    // 2d array
    int arr[3][4] = {
        {1, 2, 3, 4},
        {5, 6, 7, 8},
        {9, 10, 11, 12}
    };
    int ROWS = 3, COLS = 4;
    int i, j;
    // pointer
    int *ptr = &arr[0][0];
    // print the element of the array via pointer ptr
    for (i = 0; i < ROWS; i++){
        for (j = 0; j < COLS; j++) {
            printf("%4d ",*(ptr + (i * COLS + j)));
        }
        printf("\n");
    }
    return 0;
}

输出

1 2 3 4
5 6 7 8
9 10 11 12

指针与三维数组

三维数组是二维数组的数组。声明需三个下标:

int arr [x] [y] [j];

可视为“x”层表格,每层“x”行“y”列。

三维数组示例:

int arr[3][3][3] ={
    { {11, 12, 13}, {14, 15, 16}, {17, 18, 19} },
    { {21, 22, 23}, {24, 25, 26}, {27, 28, 29} },
    { {31, 32, 33}, {34, 35, 36}, {37, 38, 39} },
};

三维数组指针声明为:

int * ptr = &arr[0][0][0];

数组名本身即第 0 个元素地址,三维数组指针也可写为:

int * ptr = arr;

每层“x”行“y”列占用:

x * y * sizeof(data_type)

字节。假设上述三维数组 "arr" 分配内存从地址 1000 开始,第二层 (i=1) 起始位置为 1000 + (3×3)×4 = 1036 字节。

ptr = 三维数组 arr 基地址

若 JMAX 为行数,KMAX 为列数,第 1 层第 0 行第 0 列元素地址为:

arr[1][0][0] = ptr + (1 * JMAX * KMAX)

第 i 层第 j 行第 k 列元素值公式:

arr[i][j][k] = *(ptr + (i * JMAX*KMAX) + (j*KMAX + k))

示例:用指针解引用打印三维数组

用此公式通过指针解引用打印三维数组:

#include <stdio.h>
int main(){
    int i, j, k;
    int arr[3][3][3] = {
        { {11, 12, 13}, {14, 15, 16}, {17, 18, 19} },
        { {21, 22, 23}, {24, 25, 26}, {27, 28, 29} },
        { {31, 32, 33}, {34, 35, 36}, {37, 38, 39} },
    };
    int JMAX = 3, KMAX = 3;
    int *ptr = arr; // &arr[0][0][0];
    for(i = 0; i < 3; i++){
        for(j = 0; j < 3; j++){
            for(k = 0; k < 3; k++){
                printf("%d ",*(ptr+(i*JMAX*KMAX)+(j*KMAX+k)));
            }
            printf("\n");
        }
        printf("\n");
    }
    return 0;
}

输出

11 12 13
14 15 16
17 18 19

21 22 23
24 25 26
27 28 29

31 32 33
34 35 36
37 38 39

一般来说,用指针访问数组与用下标表示法访问数组相当相似。主要区别在于下标声明数组静态分配内存,而指针可用于动态内存分配。

向函数传递多维数组需用指针而非下标。不过,下标数组比指针更方便,对初学者指针可能较难。

C语言

  1. 掌握 C++ switch-case 语句:语法、流程与计算器实战案例
  2. C 语言变量详解:类型、命名规则、定义声明与内存管理
  3. C# 面试题精选 50 道(2021 版)—— 面向初学者与资深开发者
  4. 掌握C#嵌套循环:For、While与Do-While详解
  5. C 语言存储类完全解析:auto、extern、static、register
  6. C 语言注释完全指南:编写清晰高效的代码备注
  7. 掌握 C# 位运算与移位运算符:全面指南
  8. C++ 运算符全解析:类型、示例与示例程序
  9. C 语言 free() 函数实战指南:正确释放动态内存、防泄漏与避坑全解析
  10. 掌握 C# using 指令:命名空间导入、别名与静态导入全解析
  11. C# Hello World:构建你的第一个控制台应用程序
  12. C++ 友元函数与友元类:突破访问控制的利器