一.一维数组
1.一维数组的定义
(1)格式
① 方式一
与Java存在细微差异
格式:数据类型 数组名[数组长度];
例子:int arr[10];
② 方式二
定义数组的同时存入数据
Tip:
- 下面的定义方法中,数组的长度可以省略,此时会根据大括号内数据的个数,自动确定长度
- 如果定义了长度,但大括号里面填入的数据不足以填满数组,其余位置的默认值是
0
格式:数据类型 数组名[数组长度] = {data1,data2,data3...};
例子:int arr[] = {10, 20, 30};
③ 方式三
定义数组时在指定索引存入数据
1.规则和方式二类似,这里的数组长度以最大自定义存储数据的索引为准
- 例如,自定义索引中最大值为7,那么数组的长度将被设置为8.
2.方式三可以与方式二混合使用
int arr[15] = {1, [5] = 10, 11, [10] = 20, 21};
,索引0、5、6、10、11的位置被赋值
格式:数据类型 数组名[数组长度] = {[索引1] = data1,[索引2] = data2...};
例子:int arr[15] = {[5] = 10,[10] = 20};
索引5和10被赋值了,数组长度为11,其余位置数据为0
2.数据元素的调用
(1)格式
格式:数组名[索引]
例子:int score = arr[0];
(2)关于数组索引越界⭐
在C语言中,对于数组索引越界是不会进行检查的,意味着编译器不一定会报错。
3.数组长度
在C语言中,数组的长度不是直接表示的。数组的长度是在定义数组时确定的,并且存储在编译器中,而不是在运行时存储在数组对象中。因此,不能直接通过数组来获取其长度。
(1)获取数组长度
需要在运行时获取数组的长度,可以使用sizeof
运算符配合数组,间接的计算数组的长度
① 大致实现
其依旧大致就是数组成员都是同一类型的,每个成员的字节长度都一样,只要用数组的字节长度除于成员的字节长度,就可以得到数组的长度了
1.利用sizeof获取数组的字节长度(字节大小)
2.利用sizeof获取数组中单个原始占用的字节长度(字节大小)
3.用数组的字节长度除于单个元素的字节长度,就可以的到当前数组的元素数量即数组长度了
② 例子
int main() {
int a[10];
int arrL = sizeof(a);
int data = sizeof(a[0]);
printf("当前数组的字节长度:%d", arrL);
printf("当前数组单个元素的字节长度:%d", data);
//将上面获取到的数组长度与其单个元素相除,就可以得到数组中元素的个数,即数组的长度了
printf("当前数组的长度%d \n", arrL / data);
return 0;
}
4.数组的遍历
和Java类似,不过就是表示数组长度的那部分相对复杂,要用上面那个方式去表示
Idea的快捷指令:数组名.for
int main() {
int arr[10];
//给数组中的每个元素赋值
for (int i = 0; i < sizeof(arr)/sizeof(int); i++) { //对数组元素arr[0]~arr[9]赋值
arr[i] = i;
}
//遍历数组中的元素
printf("遍历数组中的元素:\n");
for (int i = 0; i < sizeof(arr)/sizeof(int); i++) { //输出arr[0]~arr[9]共10个数组元素
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
5.一位数组内存分析
相较于Java对于内存更加强调了,Java中的数组也是类似的内存分布,但因为不用手动管理,所以之前没有强调。
(1) 内存图
针对如下代码
int a[5] = {1,2,3,4,5};
对应的内存结构:
1.数组名所代表的地址值其实是该数组第一个元素的地址值
2.C语言中数组的每个元素都是有独立的地址的,每个元素以其首地址作为自己的地址值。
-
数组名:记录该数组的首地址,即
a[0]
的地址,作为当前数组的地址 -
数组的各个元素是连续分布的
-
下一个地址值一般就是:当前地址值+当前数据类型所占的字节数
-
假如 a[0] 地址是0x1122,则
a[1]地址= a[0]的地址+int字节数(4)
= 0x1122 + 4 = 0x1126,后面a[2] 地址 = a[1]地址 + int 字节数(4)
= 0x1126 + 4 = 0x112A,依次类推...
-
![image-20230811192849889](https://honescos.oss-cn-hangzhou.aliyuncs.com/img/image-20230811192849889kaVgoIZ9oB202401092140904d155cacb91c49.png)
(2)变长数组
变长数组时在C99标准中被引入,在C11标准中被标记为可选特性。某些编译器可能不支持变长数组,或者可能有特定的限制和行为。
数组声明的时候,数组长度除了使用常量,也可以使用变量或表达式来指定数组的大小。这叫做变长数组
(variable-length array,简称 VLA)。
特征: 数组的长度只有运行时才确定
好处: 使得不必在开发阶段随意的估计数组长度,而是交由程序运行时为数组分配精确的长度
① 实现
Ⅰ.通过变量作为数组长度
任何长度需要运行时才能确定的数组,都是变长数组。比如,
int i = 10;
int a1[i];
int a2[i + 5];
int a3[i + k];
Ⅱ.通过动态内存分配来实现
如果你的编译器版本不支持变长数组,还可以考虑使用动态内存分配(使用malloc()函数
)来创建动态大小的数组。
分配:
int length = 5;
int *arr = (int *)malloc(length * sizeof(int));
释放:
**注:**使用完动态内存创建数组后,一定要回收此数组的内存空间,否则,就会发生内存泄漏的问题
free(arr);
6.一维数组的应用
(1)数组的赋值
① 循环赋值
Ⅰ.优缺点
**优点:**不用引入头文件,IDEA直接代码提示
**缺点:**代码量太大,如果需要遍历多个数组,略显繁琐
Ⅱ.实现
就是Java那一套,很熟了,不赘述
通过for循环,循环遍历数组内的元素,并逐一复制给新的数组
Ⅲ.代码演示
#include
#define LENGTH 3
int main() {
int a[LENGTH] = {10, 20, 30};
int b[LENGTH];
// 复制数组 a 到数组 b
for (int i = 0; i < LENGTH; i++) {
b[i] = a[i];
}
// 打印数组 b 的内容
printf("复制后的数组 b:");
for (int i = 0; i < LENGTH; i++) {
printf("%d ", b[i]);
}
printf("\n");
return 0;
}
② memcy()函数复制
memcpy() 函数定义在头文件 string.h 中,直接把数组所在的那一段内存,再复制一份。
Ⅰ.优缺点
**优点:**更加高效,代码小
**缺点:**需要引入头文件,引入前IDEA不会有代码提示
Ⅱ.使用
头文件:string.h
函数参数:
- 目标数组
- 源数组
- 要复制的字节数:源数组长度 * 源数组数据类型占内存的字节
- 例如:
int a[LENGTH] = {10, 20, 30};
,就是3 * sizeof(int)
- 例如:
Ⅲ.代码示例
#include
#include
#define LENGTH 3
int main() {
int a[LENGTH] = {10, 20, 30};
int b[LENGTH];
// 使用 memcpy 函数复制数组 a 到数组 b
memcpy(b, a, LENGTH * sizeof(int));
// 打印数组 b 的内容
printf("复制后的数组 b:");
for (int i = 0; i < LENGTH; i++) {
printf("%d ", b[i]);
}
printf("\n");
return 0;
}
(2)数组的反转
固有思维是再定义一个数组来配合反转,但这个不可取,尽量采取下面的两个
① 方式一
Ⅰ.实现思想
思想: 数组对称位置的元素互换
实现:
![image-20221117195931777](https://honescos.oss-cn-hangzhou.aliyuncs.com/img/image-20221117195931777t0j7fXNljV202401142108100e2a3e69efb81c.png)
使用循环进行遍历,遍历的长度是数组中间,因为到中间刚好两侧数据都实现替换了
让数据1和数据2两者交换数据
- **数据1:**从起点向后走
- **数据2:**从终点向前走
Ⅱ.代码演示
int main() {
int arr[] = {1,2,3,4,5,6,7,8,9};
int size = sizeof(arr) / sizeof(arr[0]); //数组的长度
printf("原始数组:");
for (int i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
printf("\n");
//主要业务代码
for(int i = 0;i < size / 2;i++){
//将arr[i]的值赋值给temp临时存储
int temp = arr[i];
//使前面的元素和后面的元素交换数据
arr[i] = arr[size - 1 - i];
//再通过temp将前面的元素的数据交给后面的元素
arr[size - 1 - i] = temp;
//至此,前后两个对称元素的数据交换完成
}
printf("反转后的数组:");
for (int i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
② 方式二
整体就代码来看和第一种差不到,就是将原本比较复杂的式子用变量替换了
Ⅰ.示意图:
Ⅱ.代码演示
int main() {
int arr[] = {1, 2, 3, 4, 5,6,7,8,9};
int size = sizeof(arr) / sizeof(arr[0]); //数组的长度
printf("原始数组:");
for (int i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
printf("\n");
int left = 0; // 起始指针
int right = size - 1; // 结尾指针
while (left < right) {
// 交换起始指针和结尾指针指向的元素
int temp = arr[left];
arr[left] = arr[right];
arr[right] = temp;
// 更新指针位置
left++;
right--;
}
printf("反转后的数组:");
for (int i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
7.Char数组与字符串
(1)char数组
顾名思义,就是指数组元素的数据类型为字符类型的数组
(2)字符串⭐
Java中字符串存储的底层也是一个字符输出,只不过在Java中对其进行了封装,封装成String类了而已,这点从Sting具有索引便可略窥一二。
1.在C语言中,字符串通常被表示为一个字符数组,其中每个字符都是一个字节其结束必须以一个空字符(\0
)标记,这是一个特殊的字符,其ASCII值为0,标记其是一个字符串
2.C语言中字符串的空位通常用\0
(空字符)来填充
① 字符串的写法
Ⅰ.标准写法
就是用写字符数组的方式对字符串进行书写,然后在其后面再加一个\0
标记其是一个字符串
**缺点:**过于繁琐
//显式以'\0'为最后一个字符元素结束
char str[] = {'h','e','l','l','o',' ','w','o','r','l','d','\0'};
Ⅱ.简化写法
直接定义字符数组,然后指向两个双引号包裹的字符串。
优点:
- 书写简单
-
\0
标识符不用自己书写,C语言自动添加
char str[] = "hello world";
② 字符串对应数组的长度
Ⅰ.概述
对应的存储为:
字符串对应数组每个字符占用1个字节,其长度是要包含标记符号\0
的,占一个字节,也包含空格,同样占一个字节。
Ⅱ.例子:
**示例一:**其字符数组长度是6
**示例二:**其字符数组长度是50(有标识以标识为准)
char s1[] = "hello"; //示例一
char s1[50] = "hello"; //示例二
③ 字符串长度
Ⅰ.概述
单纯字符串的长度是不包含\0
的,其长度就是其本身具有的字符数。
Ⅱ.获取长度的函数
**函数:**Strlen(参数)
头文件:Sting.h
- 参数:字符串对应的字符数组
示例:
#include
#include //需要加载此头文件
int main() {
char nation[10] = "China";
printf("%d\n", strlen(nation)); //5
}
Ⅲ.示例
**示例一:**其字符长度是5
**示例二:**其字符长度是5
char s1[] = "hello"; //示例一
char s1[50] = "hello"; //示例二