1.基本概念
逻辑: 一次性定义多个相同类型的变量,并且给他分配一片连续的内存
语法:int arr [5] ;
初始化:
只有在定义的时候赋值, 才可以称为初始化。数组只有在初始化的时候才可以统一赋值。
int arr [5] = {1,2,3,4,5} ; // 定义并初始化数组
int arr [5] = {1,2,3} ; //可以, 不完全初始化
int arr [5] = {1,2,3,4,5,6,7,8,9} ; // 错误(但是可以用) , 越界初始化, 越界部分将会被编译器舍弃
int arr [ ] = {1,2,3,4,5,6,7,8,9} ; // 可以, 用户没有直接给定数组大小,
// 但是有初始化, 因此数组的大小会在初始化时确定, 大小为 9
int arr [ ] ; // 错误的, 没有给定大小也没有初始化, 因此数组的内存大小无法确定系统无法分配
注意:
数组在定义的时候必须确定他的大小。说白了就是中括号中[] 必须有数组的大小,如果没有就必须初始化。
数组元素引用:
存储模式:一片连续的内存,按照数据的类型进行分割成若干个大小相同的格子
元素的下标与偏移量:以数组开头为基础的偏移的量(数据类型大小)
int arr [5] = {1,2,3,4,5} ; // 定义并初始化数组
printf("arr[0]:%d\n" , arr[0]);
arr[0] = 99 ; // 把数组的第1个元素( 偏移量为0 )修改为 99
printf("arr[0]:%d\n" , arr[0]);
arr[5] = 250 ; //"错误" 越界访问, 并很有可能造成非法访问
printf("arr[5]:%d\n" , arr[5]);
arr = {9,8,7,6,5,4}; // 整体赋值只允许在初始化中
arr = 100 ; // 错误
printf("sizeof(arr):%ld\n" , sizeof(arr));
int len = sizeof(arr) / sizeof(int) ; // 求数组元素的个数
for (size_t i = 0; i < len ; i++)
{
printf("arr[%ld]:%d\n" , i , arr[i]);
}
2.字符数组
概念: 专门用来存放字符类型数据的数组, 称为字符数组
初始化+引用:
char ch1 [5] = {'H','e','l','l','o'} ; // 定义一个字符类型的数组并把'H','e','l','l','o' 一个一个存进去
char ch2 [6] = {"Hello"} ; // 定义一个字符型的数组, 并把 "Hello" 字符串存放到数组中 ,因此该数组为字符串数组
char ch3 [6] = "Hello" ; // 与ch2 一样, 大括号可以省略
ch3[1] = 'E' ; // 可以, 把数组中第二个元素‘e’修改为‘E’
ch3 = "Even" ; // 不可以, 只有在初始化的时候才能整体赋值
printf("%s\n" , ch1); // 在访问ch1的时候并没有发现结束符,因此很有可能会把ch2的内容一并输出
注意:
ch1 为字符数组, 它没有结束符,因此在打印输出的时候应该避免使用 %s 进行输出, 有可能会 造成越界访问。
3.数组名的含义
表示整个数组的首地址:
①在数组定义的时候
②在使用sizeof 运算符中 数组名表示整个数组的首地址(求得整个数组的大小)
③在取地址符 中 &arr , arr 表示整个数组
表示整个数组首元素的首地址:
①其它情况
int arr[10] ; // arr 表示整个数组的首地址
int len = sizeof arr ; // arr 表示整个数组的首地址 , sizeof 运算符后的括号可以省略
int (* p) [10] = &arr; //arr 表示整个数组的首地址
int *p1 = arr ; // arr 表示数组的首元素的首地址
字符串常量:
字符串常量是一个被存放在常量区的字符串,实际上也可称为一个匿名数组。
匿名数组,同样满足数组名的含义。
char * msg2 = "Hello Even" ;// "Hello Even" 字符串常量首元素的首地址
char * msg1 = "Hello Even"+1 ;
printf("%s\n", "Hello Even" ) ;// "Hello Even" 字符串常量首元素的首地址
printf("%s\n", &"Hello Even" ) ; // "Hello Even" 字符串常量的整个数组的地址
printf("%c\n", "Hello Even"[6] ) ; // "Hello Even" 字符串常量首元素的首地址 [6]
//[6] 相当于+6个单位(char) 得到 ‘E’
4.数组下标
//组的下标实际上只是编译器提供一种简写,实际上如下:
int a [100] ;
a[10] = 250 ; ====> *(a+10) = 250 ;
// 通过加法交换律,有如下结果:
a[10] = 250 ;
*(a+10) = 250 ;
*(10+a) = 250 ;
10[a] = 250 ;
5.零长数组
概念: 数组的长度为0 , char arr [0] ;
用途:一般会放在结构体的末尾, 作为可变长度的入口。(数组是唯一一个允许越界访问的载体)
struct node
{
int a;
char b;
float c;
..
...
int len;
char arr[0];
}
struct node *p = malloc(sizeof(struct node) + 20 ); // + 20 就是在原有的基础上增加20字节
p->len = 20 ; // 设置额外增长的长度为 20
strncpy(p->arr , 20 , "Hello FZ2105");
p->arr
6.变长数组
概念: 定义是, 使用一个变量作为数组的长度(元素的个数)。
重点: 变长数组并不是说在任意时候他的长度可以随意变化, 实际上只是在定义之前数组的长度是未知的有一个变量来决定, 但是定义语句过后变长数组的长度由定义那一刻变量的大小来决定。
int a = 200 ; // a 作为一个普通的变量 , 200 则可以作为arr 的长度
a = 99 ; // 99 可以作为 arr 的长度
int arr[a]; // a 当前是 99 , 因此数组arr 的长度已经确定为 99
//从此以后该数组的长度已经确定为99 不会再变换
a = 10 ; // a = 10 并不会影响数组的长度
注意:
①因为数组的长度未确定, 因此它不允许初始化。
②在使用的时候可以通过该变长数组来有限的节省内存空间。
7.多维数组
概念: 数组内部的成员也是数组
// 定义与初始化:
int arr[2][3] = { {1,2,3} , { 4,5,6} };
int arr1[2][3] = { 1,2,3,4,5,6};
//如何引用:
arr[0][0] = 100 ; // 数组:(通过下标来访问)
*(*(arr+0)+0) = 100 ; // 通过指针偏移来访问
实例:
int arr[2][3] = { {1,2,3} , { 4,5,6} };
int arr1[2][3] = { 1,2,3,4,5,6};
int *p = arr ; // p指向数组arr 的首元素
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < 3; j++)
{
printf("arr[%d][%d]:%d\t" ,i ,j , arr[i][j] );
}
}
printf("\n");
for (int i = 0; i < 6 ; i++)
{ // *arr 得到元素1 的地址 + 1 则是加一个 int 类型
printf("*(*(arr+%d)):%d\t" , i,*(*(arr)+i) );
}
printf("\n");
for (int i = 0; i < 6 ; i++)
{ // arr 指的是首元素的首地址 {1,2,3} 的首地址 + 1则 + 3个整型
printf("*(*(arr+%d)):%d\t" , i,*(*(arr+i)) );
}
printf("\n");
for (int i = 0; i < 6 ; i++)
{ // p 只是一个普通的整型指针, 与二维数组没有任何的关系
printf("*(p+%d):%d\n" , i ,*(p+i));
}