C语言数组的内存布局与访问方式小结

 更新时间:2026年03月23日 09:57:42   作者:星辰徐哥  
本文介绍了C语言数组的内存布局与访问方式,包括一维数组、二维数组和多维数组的内存布局,以及数组元素的访问方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

一、学习目标与重点

学习目标

  • 理解C语言数组的内存布局
  • 掌握数组元素的访问方式
  • 学会使用指针访问数组元素
  • 避免因数组访问不当导致的错误

学习重点

  • 数组的内存布局与存储方式
  • 数组元素的访问方法
  • 指针与数组的关系
  • 数组访问不当导致的错误

二、数组的内存布局

2.1 什么是数组?

数组是一种数据结构,用于存储相同类型的多个元素。数组中的元素在内存中连续存储,每个元素占用相同大小的内存空间。

2.2 一维数组的内存布局

一维数组的内存布局是连续的,数组的第一个元素存储在最低地址,最后一个元素存储在最高地址。

示例:

#include <stdio.h>

int main() {
    int arr[5] = {1, 2, 3, 4, 5};

    printf("数组第一个元素的地址:%p\n", &arr[0]);
    printf("数组第二个元素的地址:%p\n", &arr[1]);
    printf("数组第三个元素的地址:%p\n", &arr[2]);
    printf("数组第四个元素的地址:%p\n", &arr[3]);
    printf("数组第五个元素的地址:%p\n", &arr[4]);

    return 0;
}

执行结果:

数组第一个元素的地址:0x7ffccb8a8a50
数组第二个元素的地址:0x7ffccb8a8a54
数组第三个元素的地址:0x7ffccb8a8a58
数组第四个元素的地址:0x7ffccb8a8a5c
数组第五个元素的地址:0x7ffccb8a8a60

分析:
每个int类型的元素占用4字节的内存空间,因此数组元素的地址依次递增4字节。

2.3 二维数组的内存布局

二维数组可以看作是一维数组的数组,内存布局也是连续的。例如,二维数组int arr[2][3]可以看作是两个一维数组,每个一维数组包含3个int类型的元素。

示例:

#include <stdio.h>

int main() {
    int arr[2][3] = {{1, 2, 3}, {4, 5, 6}};

    printf("二维数组的地址:%p\n", arr);
    printf("第一行数组的地址:%p\n", arr[0]);
    printf("第二行数组的地址:%p\n", arr[1]);
    printf("第一行第一个元素的地址:%p\n", &arr[0][0]);
    printf("第一行第二个元素的地址:%p\n", &arr[0][1]);
    printf("第一行第三个元素的地址:%p\n", &arr[0][2]);
    printf("第二行第一个元素的地址:%p\n", &arr[1][0]);
    printf("第二行第二个元素的地址:%p\n", &arr[1][1]);
    printf("第二行第三个元素的地址:%p\n", &arr[1][2]);

    return 0;
}

执行结果

二维数组的地址:0x7fffd9a8a6a0
第一行数组的地址:0x7fffd9a8a6a0
第二行数组的地址:0x7fffd9a8a6ac
第一行第一个元素的地址:0x7fffd9a8a6a0
第一行第二个元素的地址:0x7fffd9a8a6a4
第一行第三个元素的地址:0x7fffd9a8a6a8
第二行第一个元素的地址:0x7fffd9a8a6ac
第二行第二个元素的地址:0x7fffd9a8a6b0
第二行第三个元素的地址:0x7fffd9a8a6b4

分析:
二维数组的内存布局是连续的,第一行数组的地址与二维数组的地址相同,第二行数组的地址比第一行数组的地址高12字节(3个int元素,每个4字节)。

2.4 多维数组的内存布局

多维数组的内存布局可以看作是一维数组的嵌套,内存空间连续存储。例如,三维数组int arr[2][3][4]可以看作是两个二维数组,每个二维数组包含3个一维数组,每个一维数组包含4个int类型的元素。

三、数组元素的访问方式

3.1 数组下标访问

数组元素可以通过数组下标访问,数组下标的范围是从0到数组长度减1。

示例:

#include <stdio.h>

int main() {
    int arr[5] = {1, 2, 3, 4, 5};

    printf("arr[0] = %d\n", arr[0]);
    printf("arr[1] = %d\n", arr[1]);
    printf("arr[2] = %d\n", arr[2]);
    printf("arr[3] = %d\n", arr[3]);
    printf("arr[4] = %d\n", arr[4]);

    return 0;
}

执行结果:

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

3.2 指针访问

数组名可以看作是数组第一个元素的指针,因此可以通过指针访问数组元素。

示例:

#include <stdio.h>

int main() {
    int arr[5] = {1, 2, 3, 4, 5};
    int *ptr = arr;

    printf("ptr[0] = %d\n", ptr[0]);
    printf("ptr[1] = %d\n", ptr[1]);
    printf("ptr[2] = %d\n", ptr[2]);
    printf("ptr[3] = %d\n", ptr[3]);
    printf("ptr[4] = %d\n", ptr[4]);

    return 0;
}

执行结果:

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

3.3 指针运算

指针可以进行运算,如加法、减法、比较等。指针运算的结果与指针所指向的类型有关。

示例:

#include <stdio.h>

int main() {
    int arr[5] = {1, 2, 3, 4, 5};
    int *ptr = arr;

    printf("ptr指向的地址:%p\n", ptr);
    printf("ptr+1指向的地址:%p\n", ptr+1);
    printf("ptr+2指向的地址:%p\n", ptr+2);

    return 0;
}

执行结果:

ptr指向的地址:0x7fffd9a8a6a0
ptr+1指向的地址:0x7fffd9a8a6a4
ptr+2指向的地址:0x7fffd9a8a6a8

分析:
指针每次加1,地址递增4字节(int类型的大小)。

四、指针与数组的关系

4.1 数组名与指针

数组名可以看作是数组第一个元素的指针,但数组名是常量指针,不能修改。

示例:

#include <stdio.h>

int main() {
    int arr[5] = {1, 2, 3, 4, 5};

    printf("数组名arr:%p\n", arr);
    printf("数组第一个元素的地址:%p\n", &arr[0]);

    return 0;
}

执行结果:

数组名arr:0x7fffd9a8a6a0
数组第一个元素的地址:0x7fffd9a8a6a0

分析:
数组名arr和数组第一个元素的地址&arr[0]是相同的。

4.2 指针数组与数组指针

指针数组和数组指针是两种不同的数据类型:

  • 指针数组:是一个数组,每个元素是指针。
  • 数组指针:是一个指针,指向一个数组。

示例:

#include <stdio.h>
int main() {
    int arr1[5] = {1, 2, 3, 4, 5};
    int arr2[5] = {6, 7, 8, 9, 10};
    // 指针数组
    int *ptr_arr[2] = {arr1, arr2};
    printf("ptr_arr[0][0] = %d\n", ptr_arr[0][0]);
    printf("ptr_arr[0][1] = %d\n", ptr_arr[0][1]);
    printf("ptr_arr[1][0] = %d\n", ptr_arr[1][0]);
    printf("ptr_arr[1][1] = %d\n", ptr_arr[1][1]);
    // 数组指针
    int (*arr_ptr)[5] = arr1;
    printf("arr_ptr[0][0] = %d\n", arr_ptr[0][0]);
    printf("arr_ptr[0][1] = %d\n", arr_ptr[0][1]);
    arr_ptr++; // 指向arr2
    printf("arr_ptr[0][0] = %d\n", arr_ptr[0][0]);
    printf("arr_ptr[0][1] = %d\n", arr_ptr[0][1]);
    return 0;
}

执行结果:

ptr_arr[0][0] = 1
ptr_arr[0][1] = 2
ptr_arr[1][0] = 6
ptr_arr[1][1] = 7
arr_ptr[0][0] = 1
arr_ptr[0][1] = 2
arr_ptr[0][0] = 6
arr_ptr[0][1] = 7

分析:

  • 指针数组ptr_arr包含两个指针,分别指向arr1和arr2。
  • 数组指针arr_ptr指向一个包含5个int元素的数组,arr_ptr++会指向arr2。

五、数组访问不当导致的错误

5.1 数组下标越界

数组下标越界是指访问数组元素时,下标超过了数组的范围。数组下标越界会导致未定义行为,可能会读取到内存中的随机值,或者导致程序崩溃。

示例:

#include <stdio.h>

int main() {
    int arr[5] = {1, 2, 3, 4, 5};

    printf("arr[5] = %d\n", arr[5]);

    return 0;
}

执行结果:

arr[5] = 0

分析:
数组arr的下标范围是0到4,访问arr[5]会导致数组下标越界,程序会读取到内存中的随机值。

5.2 指针越界访问

指针越界访问是指指针指向的地址超出了数组的范围。指针越界访问会导致未定义行为,可能会读取到内存中的随机值,或者导致程序崩溃。

示例:

#include <stdio.h>

int main() {
    int arr[5] = {1, 2, 3, 4, 5};
    int *ptr = arr;

    printf("ptr[5] = %d\n", ptr[5]);

    return 0;
}

执行结果:

ptr[5] = 0

分析:
指针ptr指向数组arr的第一个元素,ptr[5]会指向数组arr的第六个元素,导致指针越界访问。

5.3 内存泄漏

内存泄漏是指程序在动态分配内存后,没有及时释放内存,导致内存无法再使用。数组的内存泄漏通常发生在使用动态数组时。

示例:

#include <stdio.h>
#include <stdlib.h>

int main() {
    int *arr = (int *)malloc(5 * sizeof(int));
    arr[0] = 1;
    arr[1] = 2;
    arr[2] = 3;
    arr[3] = 4;
    arr[4] = 5;

    // 没有释放内存
    return 0;
}

分析:
程序使用malloc函数动态分配了5个int元素的内存,但没有使用free函数释放内存,导致内存泄漏。

六、总结

通过本文的学习,我们掌握了C语言数组的内存布局与访问方式,包括一维数组、二维数组和多维数组的内存布局,以及数组元素的访问方法(数组下标访问和指针访问)。我们还学习了指针与数组的关系,以及数组访问不当导致的错误(数组下标越界、指针越界访问和内存泄漏)。

在编写代码时,我们应该避免数组访问不当导致的错误,使用正确的访问方式,并及时释放动态分配的内存。同时,我们应该掌握指针与数组的关系,以便更好地使用数组和指针。

到此这篇关于C语言数组的内存布局与访问方式小结的文章就介绍到这了,更多相关C语言数组内存布局与访问内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C语言 pthread_create() 函数讲解

    C语言 pthread_create() 函数讲解

    这篇文章主要介绍了C语言 pthread_create() 函数讲解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-08-08
  • C++抛出和接收异常的顺序

    C++抛出和接收异常的顺序

    这篇文章主要介绍了C++抛出和接收异常的顺序,帮助大家更好的理解和学习C++,感兴趣的朋友可以了解下
    2020-08-08
  • C++操作.json文件的超详细新手教程

    C++操作.json文件的超详细新手教程

    最近因为项目原因需要解析JSON格式数据,所以这篇文章主要给大家介绍了关于C++操作.json文件的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-08-08
  • Win32应用程序(SDK)设计原理详解

    Win32应用程序(SDK)设计原理详解

    这篇文章主要介绍了Win32应用程序(SDK)设计原理,对于理解win32应用程序运行原理有很大的帮助,需要的朋友可以参考下
    2014-08-08
  • C++虚函数的实现机制分析

    C++虚函数的实现机制分析

    这篇文章主要介绍了C++虚函数的实现机制分析,需要的朋友可以参考下
    2014-07-07
  • 详解C++之类和对象(1)

    详解C++之类和对象(1)

    类是创建对象的模板,一个类可以创建多个对象,每个对象都是类类型的一个变量;创建对象的过程也叫类的实例化。每个对象都是类的一个具体实例(Instance),拥有类的成员变量和成员函数
    2021-11-11
  • C语言学生管理系统源码分享

    C语言学生管理系统源码分享

    这篇文章主要为大家分享了C语言学生管理系统的源码,帮助大家学习结构体,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-03-03
  • C++之谈谈构造函数的初始化列表

    C++之谈谈构造函数的初始化列表

    构造函数主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用,这篇文章详细介绍了构造函数的初始化列表,文章中有详细的示例代码,感兴趣的同学可以参考阅读
    2023-04-04
  • C语言链表实现简单图书管理系统

    C语言链表实现简单图书管理系统

    这篇文章主要为大家详细介绍了C语言链表实现简单图书管理系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-03-03
  • C++使用宏实现动态库加载

    C++使用宏实现动态库加载

    开发的时候,有些项目不能静态链接动态库,需要程序运行时加载动态库。本文将使用宏来实现动态库的加载,感兴趣的小伙伴可以跟随小编一起了解一下
    2022-12-12

最新评论