二维数组
定义
二维数组本质上是一个行列式的组合,也就是说二维数组由行和列两部分组成。属于多维数组,二维数组数据是通过行列进行解读。
二维数组可被视为一个特殊的一维数组,相当于二维数组又是一个一维数组,只不过它的元素是一维数组。(也就是说数组的元素的类型可以是数组类型)
语法
数据类型 数组名 [行数][列数];//二维数组外层表示行数,内层表示列数
举例:
int arr[3][3] = {{11,22,33},{21,22,23},{31,32,33}};//正确,等价于下面写法
int arr[][3] = {{11,22,33},{21,22,23},{31,32,33}}; //正确广义上称之为柔性数组int arr[3][3] = {{11,12},{21,22,23},{31}}; //正确,等价于下面写法
int arr[3][3] = {{11,12,0},{21,22,23},{31,0,0}};//正确int arr[3][3] = {};//正确,全部为0
int arr[3][3] = {{11}};//正确,未初始化的元素使用0填充int arr[][] = {{11,22,33},{21,22,23},{31,32,33}};//这种写法会编译错误,不能省略
int arr[3][] = {{11,22,33},{21,22,23},{31,32,33}};//这种写法会编译错误,不能省略
注意:在C语言中,二维数组在计算机的存储顺序是按行进行的,即第一维的下标变化慢,第二维的下标变化快。
应用场合
主要应用于数据有行列要求的情况。比如说我们现在要存储班级的学生的成绩。
特殊写法
- 下标可以是整型表达式,如:
a[2-1][2*2-1]
- 下标可以是已经有值的变量或数组元素,如:
a[2*x-1]
- 数据元素可以出现在表达式中,如:
b[1][2] = a[2][3]/2
- 注意:使用数组元素的下标应在已定义数组的大小范围内:应注意区别定义数组大小和引用数组元素的区别。
初始化
-
分行给二维数组赋初值
int arr[3][4] = {{11,12,13,14},{21,22,23,24},{31,32,33,34}};
-
可将所有数据写在一个花括号内,按照排列顺序对元素赋值
int arr[3][4] = {11,12,13,14,21,22,23,24,31,32,33,34};
-
可对部分元素赋初值,其余未赋值部分自动填充
整型默认值-0 | 字符型默认值-\0 | 浮点型默认值-0.0..
int arr[3][4] = {{11},{21,22},{31}};
-
若对全部元素赋初值,自定义数组时可以省略第1维数组的长度,第2维数组的长度必须指明。
int a[][4] = {11,12,13,14,21,22,23,24,31,32,33,34};
-
在分行赋初值时,也可以省略第1维的长度。
int arr[][4] = {{11,12,13},{0},{0,10}};
案例
案例1:
-
需求:二维数组的遍历
-
代码:
/*************************************************************************> File Name: demo01.c> Author: ZK> Description:二维数组的遍历 > Created Time: 2025年02月17日 星期一 09时15分14秒************************************************************************/#include <stdio.h>int main(int argc,char *argv[]) {//创建一个二维数组,必须指定列int arr[][3] = {{11},{21,22},{31,32,33}};//获取二维数组的大小int r_len = sizeof(arr) / sizeof(arr[0]);//遍历二维数组//外层循环,遍历行元素(行元素是数组类型)for(int row = 0;row < r_len; row++){//内层循环,遍历列元素(列元素是单一类型)int c_len = sizeof(arr[row]) / sizeof(int);//内层循环,遍历列元素(列元素时是单一类型)for(int col = 0; col < c_len; col++){//获取元素printf("%-3d",arr[row][col]);}}printf("\n");return 0; }
-
运行结果:
案例2:
-
需求:矩阵的转置(将一个2行3列的数组转换为3行2列的数组)
-
代码:
/*************************************************************************> File Name: demo02.c> Author: ZK> Description:二维数组的转置> Created Time: 2025年02月17日 星期一 09时39分39秒************************************************************************/#include <stdio.h>//定义行和列 #define ROW 2 #define COL 3int main(int argc,char *argv[]) {//准备2个数组用来存放转置前后的数列int arr_before[ROW][COL] = {{11,12,13},{14,15,16}};int arr_after[COL][ROW] = {0};//计算arr_before的大小int r_len = sizeof(arr_before) / sizeof(arr_before[0]);//遍历for(int i = 0; i < r_len; i++){//计算arr_before中的列数int c_len = sizeof(arr_before[i]) / sizeof(int);for (int j = 0; j < c_len; j++ ){printf("%-4d",arr_before[i][j]);//转置arr_after[j][i] = arr_before[i][j];}printf("\n");}printf("\n");//换行//循环输出arr_afterfor(int i = 0;i < sizeof(arr_after)/sizeof(arr_after[0]); i++){for(int j = 0;j < sizeof(arr_after[i])/sizeof(int);j++){printf("%-4d",arr_after[i][j]);}printf("\n");}return 0; }
-
运行结果:
字符数组
概念
元素类型为char字符型的数组,字符数组往往是用来存储字符串数据的。需要注意的一点是,我们C语言中的字符是字节字符。
字节字符:也就是一个字符占1个字节,在C语言中,我们使用char表示字节。
举例:
char a = 'A'; //正确
char b = '1'; //正确
char c = 65; //正确。char支持两种赋值形式,一种是字符,一种是赋值,如果是数值表示ASCII码
char d = "A"; //错误,char字符不能使用双引号
char e = '卜'; //错误,中文一个字符超过了一个char
C语言支持字符串常量,不支持字符串变量
语法
char 数组名[容量];
char 数组名[行容量][列容量];
字符数组的语法就是我们前面所学的一维数组和二维数组的语法,只不过数组的数据类型是char而已。
注意:
如果我们的char数组初始化的时候,没有完全初始化值的时候,空出来的地方使用\0
进行填充。大家要注意,这里的\0只起到占位的作用,我们是无法通过printf进行输出的。
比如:
char c[8] = {'h','e','l','l','o'};//等价于
char c[8] = {'h','e','l','l','o','\0','\0','\0'};
案例
案例1:
-
需求:输出一个字符序列(I LOVE YOU!)
-
代码:
/*************************************************************************> File Name: demo03.c> Author: ZK> Description:字符数组案例 > Created Time: 2025年02月17日 星期一 10时43分10秒************************************************************************/#include <stdio.h>int main(int argc,char *argv[]) {//创建一个数组用来存储I LOVE YOU ASCII中32对应空格char arr[10] = {'I',' ','L','O','V','E',' ','Y','O','U'};//使用for遍历数组for(int i = 0;i < sizeof(arr) / sizeof(char);i++){printf("%c",arr[i]);}printf("\n");return 0; }
-
运行结果:
案例2:
-
需求:输出一个用字符组成的菱形图案
-
代码:
/************************************************************************* > File Name:demo04.c > Author:ZK > Description: > Created Time: 2025年02月17日 星期一 10时50分33秒 ************************************************************************/#include <stdio.h>int main(int argc,char *argv[]) {// 准备数据char arr[5][5] = {{' ',' ','*',' ',' '},{' ','*','*','*',' '},{'*','*','*','*','*'},{' ','*','*','*',' '},{' ',' ','*',' ',' '}};// 遍历数组for (int i = 0;i < sizeof(arr)/sizeof(arr[0]); i++){for (int j = 0; j < sizeof(arr[i])/sizeof(char); j++){printf("%c",arr[i][j]);}printf("\n");}printf("\n");return 0; }
注意:
①如果定义时,不初始化,元素值不确定
char arr1[2]; //此时属于未初始化,元素的值不确定(随机值),大概率是\0.也有可能是其值。 char arr2[5] = {'a','b','c'};//此时属于不完全初始化,未初始化的元素使用\0进行填充
②如果提供的字符个数大于数组长度,则按照语法错误处理;如果字符个数小于数组长度,后面的元素自动为空字符(\0)
③如果提供的字符个数与数组长度相同,可以省略数组长度,系统会自动确定元素个数,适合字符较多时。
字符串结束标志
说明
-
C语言规定,字符串以字符
'\0'
作为结束标志。 -
编译系统对字符串常量自动加一个
\0
作为结束标志。 -
程序中往往通过判断
\0
来检测字符串是否结束。 -
\0
的ASCII码是0,不是一个可显示的字符,是“空操作符”,它什么都不做,不会增加有效字符,仅是一个工程判别的标志。char c[] = {'h','i'};//字符数组 hi char c[] = {'h','i','\0'};//字符串 hi char c[] = "hello";//实际等价于 hello\0// \0(空字符),ASCII码是0 // 0 , ASCII码是48 // 空格,ASCII码是32
字符数组的多样表示
我们的char数组可以以数组的形式一个个输出每个字符;我们的char数组也可以以字符串的方式整体进行输出所有字符。
案例:
/*************************************************************************
> File Name:demo05.c
> Author:ZK
> Description:
> Created Time: 2025年02月17日 星期一 11时24分19秒
************************************************************************/#include <stdio.h>int main(int argc,char *argv[])
{// 字符串的第1种表示:char s1[] = {'h','e','l','l','o',' ','w','o','r','l','d','\0'};// 字符串的第2种表示:char s2[] = {"hello world"};// 字符串常量默认以\0结尾// 字符串的第3种表示:char s3[] = "hello world";// 输出字符串printf("%s\n%s\n%s\n",s1,s2,s3);return 0;
}
注意:
- 字符串的长度与字符数组的长度不一定相同。
- 利用字符串常量可以对字符数组进行初始化,但不能用字符串常量对字符数组赋值。
// 正确演示:利用字符串常量给字符数组初始化
char arr1[6] = "hello";// 错误演示:用字符串常量为字符数组赋值
char arr2[6];
arr2 = "hello";
字符串的基础操作
在用格式化说明符%s进行输入输出时,其输入输出项均为数组名、但在输入时,相邻两个字符串之间要用空格分隔,系统将自动在字符串最后加上\0
,在输入时,遇到结束符\0
作为输出结束标志。
对于字符串操作,我们需要使用到一些系统提供的函数(API操作)。
字符串输入
scanf
语法:
scanf("%s",&数组名);
注意:数组名对应的数组只能是char类型
案例:
/*************************************************************************
> File Name:demo06.c
> Author:ZK
> Description: 字符串输入函数:scanf
> Created Time: 2025年02月17日 星期一 15时26分41秒
************************************************************************/#include <stdio.h>int main(int argc,char *argv[])
{// 创建一个数组,用来存放人的名字char name[20];printf("请输入您的名字:\n");scanf("%s",name);// scanf第二个参数需要传递变量地址,如果是数组,数组名就代表数组的首地址,因为数组本身是没有空间的,其空间就是元素空间printf("您的姓名是%s\n",name);return 0;
}
注意:采用scanf()进行字符串输入,要求字符串中不能存在空格,否则字符串遇到空格就会结束。
fgets
语法:
fgets(数组名,数组容量,stdin);
功能:
从键盘录入一个字符串常量到字符数组,返回字符数组的地址(首地址,默认返回的地址,一般用12位16进制数表示)
说明:
采用fgets进行字符串输入,可获取所有输入的字符串,包含 \n
,在实际的字符串处理时,我们可能需要处理 \n
案例:
/*************************************************************************
> File Name:demo06.c
> Author:ZK
> Description: 字符串输入函数:fgets
> Created Time: 2025年02月17日 星期一 15时26分41秒
************************************************************************/#include <stdio.h>int main(int argc,char *argv[])
{// 创建一个数组,用来存放人的名字char name[20];printf("请输入您的名字:\n");// fgets和scanf只能二选一fgets(name,sizeof(name)/sizeof(name[0]),stdin);printf("您的姓名是%s\n",name);return 0;
}
注意:
① 如果输入的字符串不包括 空格 或 换行,可以使用scanf |fgets
② 如果输入的字符串需要包含 空格 或 换行,只能使用fgets
③ 经过对比,我们发现,在字符串输入中,fgets和scanf相比,fgets友好一些
gets 危险的
语法:
gets(数组名);
功能:
从键盘录入一个字符串常量到字符数组,返回字符数组的地址(首地址,默认返回的地址,一般用12位16进制数表示)
说明:
采用gets进行字符串输入,可获取所有输入的字符串,包含 \n
,在实际的字符串处理时,我们可能需要处理 \n
案例:
/*************************************************************************
> File Name:demo06.c
> Author:ZK
> Description: 字符串输入函数:fgets
> Created Time: 2025年02月17日 星期一 15时26分41秒
************************************************************************/#include <stdio.h>int main(int argc,char *argv[])
{// 创建一个数组,用来存放人的名字char name[20];printf("请输入您的名字:\n");// fgets和scanf只能二选一fgets(name,sizeof(name)/sizeof(name[0]),stdin);printf("您的姓名是%s\n",name);return 0;
}
字符串输出
printf
语法:
printf("%s",数组名);
案例:
/*************************************************************************
> File Name:demo06.c
> Author:ZK
> Description: 字符串输入函数:scanf
> Created Time: 2025年02月17日 星期一 15时26分41秒
************************************************************************/#include <stdio.h>int main(int argc,char *argv[])
{// 创建一个数组,用来存放人的名字char name[20];printf("请输入您的名字:\n");scanf("%s",name);// scanf第二个参数需要传递变量地址,如果是数组,数组名就代表数组的首地址,因为数组本身是没有空间的,其空间就是元素空间printf("您的姓名是%s\n",name);return 0;
}
fputs
语法:
fputs(const char *s,FILE *stream);
功能:
输出一个字符串
说明:
字符串可以包含转义字符
案例:
/*************************************************************************
> File Name:demo06.c
> Author:ZK
> Description: 字符串输入函数:fputs()
> Created Time: 2025年02月17日 星期一 15时26分41秒
************************************************************************/#include <stdio.h>int main(int argc,char *argv[])
{// 创建一个数组,用来存放人的名字char name[20];printf("请输入您的名字:\n");// gets、fgets和scanf只能多选一gets(name);// 输出fputs(name,stdout);// 标准的输出return 0;
}
puts
语法:
puts(const char *s);
功能:
输出一个字符串
说明:
字符串可以包含转义字符
案例:
/*************************************************************************
> File Name:demo06.c
> Author:ZK
> Description: 字符串输入函数:fputs()
> Created Time: 2025年02月17日 星期一 15时26分41秒
************************************************************************/#include <stdio.h>int main(int argc,char *argv[])
{// 创建一个数组,用来存放人的名字char name[20];printf("请输入您的名字:\n");// gets、fgets和scanf只能多选一gets(name);// 输出puts(name);// 标准的输出return 0;
}
字符串转数值
-
strtol
long strtol(const char *str, char **endptr, int base);
将字符串转换为长整型数。
参数说明:
str
:指向要转换的字符串的指针。endptr
:一个指向字符指针的指针。如果提供了这个参数,并且转换成功,*endptr
将被设置为指向第一个未转换字符的指针。如果endptr
是NULL
,则不使用它。base
:用于指定转换的基数。它可以是 2 到 36 之间的值,或者是特殊值 0。如果base
是 0,则函数会根据字符串的前缀(如 “0x” 或 “0X” 表示十六进制,“0” 表示八进制,否则默认为十进制)来自动确定基数。
-
strtoul
unsigned long strtoul(const char *str, char **endptr, int base);
将字符串转换为无符号长整型数。
-
strtod
double strtod(const char *str, char **endptr);
将字符串转换为双精度浮点数。
-
atoi
int atoi(const char *str);
将字符串转换为整型数(不推荐使用,建议使用 strtol )。
-
atol
long atol(const char *str);
将字符串转换为长整型数(不推荐使用,建议使用 strtol )。
-
atof
double atof(const char *str);
将字符串转换为双精度浮点数(不推荐使用,建议使用 strtod )。
案例:
/*************************************************************************
> File Name:demo09.c
> Author:ZK
> Description: 字符串转换数值
> Created Time: 2025年02月17日 星期一 16时20分34秒
************************************************************************/#include <stdio.h>
#include <stdlib.h>int main(int argc,char *argv[])
{printf("%lo,%ld,%lx\n",strtol("12",NULL,8),strtol("12",NULL,10),strtol("12",NULL,16));printf("%lo,%ld,%lx\n",strtol("012",NULL,0),strtol("12",NULL,10),strtol("0x12",NULL,0));int a = 10;printf("%p,%lx\n",&a,&a);return 0;
}