一、一维数组
1.作用
需要保存大量的类型相同的数据
比如:保存全班同学的成绩
2.定义数组
类型名 数组名[数组元素个数]
任何合法的C语言类型,都可以定义成数组
char buf[10]; //最多可以存放10个字符
int buf1[50]; //最多可以存放50个整数
3.数组的初始化以及赋值
写法一: int a[5]={45,85,74,96,63}; //列表初始化
char b[10]="hello";
写法二: int a[5]={45,25}; //部分初始化,其它没有初始化的都是0(可以用于数组的清空)
char b[10]={'h','e','l','l','o'};
写法三: int a[]={85,96}; //[]不写元素个数(省略[]里面的数字),只能在定义数组的时候立马初始化才可以
char b[]="world";
写法四: int a[]; //错误写法
a[0]=78;
写法五: int a[3]; //定义了数组,但是没有初始化
a[3]=88; //数组越界了,下标0--2
写法六: 把数组前面50个元素初始化为100,后面50个初始化为200
int b[100]={[0 ... 49]=100, [50 ... 99]=200};
注意:...左右两边必须要有空格
数组可以用变量来表示元素总个数
4.数组元素的下标
从0开始,使用的时候不要越界
数组越界的危害(容易引起段错误)
int a[10]; //下标范围0--9之间
第一种:往前越界
a[-2]; //访问a[0]前面8个字节的地址里面的数据
第二种:往后越界
a[11]; //访问a[9]后面8个字节的地址里面的数据
段错误:segment fault
由于程序员在代码中访问了非法地址导致的(经常见于指针没有初始化,数组越界)
5.数组的存储
数组在计算机中是连续存储(数组元素的地址是紧挨着的)
6.数组的大小
sizeof(); //求任何类型数据的大小
strlen(); //专门用来求字符串的实际长度
#include
size_t strlen(const char *s);
返回值:字符串的实际长度
参数:s --》你要计算长度的字符串
原理:计算字符串实际长度,遇到\0认为字符串结束了
strlen求字符串的长度不计算\0
C语言是如何存储字符串:
字符串的结尾有个结束标记,结束标记叫做\0,\0是个字符,ASCII码值是0
总结:
第一:sizeof是运算符 strlen是个函数
第二:sizeof适用范围广 strlen只能求字符串的实际长度
第三:sizeof求字符串的长度会把\0算上
7.空字符串,'A'和"A"的区别
char buf[10]=""; //空字符串
'a'; //字符a 单个字符
"a"; //字符串a a和\0
8.char类型数组重复使用,清空数组
8.1 char类型数组重复使用
char buf[10];
while(1)
{
scanf("%s",buf); //假如第一次输入的字符串长一点,下一次输入的字符串短一点,scanf都会自动帮你在字符串后面添加\0保存到buf中,短一点的字符串覆盖长字符串的前面部分,并且有个\0在后面(容易忽略)——》遇到\0停止打印,看不到后面没有覆盖的那一部分
长的会直接把短的覆盖完——》scanf都会自动帮你在字符串后面添加\0
}
8.2 清空数组
数组先清空后使用(数组不清空,里面是随机数)
写法一:
#include
void bzero(void *s, size_t n); //把数组元素全部填充为0
参数: s --》数组名
n --》数组的大小
写法二:
int buf[10]={0}; //部分初始化的原理,其他没有初始化的默认都是0,把数组清零
char buf[5]={0}; //部分初始化的原理,其他没有初始化的默认都是0,把数组清零
清空数组的方法:
9.字符串和字符数组的区别
核心区别
- 字符数组:只是一块连续的内存,用来存字符,没有强制的结束标志。
- 字符串:是以'\0'(ASCII 值为 0 的字符)作为结束标志的字符数组。
二、二维数组
1.作用
int a[5][6]; //行列存放整数,有5行,每一行最多可以存放6个整数,总共可以存放30个整数
应用例子:2048游戏
char b[5][10]; //可以存放5个字符串,每个字符串的最大长度不能超过10个字节,用来保存多个字符串
2.定义二维数组
类型名 数组名[行数][列数]
任何合法的C语言类型,都可以定义成二维数组
char buf[10][20]; //最多存放10个字符串,每个字符串的长度不能超过20个字符
int buf1[50][50]; //50行整数,每一行最多50个整数
3.二维数组的初始化以及赋值
写法一:int a[5][6]={45,78,96}; //部分初始化,正确
char b[3][10]={"hello","world","gec"};
写法二:int a[][6]={89,9}; //正确
char b[][10]={"hello","world","gec"}; //正确
int a[][]={89,9}; //错误
int a[5][]={89,9}; //错误
C 语言允许在初始化时省略第一维的大小,编译器会根据初始化列表自动推断。
写法三:int a[5][6]={{5},{8,9},{89,63}}; //正确,分组初始化
//分组初始化必须从左到右连续(花括号不要跳跃)
int a[5][6]={{5},8,9,{89,63}}; //错误的写法,不满足从左到右连续(花括号中间跳跃了)
char b[3][10]={{'h','e','l','l','o'},{'g','e','c'}}
写法四:int a[5][6]; //分别赋值
a[0][0]=99;
a[1][2]=67;
char b[3][10];
b[0][1]='h';
初始化 vs 赋值:本质区别
在 C 语言中,初始化和赋值是两种完全不同的机制,区别体现在以下几个方面:
对比维度 | 初始化 | 赋值 |
发生时机 | 变量定义时 | 变量定义之后的任何时刻 |
语法性质 | 属于定义语句的一部分 | 属于可执行语句(表达式语句) |
操作对象 | 变量首次获得初值 | 变量改变已有值 |
对数组整体 | 允许(如 char a[10] = f"hello";) | 不允许(a = "hello"非法) |
对结构体整体 | 允许(如 struct S s = {1,2};) | 不允许(除非用复合字面量或逐个成员) |
底层实现 | 编译器在变量生命周期开始前 静态或栈上布置好值 | 运行时通过指令拷贝数据 |
4.数组元素的下标
行和列都是从0开始,使用的时候不要越界
5.二维数组的存储
二维数组在计算机中是连续存储(数组元素的地址是紧挨着的)
二维数组:你可以把它看作是特殊的"一维数组"(可以把二维数组拆分成多个一维数组)
6.二维数组的大小
情况一:两个下标都有
int a[5][6]; //公式:行*列*sizeof(类型)
情况二:少了行下标,笔试题常考
int a[][6]={78,96}; //依据初始化列表中值的个数,确定有几行
列数必须写,行数自动算,元素除不尽,行数向上添,空着补 0 填