<C语言> 数组

news/2024/11/29 22:39:22/

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

1.1 数组的创建

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

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

type arrayName[arraySize];

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

声明一维数组实例:

int numbers[5];  //声明一个int型的一维数组,大小为5char 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);       //000000000061FDF0printf("%p\n", &arr[0]);  //000000000061FDF0printf("%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.ppmy.cn/news/535912.html

相关文章

yolo格式visdrone转换

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

ProGuard混淆及R8优化

前言&#xff1a;使用java编写的源代码编译后生成了对于的class文件&#xff0c;市面上很多软件都可以对class文件进行反编译&#xff0c;况且Android开发的应用程序是用Java代码写的&#xff0c;为了很好的保护Java源代码&#xff0c;我们需要对编译好后的class文件进行混淆。…

数据猿·金猿榜丨2017中国智能语音领域最具潜力创业公司

【数据猿导读】 “2017中国智能语音领域最具潜力创业公司”盘点源于数据猿推出的“金猿榜”系列内容&#xff0c;旨在通过媒体的方式与原则&#xff0c;发掘大数据领域最具潜力的创新型企业 编辑 | sharon 官网 | www.datayuan.cn 微信公众号ID | datayuancn “2017中国智能语音…

揭秘OPhone白手起家前后:一个系统的诞生

转载说明&#xff1a;一个新生事务的诞生&#xff0c;给一些人会可能带来一些生存的威胁&#xff0c;给另外一些人可能会带来一些机会&#xff0c;最近手机操作系统大战硝烟弥漫&#xff0c;如果能掌握好这个机会&#xff0c;对于开发人员来来说可能会带来一笔小财&#xff0c;…

跨界转型 打造大数据旗舰

从厦华电子公告的信息来看&#xff0c;厦华电子结束传统的电子产品业务后&#xff0c;将转向盈利能力较强&#xff0c;发展前景广阔的大数据解决方案业务。 数联铭品主营大数据 厦华电子5月27日晚间公告称&#xff0c;根据上交所二次问询函要求&#xff0c;公司、中介机构等相关…

国产手机光辉岁月

国产手机光辉岁月 TCL手机 昔日辉煌 TCL 移动通信有限公司生产经营GSM 数字移动电话。2001 年该公司取得高速的发展&#xff0c;TCL 手机的销量、销售收入及利润增幅巨大&#xff0c;取得很好效益。全年共销售手机129 万台&#xff0c;比去年同期增长了497.22%&#xff0c; …

股票的最新和历史股息收益率查询(3)

股息收益率 最近12个月的现金分红总额 / A股总市值。 山西焦化的股息收益率走势图 华域汽车的股息收益率走势图 一汽富维的股息收益率走势图 华远地产的股息收益率走势图 华银电力的股息收益率走势图 闻泰科技的股息收益率走势图 江苏索普的股息收益率走势图 ST大控的股…

成都16条新经济优势赛道解读之5G大数据

作者 | 36氪四川 来源 | 36氪四川&#xff08;ID&#xff1a;SC36kr&#xff09; 原标题 | 成都16条新经济优势赛道解读之5G&大数据 当前成都新经济企业已高达36万家&#xff0c;7月15日&#xff0c;成都市委十三届七次全会举行&#xff0c;会上提出成都要构建以新经济为…