一维数组
二维数组
数组越界
数组作为函数参数
数组名是什么
总结
数组
前面简单的认识了一下数组,这次更加深入的去了解一下C语言中的数组。
C语言是支持多维数组的,但是经常使用的是一维数组和二维数组。
存放一个数据就要创建一个变量来接收这个数据,如果要存放一百个数据就要创建一百个变量来接收这个一百个数据,这样做会很麻烦。所以C语言中引入了数组,创建一个数组就可以开辟一片连续的空间来存放多个数据。
数组是一组相同类型元素的集合。
一维数组1、数组的创建
数组的创建方式: type_t arr_name [const_n];
- type_t :是指数组的元素类型
- arr_name:是数组名
- const_n :是一个常量表达式,用来指定数组的大小
//创建一个存放5个元素的数组,这个数组中每个元素都是整型 //数组名:arr,数组类型:int,数组大小:5(可以存放5个元素) int arr[5]; //创建一个存放10个元素的数组,这个数组中每个元素都是字符 //数组名:ch,数组类型:char,数组大小:10(可以存放10个元素) char ch[10]; //创建一个存放15个元素的数组,这个数组中每个元素都是浮点型 //数组名:du,数组类型:double,数组大小:15(可以存放15个元素) double du[15];
补充:变长数组。
变长数组是指用整型变量或表达式声明或定义的数组大小,而不是说数组的长度会随时变化,变长数组在其生存期内的长度同样是固定的。
- C99标准之前是不支持数组的大小使用变量,只能使用常量!
- C99中增加了变长数组的概念,允许数组的大小是变量,而且要求编译器支持C99。
- 变长数组创建的时候不可以初始化。
- 我们常见的编译器对C99的支持都不够好,这里只是简单的了解一下。
2、数组的初始化
创建的同时给一些初始值叫初始化。
1.整型数组初始化
(1)完全初始化:数组的大小是多少就初始化多少个元素
int arr[10]={1,2,3,4,5,6,7,8,9,10};
如果数组中存放的元素大于数组的大小,程序运行就会报错。
(2)不完全初始化:只初始化一部分元素,剩下的元素默认初始化为0。
int arr[10] = { 1,2,3 };
(3)将数组全部初始化为0
int arr[10] = { 0 };
这种写法是将数组中第一个元素初始化为0,剩下的元素默认初始化为0。
(4)对数组初始化不指定数组大小。
int arr[ ] = {1,2,3};
当对数组初始化的同时可以不指定数组的大小,数组的大小是根据初始化的内容来确定。
补充:如果对数组初始化成下面这种代码,再对该数组赋多个值编译器就会报错。
int arr[] = { 0 };
将数组中的元素初始化为0,这里数组的大小是1。如果输入多个值会造成数组越界。
数组创建的时候也可以不初始化 ,但不对数组初始化数组中存放的就是随机值。
这样在使用数组时对数组是不可以控的,所以建议在创建数组的同时对数组进行初始化(可以初始化为0)。
2.字符数组初始化
char ch1[] = { 'a',98,'b' }; //这里的98是 b 的ASCII码值 char ch2[] = { 'a','b','c' }; char ch3[] = "abcde";
- ch1 与 ch2有3个元素,数组的大小是3个字节
- ch3 有4个元素,数组的大小是4个字节
补充:
(1)strlen是一个库函数,使用时要引入头文件
计算的是字符串的长度,并且只能针对字符串,关注的是字符串中是否有'',计算的是''之前的字符个数。
(2)sizeof是一个 *** 作符(运算符)
sizeof是用来计算所占内存空间大小的,任何数据类型都可以使用,只关注空间的大小,不在乎内存中是否存在''。
计算一下数组的大小:
计算一下字符串长度
因为ch1和ch2中并没有存放字符 '',所以计算出的的是随机值,ch3中字符串末尾隐藏了字符'',strlen只计算 '' 之前的字符,所以计算出的结果是3。
字符数组初始化是也可以指定大小,但是要注意当使用字符数组存放字符串的时候,数组的大小要比字符串中的字符个数多1。
char arr1[3] = { 'a','b','c' }; char arr2[4] = "abc";
3、数组的使用
数组是有下标的,数组的下标是从0开始的,如果有n个元素,最后一个元素的下标是 n - 1
(1)使用数组的下标来访问数组的一个元素。
*** 作符: [ ] ,下标引用 *** 作符。就是数组访问的 *** 作符。
既然是 *** 作符那肯定有 *** 作数(比如 a+b 中,a 和 b 就是 + 的 *** 作数)。arr 和 5 就是 [ ] 的 *** 作数(这个可以在写代码的时候慢慢去感受)。
(2)访问数组的所有元素(输出)
#includeint main() { int arr[] = { 1,2,3,4,5,6,7,8,9,10 }; int i = 0; //计算数组的元素个数 //sizeof(arr)计算的是这个数组的大小 //sizeof(arr[0])计算数组第一个元素的大小 //因为这个一个整型数组,每个元素都是整型,所占内存的大小都是4 int sz = sizeof(arr) / sizeof(arr[0]); for (i = 0; i < sz; i++) { printf("%d ", arr[i]); } printf("n"); return 0; }
运行结果
(3)数组赋值(输入)
#includeint main() { int arr[] = { 1,2,3,4,5,6,7,8,9,10 }; int i = 0; int sz = sizeof(arr) / sizeof(arr[0]); for (i = 0; i < sz; i++) { scanf("%d", &arr[i]); } for (i = 0; i < sz; i++) { printf("%d ", arr[i]); } printf("n"); return 0; }
运行结果
总结:
- 数组是使用下标来访问的,下标是从0开始。
- 数组的大小可以通过计算得到。
int arr[10]; int sz = sizeof(arr) / sizeof(arr[0]);
4、数组在内存中的存储
#includeint main() { int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; int i = 0; //打印数组每个元素的地址 for (i = 0; i < 10; i++) { printf("&arr[%d] = %pn", i, &arr[i]); } return 0; }
运行结果
#includeint main() { int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; int i = 0; int* p = &arr[0]; for (i = 0; i < 10; i++) { printf("%p -------%pn", p + i, &arr[i]); } return 0; }
只要指针变量 p 指向数组第一个元素的地址,p + i 就是 arr[ i ] 的地址 。
可以使用指针 p + i 访问数组的元素。
二维数组随着数组下标的增长,元素的地址也在有规律的递增。
由此可以得出结论:数组在内存中是连续存放的。
1、数组的创建
//创建一个3行4列的整型数组,数组中每个元素都是整型 int arr1[3][4]; //创建一个2行5列的整型数组,数组中每个元素都是浮点型 double arr2[2][5]; //创建一个3行5列的整型数组,数组中每个元素都是字符型 char arr3[3][5];
2、数组的初始化
(1)完全初始化
int arr1[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };
(2)不完全初始化
int arr1[3][4] = { 1,2,3,4,5,6};
在指定位置放指定元素
二维数组初始化的时候,在大括号里将元素放到几个大括号内就表示将二维数组设置为几行。
第一行放1,2;第二行放3,4;第三行放5,6;
(3)二维数组如果有初始化,行可以省略,列不能省略
int arr1[][3] = { {3,4},{5,6} }; int arr2[][3] = { 3,4,5,6 };
二维数组可以省略行下标不可以省略列下标。
- 二维数组在内存中的存储是连续的,第二行必须放到第一行的后面。
- 省略了列但知道行,不能确定下一行的起始位置。
- 省略了行但知道列,可以确定每一行的起始位置。
- 数组会根据初始化的内容将一行占满后,再将下一个元素放到下一行。
3、数组的使用
(1)使用数组的下标来访问数组的一个元素。
(2)访问数组的所有元素(输出)
#includeint main() { int arr1[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 }; int i = 0; int j = 0; for (i = 0; i < 3; i++) { for (j = 0; j < 4; j++) { printf("%d ", arr1[i][j]); } printf("n"); } return 0; }
运行结果
(3) 数组赋值(输入)
#includeint main() { int arr1[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 }; int i = 0; int j = 0; for (i = 0; i < 3; i++) { for (j = 0; j < 4; j++) { scanf("%d", &arr1[i][j]); } } for (i = 0; i < 3; i++) { for (j = 0; j < 4; j++) { printf("%d ", arr1[i][j]); } printf("n"); } return 0; }
运行结果
二维数组可以通过行列下标访问数组,行列下标都是从0开始的。
4、数组在内存中的存储
#includeint main() { int arr1[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 }; int i = 0; int j = 0; for (i = 0; i < 3; i++) { for (j = 0; j < 4; j++) { printf("&arr1[%d][%d] = %pn", i, j, &arr1[i][j]); } printf("n"); } return 0; }
运行结果
数组越界数组的下标是有范围限制的。
数组的下规定是从0开始的,如果数组有n个元素,最后一个元素的下标就是n-1。
数组的下标如果小于0,或者大于n-1,就是数组越界访问了,超出了数组合法空间的访问。
正确的代码 :
C语言本身是不做数组下标的越界检查,编译器也不一定报错,但是编译器不报错,并不意味着程序就是正确的。
程序员写代码时,最好自己做越界的检查。
数组作为函数参数写一个冒泡排序来感受一下数组传参
冒泡排序:
相邻的两个元素进行比较,不满足条件(升序或降序)就进行交换。
一趟冒泡排序可以确定一个数的位置(让当前待排序的数组中一个元素来到最终应该出现的位置)。n个元素就进行n-1趟排序。
代码
#includevoid bubble_sort(int arr[]) { int i = 0; int j = 0; //计算元素个数 int sz = sizeof(arr) / sizeof(arr[0]); //确定趟数 for (i = 0; i < sz - 1; i++) { //一趟冒泡排序的过程 for (j = 0; j < sz - 1 - i; j++) { if (arr[j] > arr[j + 1]) { //交换 int temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } } int main() { int arr[10] = { 10,9,8,7,6,5,4,3,2,1 }; int i = 0; //设计一个函数对arr数组进行排序 - 冒泡排序 bubble_sort(arr); for (i = 0; i < 10; i++) { printf("%d ", arr[i]); } printf("n"); return 0; }
运行结果
程序执行后根据结果可以看出,此时写的冒泡排序并没有将数组中的元素按升序排列。
错误原因:
根据代码的调试结果可以得出:实际上数组传参,传递的不是整个数组,传过去的是数组首元素的地址。
数组传参传递数组首元素的地址的原因:
- 因为数组是在内存中开辟一大块空间,如果将这块空间传递过去,那么函数在接收的时候也要开辟一样大小的空间来接收这个数组,这样做就造成了空间的浪费。
- 数组在内存中开辟的空间都是连续的(地址是连续的),数组传参的时候只要将数组首元素的地址传递过去,就可以找到整个数组的元素。所以数组传参的时候只传递了首元素的地址,这样不会浪费空间,还可以找到数组中的所有元素。
因为数组传参传递的是首元素的地址,所以在接收地址的时候可以写出指针的形式。
- 严格的讲,函数接收数组传递的地址参数应该用指针接收,形参可以写出数组的形式是因为,写出数组的形式可以帮助初学者更好的理解。
- 因为数组传递的是首元素的地址,所以需要在main函数中先计算出元素的个数,再将元素个数传递给函数。
正确的代码
#include//void bubble_sort(int* arr,int sz) void bubble_sort(int arr[], int sz) { int i = 0; int j = 0; //确定趟数 for (i = 0; i < sz-1; i++) { //一趟冒泡排序的过程 for (j = 0; j < sz - 1 - i; j++) { if (arr[j] > arr[j + 1]) { //交换 int temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } } int main() { int arr[] = { 10,9,8,7,6,5,4,3,2,1 }; //计算元素个数 int sz = sizeof(arr) / sizeof(arr[0]); int i = 0; //设计一个函数对arr数组进行排序 - 冒泡排序 //实际上数组传参,传递的不是整个数组,传过去的是数组首元素的地址 bubble_sort(arr, sz); for (i = 0; i < sz; i++) { printf("%d ", arr[i]); } return 0; }
运行结果
数组名是什么#includeint main() { int arr[] = { 1,2,3,4 }; printf("%dn", sizeof(arr)); return 0; }
运行结果
打印地址,看一下数组名
#includeint main() { int arr[] = { 1,2,3,4 }; printf("%pn", arr);//arr是数组首元素的地址 printf("%pn", &arr[0]);//数组首元素的地址 printf("%pn", &arr);//数组的地址 return 0; }
运行结果
从结果可以看出,数组首元素的地址与数组的地址是相同的,可是两个所代表的意义并不相同。
给每个地址加1,看一下结果
#includeint main() { int arr[] = { 1,2,3,4 }; printf("%pn", arr);//arr是数组首元素的地址 printf("%pn", &arr[0]);//数组首元素的地址 printf("%pn", &arr);//数组的地址 printf("n"); printf("%pn", arr+1);//arr是数组首元素的地址 printf("%pn", &arr[0]+1);//数组首元素的地址 printf("%pn", &arr+1);//数组的地址 return 0; }
运行结果
- sizeof(数组名),计算整个数组的大小,sizeof内部单独放一个数组名,数组名表示整个数 组。
- &数组名,取出的是数组的地址。&数组名,数组名表示整个数组。
- 除了这两种情况,所有的数组名都表示数组首元素的地址。
这篇文章详细的叙述了C语言中数组的知识点,可能有些地方说的不是很清楚,望谅解!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)