目录
1 一维数组
1.1 一维数组的创建与初始化
1.2.1 一维数组的创建
1.2.2 一维数组的初始化
1.2 一维数组的访问
1.3 一维数组在内存中的存储
2 二维数组
2.1 二维数组的创建与初始化
2.1.1 二维数组的创建
2.2.2 二维数组的初始化
2.2 二维数组的访问
2.3 二维数组在内存中的存储
3 字符数组
3.1 字符数组的创建与初始化
3.2 字符数组在内存中的存储
3.3 补充:字符串的两种定义方式
4 数组大小的计算
4.1 sizeof操作符
4.2 数组大小 & 元素个数 计算
5 指针和数组 & 数组名代表的意义
6 数组传参
7 指针数组
8 数组指针
【数组的定义】:一组相同类型元素的集合。
【数组的下标】:数组每个元素都有对应下标,下标从0开始。
1 一维数组
1.1 一维数组的创建与初始化
1.2.1 一维数组的创建
type_t arr_name [const_n]//即:元素类型 数组名 [数组大小]//示例:int arr[10];char ch[20];
【注意点】
const_n通常是常量表达式,即:
int n = 10;
int arr[n]; //这种方式不能正常创建
但C99标准中引入了变长数组(数组长度可以通过变量确定)的概念,创建时可以使用变量,但这样的数组不能初始化。
这种情况也可以使用宏定义的方式:
#define N 10;
int arr[N];
1.2.2 一维数组的初始化
(1)完全初始化:数组中的所有元素在定义时都被指定了具体的值。
① 元素个数确定,元素值确定
int arr1[5] = {1, 2, 3, 4, 5}; char ch1[5] = {'a', 'b', 'c', 'd', 'e'};
② 只指定元素值
int arr[] = {1, 2, 3};//数组大小为3char ch1[] = {'a', 'b', 'c', 'd', 'e'}; //数组大小为5char ch2[] = "abcde"//数组大小为6,包含:abcde和\0
此时:arr为一个包含3个整形元素的数组,数组元素分别是:1,2,3。
因此,省略数组大小时必须初始化数组内容,因为这种情况下数组大小根据初始化内容确定!!!
关于字符数组详细内容请看第3章~~~
③ 只指定元素个数
int arr[10] = {0};
此时:arr为一个包含10个整形元素的数组,数组元素都被初始化为0。
(2)不完全初始化
① 只确定部分数组内容
int arr[5] = {1, 2, 3};
此时: arr前3个元素分别是:1,2,3,后面的2个元素为0。
【注意区分 int arr[5] = {1} 和 int arr[5] = {0}】
int arr[5] = {1}; 此时arr为一个包含5个整形元素,第一个元素为1,其余为0;
int arr[5] = {0}; 此时arr为一个包含5个整形元素,元素值全为0;
char ch1[5] = {'a', 'b', 'c'};char ch2[5] = "abc";
此时:ch1前3个元素分别是:a b c,后面2个元素为'\0';ch2前3个元素分别是a b c,后面2个元素为'\0'。
关于字符数组详细内容请看第3章~~~
小结:整型数组不完全初始化会将未定义位置值初始化为0;字符数组不完全初始化会将未定义位置值初始化为\0。
1.2 一维数组的访问
数组通过 下标引用操作符[] 来访问内容,注意下标从0开始!
int arr[5] = {1, 2, 3, 4, 5};printf("%d", arr[2]); //打印3
1.3 一维数组在内存中的存储
一维数组在内存中是连续存放的,地址由低到高变化。
【数组越界访问】
数组下标有范围限制,如果有那个元素,那么最后一个元素对应下表(n-1),如果访问下标超出0~(n-1)就产生越界访问。这种情况编译器不一定报错,但不意味着程序是对的。
2 二维数组
2.1 二维数组的创建与初始化
2.1.1 二维数组的创建
int arr[3][4]; //创建3行4列的整型数组char arr[3][4];double arr[3][4];
2.2.2 二维数组的初始化
(1)完全初始化:数组大小确定,元素值确定
int arr[4][5] = { {1, 2, 3, 4, 5}, {2, 3, 4, 5, 6}, {3, 4, 5, 6, 7}, {4, 5, 6, 7, 8} };//创建4行5列整型数组
(2)不完全初始化
int arr[4][5] = { {1, 2}, {2, 3, 4}, {3, 4, 5, 6, 7}, {4, 5, 6, 7, 8} };int arr[][5] = { {1}, {2, 3, 4}, {3, 4}, {4, 5, 6, 7, 8} };
二维数组不完全初始化时:未定义位置会初始化为0。
注意:二维数组可以省略行数,不能省略列数!!!
(3)其他初始化方式
int arr[4][5] = {1,2,3,4,5,6,7,8,9,10};
编译器会按照指定数组大小将已定义的内容填充进去,剩余位置填充为0;
所以此时arr,第一行元素为:1 2 3 4 5,第二行元素为:6 7 8 9 10,剩余两行元素全为0。
注意:这种初始化也是“可以省略行,不可以省略列”!
2.2 二维数组的访问
二维数组也通过 操作符[] 访问元素内容,注意:行列下标都从0开始!
2.3 二维数组在内存中的存储
二维数组在内存中也是连续存放的,地址由低到高变化。
二维数组可以理解为:存放 “一维数组” 的数组。
3 字符数组
3.1 字符数组的创建与初始化
字符数组的创建char ch[10]; //创建一个可以存储10个字符的字符数组
字符数组的初始化 (1)字符串值直接初始化
char ch1[] = "abc";//ch1中存放元素:a b c \0;char ch2[4] = "abc";//ch2中存放元素:a b c \0;char ch3[3] = "abc";//ch3中存放元素:a b c ;
在长度定义准确或未定义长度时,字符串末尾会加上'\0'。
(2) 逐个字符初始化
char ch1[] = {'a', 'b', 'c'};//ch1中存放元素:a b c;char ch2[] = {'a', 98, 'c'};//ch2中存放元素:a b c;//(ch3中将整数98用于初始化字符数组,此时98会被解释为字符值,而98对应ASCII为b,因此此处ch3[1]初始化为b。)//这种情况下虽然会发生转换,但不建议这么定义!char ch3[] = {'a', 'b', 'c', '\0'}
小结:字符串值直接初始化,通常末尾会有'\0';逐个字符初始化,如果不特意添加则不会有'\0'。
【字符串的结尾】:C语言中,字符串以 '\0'(空字符)作为结束标志。
C语言中很多库函数(如:printf、strlen......)在处理字符串时依赖 '\0' 来确定字符串长度。如果字符串缺少这个结束标志,使用这类函数会导致未定义行为!如下图所示
3.2 字符数组在内存中的存储
3.3 补充:字符串的两种定义方式
(1)使用字符数组:直接定义字符数组储存字符,这种情况编译器会自动加上结束符'\0'
int main() { // 定义字符数组,自动添加结束符 '\0' char str1[] = "Hello, world!"; printf("%s\n", str1); // 输出 "Hello, world!" str1[0] = 'Y'; printf("%s\n", str1); //输出 "Yello, world!" return 0;}
实际数组包含13个字符和1个结束符'\0';通过下标可以修改字符串内容。 (2)使用字符指针:通过字符指针指向一个字符串,字符串会在内存“代码段”存储,以'\0'结束
int main() { // 使用字符指针指向字符串字面量 char *str2 = "Hello, world!"; printf("%s\n", str2); // 输出 "Hello, world!" return 0;}
字符指针指向的字符串存储在静态区中,不能被修改,并且空间在程序结束才会被操作系统回收。
4 数组大小的计算
4.1 sizeof操作符
sizeof用于获取数据类型(如:int、char、float、double、结构体......)or对象所占用的内存大小(以字节为单位)。
int main() { printf("Size of int: %zu bytes\n", sizeof(int)); // 4 printf("Size of char: %zu bytes\n", sizeof(char)); // 1 printf("Size of float: %zu bytes\n", sizeof(float)); // 4 printf("Size of double: %zu bytes\n", sizeof(double)); //8 return 0;}
【关于sizeof需要注意的点】
sizeof是操作符,不是函数!sizeof计算字符串大小时,不依赖于字符串的结束符'\0'。(有'\0'则计入,没有也不影响)sizeof的计算发生在编译阶段
int main(){ int a = 5; short s = 11; printf("%d\n", sizeof(s = a + 2)); //2 【sizeof通过类型确定s大小是short类型,大小为2】 //从数学角度看,a+2的值为整型,但一定要赋值给short类型的s,会发生截断,并且short类型占2字节 //截断后s的值应该是7 printf("%d\n", s); //11 【sizeof在编译期间就完成处理,而s的运算结果在可运行程序中计算】 return 0;}
sizeof并不会去访问变量的实际内容,而是根据类型信息确定变量在内存中需要占用的空间大小。 4.2 数组大小 & 元素个数 计算
int main() {int arr[] = { 1,2,3,4,5,6,7,8,9,10 };int sz = sizeof(arr) / sizeof(arr[0]);printf("%d\n", sz); //输出:10}
sizeof(arr) :整个数组大小;
sizeof(arr[0]) :数组首元素大小,即每个元素的大小。
5 指针和数组 & 数组名代表的意义
通常情况下,数组名表示首元素地址。数组名各种操作访问到的内容:
因此,*(arr+i) 和 arr[i] 等价 。
但是有以下两种特殊情况:(1)sizeof(数组名)
数组名单独放在sizeof内部,此时 数组名 表示整个数组,计算出的是整个数组的大小。
(2)&数组名
此时数组名也表示整个数组,取出的是整个数组的地址。
这也代表着 &数组名 结合+-等操作访问到的是整个数组:
上面提到:二维数组可以理解为 存放一维数组 的数组 。结合这个知识点加深理解:
6 数组传参
数组作为参数传递给函数时,实际上是将数组的指针传递给函数,即传递的时数组的地址。
数组名在参数列表中会被隐式转换为指向其第一个元素的指针。
(1)一维数组传参
//方式一void printArray(int arr[], int size) { //函数内容略}//方式二void printArray(int* arr, int size) { //函数内容略}int main() { int arr[] = { 1, 2, 3, 4, 5 }; int sz = sizeof(arr) / sizeof(arr[0]); // 计算数组的元素个数 printArray(arr, sz); // 传递数组和大小 return 0;}
(2)二维数组传参
//方式一void printArray(int arr[3][4]) { //函数内容略}//方式二void printArray(int arr[][4]) {//函数内容略}int main() {int arr[3][4] = { {1,2,3,4},{2,3,4,5},{3,4,5,6} };printArray(arr);}
注意:二维数组传参时,形参列表中也是数组行信息可以省略,列信息不能省略!
在传参过程中,如果粗要用到数组的元素个数,要在传参前计算出结果,将结果作为参数传递给函数。
如果在被调用函数内部计算元素个数,会出现错误。如下图所示
7 指针数组
即:存放指针的数组
char* arr[5];int* arr2[6];
示例:
8 数组指针
即:指向数组的指针
int (*p)[10];
p是一个数组指针,指向一个大小为10的整型数组* :说明p是一个指针变量[10] : 说明数组有10个元素int :说明数组每个元素类型为int