<C语言> 数组

news2024/11/15 19:26:18

1.一维数组的创建和初始化。

1.1 数组的创建

数组是一组相同类型元素的集合。

使用以下方式声明一个一维数组:

type arrayName[arraySize];

type是数组中元素的类型,arrayName是数组的名称,arraySize是数组的大小(即元素的个数)。注意,数组的大小必须是一个常量,不能是变量。

声明一维数组实例:

int numbers[5];  //声明一个int型的一维数组,大小为5

char arr3[10];   //声明一个char型的一维数组,大小为10
float arr4[1];    //声明一个float型的一维数组,大小为1
double arr5[20];  //声明一个double型的一维数组,大小为20

//以下代码只有在支持变长数组的情况下才支持。
int count = 10;
int arr2[count];   //err

注意:数组创建,在C99标准之前, [] 中要给一个常量才可以,不能使用变量。在C99标准支持了变长数 组的概念,数组的大小可以使用变量指定,但是数组不能初始化。

1.2 数组的初始化

数组的初始化是指,在创建数组的同时给数组的内容一些合理初始值(初始化)。一维数组可以在声明时进行初始化,也可以在声明后通过循环或逐个赋值的方式进行初始化。

以下是一些常见的一维数组初始化方法:

//静态初始化
int arr1[10] = {1};  			//不完全初始化,第一个元素初始化为1,其余的元素默认初始化为0
int arr2[] = {1,2,3,4};
int arr3[5] = {12345}char arr4[3] = {'a',98, 'c'};
char arr5[] = {'a','b','c'};
char arr6[] = "abcdef";


// 循环赋值
int numbers[5];
for (int i = 0; i < 5; i++) {
    numbers[i] = i + 1;
}

//逐个赋值
int numbers[5];
numbers[0] = 1;
numbers[1] = 2;
numbers[2] = 3;
numbers[3] = 4;
numbers[4] = 5;

如果没有提供初始值,则数组的元素将根据数组的类型自动初始化。例如,对于整型数组,元素将被初始化为0;对于字符数组,元素将被初始化为空字符(‘\0’)。

1.3 一维数组的使用

1.访问数组元素: 可以使用索引访问数组中的特定元素,索引从0开始,最大索引为数组大小减1。可以使用以下语法访问数组元素:

例如:访问数组numbers中的第三个元素:

int x = numbers[2];

2.修改数组元素

例如:将数组numbers中的第一个元素修改为10:

numbers[0] = 10;

3.遍历数组

可以使用循环结构(如for循环)来遍历整个数组,访问每个元素进行操作。

例如:遍历数组numbers,并将每个元素打印出来:

for (int i = 0; i < arraySize; i++) {
    printf("%d ", numbers[i]);
}

4.传递数组给函数:

数组可以作为函数的参数传递,可以通过传递数组的指针或使用数组作为形参。

例如:函数接受一个整型数组作为参数,计算数组中所有元素的总和:

int calculateSum(int arr[], int size) {
    int sum = 0;
    for (int i = 0; i < size; i++) {
        sum += arr[i];
    }
    return sum;
}

调用该函数并传递数组:

int result = calculateSum(numbers, arraySize);

注意:数组的大小可以通过计算得到

int arr[10];
int sz = sizeof(arr)/sizeof(arr[0]);

1.4一维数组在内存中的存储

一维数组在内存中是连续存储的,它们按照顺序存储在一段连续的内存空间中。

当你声明一个一维数组并为其分配内存时,计算机会为数组分配一块连续的内存空间,以存储数组的元素。数组的大小决定了需要分配多少内存空间,而每个元素的大小取决于数组元素的数据类型。

#include <stdio.h>
int main() {
    int arr[10] = {0};
    int i = 0;
    int sz = sizeof(arr) / sizeof(arr[0]);

    for (i = 0; i < sz; ++i) {
        printf("&arr[%d] = %p\n", i, &arr[i]);
    }
    return 0;
}

输出结果如下:

&arr[0] = 000000000061FDF0
&arr[1] = 000000000061FDF4
&arr[2] = 000000000061FDF8
&arr[3] = 000000000061FDFC
&arr[4] = 000000000061FE00
&arr[5] = 000000000061FE04
&arr[6] = 000000000061FE08
&arr[7] = 000000000061FE0C
&arr[8] = 000000000061FE10
&arr[9] = 000000000061FE14

可以看到因为是int类型,所以每次递增4字节

2.二维数组的创建和初始化

二维数组是一个由多个一维数组组成的数组。它可以看作是一个表格或矩阵,具有行和列的概念。

2.1 二维数组的创建

//数组创建
int arr[3][4];  //三行四列的整型二维数组
char arr[3][5];  //三行五列的字符型二维数组
double arr[2][4];   //二行四列的double型二维数组

2.2 二维数组的初始化

1.按行初始化:

初始化一个包含3行4列的整型二维数组:

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

在这个例子中,matrix 是一个包含3行4列的整型二维数组,并且按行初始化了每个元素的值。

2.使用循环初始化:

可以使用嵌套循环为二维数组的每个元素赋值。

例如,初始化一个包含3行4列的整型二维数组,每个元素都被赋值为0:

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

在这个例子中,通过嵌套的循环,对 matrix 的每个元素进行赋值操作。

3.部分初始化:

可以只对二维数组的一部分元素进行初始化,其余元素将自动初始化为默认值(如0)。

例如,初始化一个包含3行4列的整型二维数组,只对前两行进行初始化:

int matrix[3][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}};

在这个例子中,前两行的元素被初始化,第三行的元素将被自动初始化为0。

注意:二维数组如果有初始化,行可以省略,列不能省略

以下是一个例子:

int matrix[][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};

在这个例子中,我们省略了行数,但指定了每行的列数为3。编译器会根据提供的初始化值自动计算出行数为3,并分配相应的内存空间。

另外,还可以对部分元素进行初始化:

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

在这个例子中,我们只提供了两行的初始化值,而未指定行数。编译器会根据提供的初始化值自动计算出行数为2,并分配相应的内存空间。第三行将被自动初始化为默认值(通常为0)。

2.3 二维数组的使用

二维数组的使用也是通过下标的方式。

1.访问数组元素:

例如,访问一个名为 matrix 的二维数组中的元素:

int matrix[3][4];
matrix[1][2] = 42;  // 将第二行第三列的元素赋值为42

2.遍历数组:

可以使用嵌套的循环来遍历二维数组的所有元素。外层循环用于遍历行,内层循环用于遍历列。

例如,遍历一个名为 matrix 的二维数组并打印所有元素:

int matrix[3][4];
for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 4; j++) {
        printf("%d ", matrix[i][j]);
    }
    printf("\n");
}

3.多维数组作为函数参数:

你可以将二维数组作为函数的参数进行传递。在函数声明时,需要指定数组的列数,可以省略行数。

例如,定义一个接受二维整型数组作为参数的函数:

void printMatrix(int matrix[][4], int numRows) {
    for (int i = 0; i < numRows; i++) {
        for (int j = 0; j < 4; j++) {
            printf("%d ", matrix[i][j]);
        }
        printf("\n");
    }
}

2.4 二维数组在内存中的存储

二维数组在内存中以连续的方式存储其元素。C语言使用行优先(row-major)顺序来存储二维数组,即将相邻的元素按行排列在内存中。

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

在内存中,这个二维数组会被存储为一段连续的内存块,每个元素按行排列,如下所示:

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

从内存的角度来看,二维数组 matrix 实际上是一维的,但是通过行索引和列索引的组合可以方便地访问到特定的元素。

当你使用 matrix[i][j] 来访问数组元素时,编译器会根据数组的类型和索引的值计算出正确的内存地址。对于上述示例,访问 matrix[1][2] 时,编译器会计算出内存地址为 &matrix[1][2],并返回对应的值。

需要注意的是,行和列的索引都是从0开始的。对于二维数组 matrix,行索引的范围是0到2,列索引的范围是0到3。

像一维数组一样,这里我们尝试打印二维数组的每个元素。

#include <stdio.h>
int main() {
    int arr[3][4];
    int i = 0;
    for (i = 0; i < 3; i++) {
        int j = 0;
        for (j = 0; j < 4; j++) {
            printf("&arr[%d][%d] = %p\n", i, j, &arr[i][j]);
        }
    }
    return 0;
}

输出结果:

&arr[0][0] = 000000000061FDE0
&arr[0][1] = 000000000061FDE4
&arr[0][2] = 000000000061FDE8
&arr[0][3] = 000000000061FDEC
&arr[1][0] = 000000000061FDF0
&arr[1][1] = 000000000061FDF4
&arr[1][2] = 000000000061FDF8
&arr[1][3] = 000000000061FDFC
&arr[2][0] = 000000000061FE00
&arr[2][1] = 000000000061FE04
&arr[2][2] = 000000000061FE08
&arr[2][3] = 000000000061FE0C

3.数组越界

当访问数组时,数组越界指的是使用了超出数组有效索引范围的索引值。在C语言中,数组的索引从0开始,最大索引为数组长度减1。数组越界会导致未定义的行为,可能会产生以下问题:

1.读取未初始化的内存:

当尝试读取超出数组范围的元素时,访问到的内存位置可能不属于数组。这意味着实际上读取的是未初始化的内存内容,其值是不确定的。这可能导致程序产生错误的结果或不可预测的行为。

int arr[5] = {1, 2, 3, 4, 5};
int value = arr[10];  // 越界访问

在上面的例子中,arr 数组的长度为5,但是我们尝试通过索引10访问元素,这超出了数组的有效索引范围,会导致未定义的行为。

2.写入非法内存:

当尝试在超出数组范围的位置写入数据时,实际上会修改内存中不属于数组的位置。这可能导致其他变量或数据被修改,或者导致程序崩溃。

int arr[3] = {1, 2, 3};
arr[5] = 10;  // 越界写入

在上面的例子中,arr 数组的长度为3,但是我们尝试通过索引5写入元素,这超出了数组的有效索引范围,会导致写入非法内存位置,破坏了内存的完整性。

3.缓冲区溢出:

如果使用数组来存储字符串或其他数据,并尝试在超出数组范围的位置写入数据,可能会导致缓冲区溢出。这可能会破坏相邻的内存数据或导致安全漏洞,如缓冲区溢出攻击。

char str[5] = "Hello";
str[5] = '!';  // 越界写入

在上面的例子中,str 数组的长度为5,但是我们尝试在第6个位置写入字符 '!',这超出了数组的有效索引范围,可能会破坏相邻内存的数据或导致字符串结束符 \0 的缺失。

避免数组越界问题非常重要。确保在访问数组时使用有效的索引,并在迭代数组时小心处理边界条件。C语言本身是不做数组下标的越界检查,编译器也不一定报错,但是编译器不报错,并不意味着程序就 是正确的,所以程序员写代码时,最好自己做越界的检查。

4.数组作为函数参数

往往我们在写代码的时候,会将数组作为参数传个函数,比如:我要实现一个冒泡排序函数。

//方法1:
#include <stdio.h>
void bubble_sort(int arr[]) {
    int sz = sizeof(arr) / sizeof(arr[0]);//这样对吗?
    int i = 0;
    for (i = 0; i < sz - 1; i++) {
        int j = 0;
        for (j = 0; j < sz - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                int tmp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = tmp;
            }
        }
    }
}
int main() {
    int arr[] = {3, 1, 7, 5, 8, 9, 0, 2, 4, 6};
    bubble_sort(arr);//是否可以正常排序?
    for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++) {
        printf("%d ", arr[i]);
    }
    return 0;
}

在这里插入图片描述

经过调试发现sz=1,很奇怪,sizeof(arr) / sizeof(arr[0])为什么计算为1?这是因为在函数参数中,数组会退化为指针,无法通过 sizeof 运算符正确地获取数组的大小。在函数内部,参数 arr 实际上是一个指向整型的指针。

要在函数内部获取数组的大小,你需要传递数组的长度作为额外的参数,或者在数组的最后一个元素后面添加一个特定的标记来表示数组的结尾。

所以正确的方式如下:

void bubble_sort(int arr[], int size) {
    int sz = size;
    // 其他代码不变
}

int main() {
    int arr[] = {3, 1, 7, 5, 8, 9, 0, 2, 4, 6};
    int size = sizeof(arr) / sizeof(arr[0]);
    bubble_sort(arr, size);
    // 其他代码不变
}

4.1数组名是什么?

#include <stdio.h>
int main() {
    int arr[10] = {1, 2, 3, 4, 5};
    printf("%p\n", arr);       //000000000061FDF0
    printf("%p\n", &arr[0]);  //000000000061FDF0
    printf("%d\n", *arr);     //1
    //输出结果
    
    return 0;
}

结论:

数组名是数组首元素的地址。(有两个例外)

如果数组名是首元素地址,那么:

int arr[10] = {0};
printf("%d\n", sizeof(arr));

为什么输出的结果是:40?

补充:

  1. sizeof(数组名),计算整个数组的大小,sizeof内部单独放一个数组名,数组名表示整个数组。
  2. &数组名,取出的是数组的地址。&数组名,数组名表示整个数组。

除此1,2两种情况之外,所有的数组名都表示数组首元素的地址。

此外,还需要注意的是,数组名本身是一个常量指针,不能进行修改。你不能对数组名使用赋值操作,例如 arr = some_other_array; 是非法的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/677333.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

linux系统中如何制作rootfs?详细教程

如何制作rootfs&#xff1f;安排&#xff01;想直奔主题的&#xff0c;直接跳到第四部分。 一、分析 1. 文件系统简介 理论上说一个嵌入式设备如果内核能够运行起来&#xff0c;且不需要运行用户进程的话&#xff0c;是不需要文件系统的&#xff0c;文件系统简单的说就是一种…

硬件入门之什么是mos管

硬件入门之什么是mos管 文章目录 硬件入门之什么是mos管一、mos管是什么&#xff1f;MOS管常用于&#xff1a; 驱动大功率电路中。MOS选型参数mos管调参数 二、实际应用场景1.防反接保护电路&#xff09;2.防过压保护电路3.防反接防过压电路一体电路4.驱动电路 总结 一、mos管是…

VS+QT+VTK三维网格显示-点面选择-法线法向量显示-配准-分割窗体程序

程序示例精选 VSQTVTK三维网格显示-点面选择-法线法向量显示-配准-分割窗体程序 如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对<<VSQTVTK三维网格显示-点面选择-法线法向量显示-配准-分…

Quartz使用H2数据库(嵌入模式)进行持久化

使用H2(嵌入模式)持久化Quartz任务 &#x1f51d;前言&#xff1a; Quartz在包内提供了多种数据库的sql文件&#xff0c;大家可以选择方便的使用。路径如下。 1.初始化h2(不使用Server模式) public class InitH2 {private static String USER_NAME "lee";private…

Unity简单操作:InputSystem获取WASD键盘输入 移动人物

目录 安装InputSystem 在编辑的脚本中使用 InputSystem生成的脚本 Unity版本&#xff1a;2019.2.3f1 安装InputSystem 菜单栏/Window/Package Manager/Input System 工程面板内 右键-->创建Input Actions 选中New Controls改名为PlayerControls 然后属性 面板按下Edit as…

软件项目管理 第七章 软件项目的质量管理与配置管理 课后习题参考答案——主编:李冰、张桥珍、刘玉娥

第七章 软件项目的质量管理与配置管理 课后习题参考答案 1.选择题 (1)项目质量管理的最终责任由谁来承担?&#xff08;D&#xff09; A.项目开发人员 B.采购经理 C.质量经理 D.项目经理 (2)“质量成本”是一个项目管理概念,它说明了下列哪项成本?…

HDFS 写流程源码分析

HDFS 写流程源码分析 一、客户端&#xff08;一&#xff09;文件创建及Pipeline构建阶段&#xff08;二&#xff09;数据写入&#xff08;三&#xff09;输出流关闭 二、NameNode端&#xff08;一&#xff09;create 环境为hadoop 3.1.3 一、客户端 以下代码创建并写入文件。 …

如何优雅地安装 Android Studio

&#x1f4ad; 写在前面&#xff1a;我们假设读者已经搞定 JDK 了&#xff0c;如果没搞定请先搜索 JDK 的安装教程。访问 Oracle JDK 下载页面&#xff1a;访问 Java Downloads | Oracle &#xff0c;点击 "JDK Download" 按钮。选择适合您操作系统的 JDK 版本&#…

设计模式之模板方法模式笔记

设计模式之模板方法模式笔记 说明Template Method(模板方法)目录模板方法模式示例类图抽象类包菜类菜心类测试类 说明 记录下学习设计模式-模板方法模式的写法。JDK使用版本为1.8版本。 Template Method(模板方法) 意图:定义一个操作中的算法骨架&#xff0c;而将一些步骤延…

yolo格式visdrone转换

目录 yolo格式转换1. Visdrone2019格式转换 yolo格式转换 1. Visdrone2019格式转换 数据集下载地址https://aistudio.baidu.com/aistudio/datasetdetail/115729 如果是visdrone数据集&#xff0c;直接使用txt2xml.py去转换&#xff0c;修改annotation和img的路径&#xff0c…

<Linux开发>驱动开发 -之- Linux LCD 驱动

&#xff1c;Linux开发&#xff1e;驱动开发 -之- Linux LCD 驱动 交叉编译环境搭建&#xff1a; &#xff1c;Linux开发&#xff1e; linux开发工具-之-交叉编译环境搭建 uboot移植可参考以下&#xff1a; &#xff1c;Linux开发&#xff1e; -之-系统移植 uboot移植过程详细…

网络基础一

网络发展 独立模式&#xff1a;计算机之间相互独立。 网络互联&#xff1a;多台计算机连接在一起&#xff0c;完成数据共享。 局域网LAN&#xff1a;计算机数量更多了&#xff0c;通过交换机和路由器连接在一起&#xff1b; 广域网WAN&#xff1a;将远隔千里的计算机都连在…

[BPU部署教程] 万字长文!通透解读模型部署端到端大流程——以终为始,以行为知

去年6月份拿到开发板到现在&#xff0c;转眼已经过去大半年了&#xff0c;这个博客11月初就在写&#xff0c;断断续续写到现在。C部署需要考虑的问题很多&#xff0c;如果只给个简单部署教程的话&#xff0c;就算整理出来&#xff0c;感觉帮助也不大&#xff0c;各位开发时候我…

YOLOv5改进系列(11)——添加损失函数之EIoU、AlphaIoU、SIoU、WIoU

【YOLOv5改进系列】前期回顾: YOLOv5改进系列(0)——重要性能指标与训练结果评价及分析 YOLOv5改进系列(1)——添加SE注意力机制

模版方法模式在 JDK 及 spring 源码中的应用

模版方法模式 模板方法模式是一种行为设计模式&#xff0c; 它在超类中定义了一个算法的框架&#xff0c; 允许子类在不修改结构的情况下重写算法的特定步骤。 更多有关于模版方法模式的介绍详见&#xff1a;https://refactoringguru.cn/design-patterns/template-method 模版…

津津乐道设计模式 - 委派模式详解(以家庭弟位让你彻底明白)

&#x1f337; 古之立大事者&#xff0c;不惟有超世之才&#xff0c;亦必有坚忍不拔之志 &#x1f390; 个人CSND主页——Micro麦可乐的博客 &#x1f425;《Docker实操教程》专栏以最新的Centos版本为基础进行Docker实操教程&#xff0c;入门到实战 &#x1f33a;《RabbitMQ》…

STM32 Proteus仿真DHT11温度湿度光敏光强DS1302闹钟-0044

STM32 Proteus仿真DHT11温度湿度光敏光强DS1302闹钟-0044 Proteus仿真小实验&#xff1a; STM32 Proteus仿真DHT11温度湿度光敏光强DS1302闹钟-0044 功能&#xff1a; 硬件组成&#xff1a; STM32F103C6T6单片机 DHT11温度湿度光敏电阻采集光强 多个按键模拟红外遥控1个LED…

node.js--vue仓库进销存管理信息系统whkb8

随着社会的发展&#xff0c;系统的管理形势越来越严峻。越来越多的用户利用互联网获得信息&#xff0c;但各种信息鱼龙混杂&#xff0c;信息真假难以辨别。为了方便用户更好的获得仓库管理信息&#xff0c;因此&#xff0c;设计一种安全高效的仓库管理信息系统极为重要。 为设计…

【从零开始学习JAVA | 第十五篇】 多态

前言&#xff1a; 本篇我们来解释一下什么是多态关系&#xff0c;多态关系属于面向对象三大特征的最后一个&#xff0c;可以说面向对象的重点就在多态&#xff0c;因此我们要学好面向对象编程思想&#xff0c;就要学好多态。 多态&#xff1a; Java中的多态是指同一类对象在不同…

nssctf之SSRF刷题记录

[NISACTF 2022]easyssrf 题目讲的主要是ssrf以及php伪协议的能力&#xff0c;题目详情如下 一般来说&#xff0c;当一个网站出现curl类的功能时就可能会出现ssrf之类的漏洞&#xff0c;常见的ssrf协议如下 file:/// dict:// sftp:// ldap:// tftp:// gopher://file:// 这种…