一.一维数组:
(1).一维数组的创建
- 概念:
type_t arr_name [const_n]; //type_t 是指数组的元素类型 //arr_name 是数组的名字 //const_n 是一个常量表达式,用来指定数组的大小
- 数组创建实例:
#include<stdio.h> int main() { //代码1 int arr1[10]; char arr2[10]; float arr3[1]; double arr4[20]; //代码2 //用宏定义的方式 #define X 3 int arr5[X]; //代码3 //错误使用 //下面的代码只能支持在C99标准的编译器上编译 int count = 10; int arr6[count];//这种数组是不能初始化的 //在C99标准之前,数组的大小必须是常量或者常量表达式 //在C99之后,数组的大小可以是变量,为了支持变长数组 return 0; }
注:在C99标准之前,数组的大小必须是常量或者常量表达式
在C99之后,数组的大小可以是变量,为了支持变长数组
(2).数组的初始化:
- 概念:数组的初始化是指,在创建数组的同时给数组的一些内容一些合理的初始值(初始化)。
- 数组是具有相同类型的集合,数组的大小(即所占字节数)由元素个数乘以单个元素的大小。
- 数组只能够整体初始化,不能被整体赋值。只能使用循环从第一个逐个遍历赋值。
- 初始化时,数组的维度或元素个数可忽略 ,编译器会根据花括号中元素个数初始化数组元素的个数。
- 当花括号中用于初始化值的个数不足数组元素大小时,数组剩下的元素依次用0初始化。
字符型数组在计算机内部用的时对应的ascii码值进行存储的。 - 一般用”“引起的字符串,不用数组保存时,一般都被直接编译到字符常量区,并且不可被修改。
(3).一维数组的使用:
- 实例;
#include <stdio.h> int main() { int arr[10] = { 0 };//数组的不完全初始化 //计算数组的元素个数 int sz = sizeof(arr) / sizeof(arr[0]); //对数组内容赋值,数组是使用下标来访问的,下标从0开始。所以: int i = 0;//做下标,此时可以是变量 for (i = 0; i < 10; i++) { arr[i] = i; } //输出数组的内容 for (i = 0; i < 10; ++i) { printf("%d ", arr[i]); } return 0; }
- 注意: sizeof()操作符用于取长度,以字节为单位。sizeof(数组名)即求的时整个数组的大小。sizeof(首元素)即求数组单个元素大小。 用0下标,是因为数组至少存在一个有效元素,所以0下标永远存在。数组是使用下标来访问的,下标是从0开始。 数组的大小可以通过计算得到。建议采用sizeof(arr)/sizeof(arr[0])这种方式。
(4).一维数组在内存中的储存:
- 数组在内存中开辟是线性连续且递增的。
- 在c语言中,任何变量(基本变量,指针变量,结构体变量,数组变量)的空间都是整体开辟,但任何元素的起始地址一定是开辟字节当中最小的。
二.二维数组:
(1).二维数组的创建
二维数组创建时,行数可以忽略不写。并且所有维度的数组其第一个方括号的内容可忽略。
#include<stdio.h>
int main()
{
//数组创建
int arr1[3][4];//[行数][列数]
char arr2[4][5];
double arr3[2][4];
return 0;
}
(2).二维数组的初始化
//数组初始化
int arr[3][4] = {1,2,3,4};
int arr[3][4] = {{1,2},{4,5}};
int arr[][4] = {{2,3},{4,5}};
注意:
花括号中的一个花括号代表一个一维数组的初始化。当里面无花括号分组时,按照顺序从第一个开始逐个进行初始化。余下的未赋值的元素用0初始化。
(3).二维数组的使用
- 二维数组的使用也是通过下标的方式,用双重循环嵌套进行索引使用。
- 二维数组中数的查找:
#include<stdio.h> int main() { int arr[3][3] = { 1,2,3,4,5,6,7,8,9 }; printf("%d", arr[1][2]); //二维数组中数下标是这样规定的 //00 01 02 //10 11 12 //20 21 22 //如此输出便是6 return 0; }
- 二维数组中打印整个数组:
#include<stdio.h> int main() { int arr[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 }; int i = 0; for (i = 0; i < 3; i++) { int j = 0; for (j = 0; j < 4; j++) { printf("%d ", arr[i][j]); } printf("\n"); } return 0;
- 二维数组中重新为数组赋值:
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> int main() { int arr[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 }; int i = 0; for (i = 0; i < 3; i++) { int j = 0; for (j = 0; j < 4; j++) { scanf("%d ", &arr[i][j]); } } for (i = 0; i < 3; i++) { int j = 0; for (j = 0; j < 4; j++) { printf("%d ", arr[i][j]); } printf("\n"); } return 0; }
(4).二维数组在内存中的储存
- 二维数组在内存的空间布局上,也是线性连续且递增的!!!
- 二维数组本质上也是一维数组,只不过内部元素放的是一维数组
.
三.数组越界:
- 数组的下标是有范围限制的。
- 数组的下规定是从0开始的,如果数组有n个元素,最后一个元素的下标就是n-1。所以数组的下标如果小于0,或者大于n-1,就是数组越界访问了,超出了数组合法空间的访问。
- C语言本身是不做数组下标的越界检查,编译器也不一定报错,但是编译器不报错,并不意味着程序就是正确的,所以程序员写代码时,最好自己做越界的检查。
- 二维数组的行和列也可能存在越界:
四.数组作为函数参数:
- 调用函数传参数组时,减少函数传数组时的成本问题(时间和空间)。因为传参时,需要临时拷贝,如果数组过大,可能会浪费资源,严重的话可能栈溢出。
- 数组元素降维成指向数组内部元素类型的指针。
- 对指针加一,加上所指向的类型的大小。
例题:往往我们在写代码的时候,会将数组作为参数传整个函数,比如我要实现一个冒泡排序函数,将一个整型数组排序。
代码如下:
#include<stdio.h>
//形参是数组的形式;
void popo(int arr[], int n)
//数组名本质上是数组首元素的地址,地址是应该使用指针来接收
//所以arr[]看似是数组,本质上是指针变量
{
//趟数
int i = 0;
for (i = 0; i < n - 1; i++)
{
//一趟冒泡排序
int j = 0;
for (j = 0; j < n - 1 - i; j++)
{
if (arr[j] > arr[j + 1])
{
//交换
int x = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = x;
}
}
}
}
int main()
{
int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
int n = sizeof(arr) / sizeof(arr[0]);
popo(arr,n);
int i = 0;
for (i = 0; i < n ; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
总结:
- 形参格式,例如
int arr[ ]
或者int *arr
,两者等价 - 形参元素个数可被忽略,并且建议忽略(有可能改变了实参的大小,这样比较方便)。或者也可以填写比实参元素个数大的值。
- 用
sizeof()
求数组元素个数时,尽量在数组定义时求。因为传参后数组会降维成指针。
五.数组名:
(1).一维数组数组名:
数组名确实能表示首元素的地址,但是有两个例外:
- sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小,单位是字节
- &数组名,这里的数组名表示整个数组,取出的是整个数组的地址
(2).二维数组数组名:
二维数组的数组名也表示数组首元素的地址(这里的首元素是指第一行元素地址)