上周我们主要学习了数组和指针两大块重要的 知识点,下面让我们一起来研究一下吧~
学习数组首先我们要掌握的是:1.数组的定义和数组的初始化 2.数组下标的使用 3.数组名的含义
数组基本概念
概念:
由相同类型的多个元素所组成的一种复合数据类型
在工程中同时定义多个相同类型的变量时,重复定义,可以使用数组
逻辑:
一次性定义多个相同的变量,并存储到一片连续的内存中
格式:
类型说明符 数组名[整型常量表达式];
类型说明符:指定数组元素的数据类型,任意c语言合法类型都可以
数组名 : c语言标识符,其值为该数组的首地址(常量)
整型常量表达式:指定数组元素的个数
语法释义:
a是数组名,即这片连续内存的名称
[5]代表这片连续内存总共分成5个相等的格子,每个格子称为数组的元素
int代表每个元素的类型,可以是任意基本类型,也可以是组合类型,甚至可以是数组
数组中所有元素的类型都是一致
数组申请的空间是连续的,从低地址到高地址依次连续存放数组中的每个元素
数组定义
初始化:
在定义的时候赋值,称为初始化
数组元素的引用
存储模式:
一片连续的内存,按数据类型分割成若干相同大小的格子
元素下标:
数组开头位置的偏移量
字符数组
概念:
专门存放字符的数组,称为字符数组
初始化与元素引用:
char s1[5] = {'a', 'b', 'c', 'd', 'e'}; // s1存放的是字符序列,非字符串
char s2[6] = {'a', 'b', 'c', 'd', 'e', '\0'}; // s2存放了一个字符串char s[6] = {"abcde"}; // 使用字符串直接初始化字符数组
char s[6] = "abcde" ; // 大括号可以省略s[0] = 'A'; // 索引第一个元素,赋值为 'A'
数组偏移量
int Array[6] = {10,20,30,40,50,60};
// 数组地址偏移量
// 000000000061FE04,000000000061FE04
printf("%p,%p",&Array[1],&Array[0]+1);
数组元素地址解引用
通过对数组元素地址解引用,可以获取地址空间里面的数据
int a = 10;
printf("%d\n",a);printf("%p\n",&a);
// * 表示将地址里面的内容取出,我们把它称为解引用
printf("%d\n",*(&a));//--------------------------
char Array[5] = {'j','a','c','k'};
printf("%c\n",Array[0]);
printf("%c,%c\n",*(Array+0),*(&Array[0]));
printf("%c,%c\n",*(Array+1),*(&Array[1]));
printf("%c,%c\n",*(Array+2),*(&Array[2]));
printf("%c,%c\n",*(Array+3),*(&Array[3]));
字符串常量
- 字符串常量在内存中的存储,实质是一个匿名数组
- 匿名数组,同样满足数组两种含义的规定
多维数组(二维数组)
- 概念:若数组元素类型也是数组,则称该数组为多维数组,就是一维数组的集合
- 二维数组在内存中的存放:先放第一行,再放第二行
- 多维数组的语法跟普通的一维数组语法完全一致
- 初始化:
int a[2][3] = {{1,2,3}, {4,5,6}}; // 数组的元素是另一个数组
int a[2][3] = {{1,2,3}, {4,5,6}, {7,8,9}}; // 错误,越界了
int a[2][3] = {{1,2,3}, {4,5,6,7}}; // 错误,越界了int a[ ][3] = {{1,2,3}, {4,5,6}}; // OK,自动根据初始化列表分配数组元素个数
int a[2][3] = {{1,2,3}}; // OK,只初始化数组元素的一部分
- 元素引用:
// a[0] 代表第一个元素,这个元素是一个具有 3 个元素的数组:{1,2,3}
// a[1] 代表第二个元素,这个元素也是一个具有 3 个元素的数组:{4,5,6}printf("%d", a[0][0]); // 输出第一个数组的第一个元素,即1
printf("%d", a[1][2]); // 输出第二个数组的第三个元素,即6
//1、定义
//int arr[3][4];
//2、定义的时候初始化
//1)分行给二维数组初始化
/* int arr[3][4] = { {10,20,30,33},
{40,50,60,66},
{70,80,90,99}}; */
//2)将所有的数据全部写在一个大括号里面,按照数组的排列顺序进行赋值
//int arr[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
//int arr[3][4] = {{1,2,3,4},{5},6,7};//3)清空数组
//int arr[3][4] = {0};
//4)如果对全部元素进行赋值,则定义数组的时候可以省略第一维的长度
//int arr[][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
//int arr[][] = {1,2,3,4,5,6,7,8,9,10,11,12}; 错误
//int arr[3][] = {1,2,3,4,5,6,7,8,9,10,11,12}; 错误
#include <stdio.h>
int main(int argc, char const *argv[])
{ // 只有一个[]的数组称为一维数组
char name[5] = "jack";
char name1[5] = "rose";
char name2[5] = "ken";
// 有两个[]的数组称为二维数组
// 二维数组初始化
// 第一个[3]表示字符串的个数,表示这个数组能存放3个字符串
// 第二个[5]表示每个字符串最大的长度为5字节
char Name[3][5] = {"jack","rose","ken"};
char Name1[3][5] = {{"jack"},{"rose"},{"ken"}};
char Name2[3][5] = {{'j','a','c','k','\0'},{"rose"},{"ken"}};
printf("%c\n",Name[0][0]);
printf("%c\n",Name[0][1]);
printf("%c\n",Name[1][1]);
printf("%c\n",Name[2][2]);// 初始化整数二维数组
int nums[2][3] = {{1,2,3},{7,8,9}};
printf("%d\n",nums[1][2]);
int nums1[][3] = {{1,2,3},{7,8,9}};
int num[] = {1,2,3};
// 如果值初始化一部分,后面的所有空间都会初始化为0
int nums2[2][3] = {{1,2,3}};
printf("%d\n",nums2[1][1]);// 将num1里面的所有空间初始化为0
int num1[5] = {0};
printf("%d\n",num1[3]);// 二维数组定义
int nums3[2][3];
nums3[0][0] = 1;
nums3[0][1] = 2;
int i = 1;
for(int row = 0; row < 2; row++)
{
for(int col = 0; col < 3; col++)
{
nums3[row][col] = i;
i++;
}
}
for(int row = 0; row < 2; row++)
{
for(int col = 0; col < 3; col++)
{
printf("%d\t",nums3[row][col]);
}
printf("\n");
}
return 0;
}
二维数组解引用
// 二维数组初始化字符串
char buf1[2][5] = {"jack","rose"};
printf("%s,%s\n",&buf1[0][0],buf[0]);
printf("%s,%s\n",&buf1[1][0],buf[1]);// 取二维数组中的某个字符
printf("%c,%c,%c,%c\n",buf1[0][1],*(&buf1[0][1]),*(buf1[0]+1),*(*(buf1+0)+1));
printf("%c,%c,%c,%c\n",buf1[1][2],*(&buf1[1][2]),*(buf1[1]+2),*(*(buf1+1)+2));
数组地址偏移
#include <stdio.h>
int main()
{
char Names[3][5] = {"jack","rose","ken"};
// printf("Names[0] %p,%p\n",Names[0],&Names[0][0]);
// printf("Names[1] %p,%p\n",Names[1],&Names[1][0]);
// printf("Names[2] %p,%p\n",Names[2],&Names[2][0]);
// Names[0]相当于&Names[0][0]
printf("Names[0]+1=%p,%p\n",Names[0]+1,&Names[0][1]);
printf("Names[0][0]:%c,%c\n",Names[0][0],*(Names[0]+0));
printf("Names[0][2]:%c,%c\n",Names[0][2],*(Names[0]+2));printf("&Names[0]+1=%p,%p\n",&Names[0]+1,&Names[1]);
printf("*(&Names[0]+1)=%c,%c\n",*(&Names[0]+1)[0],*(&Names[1])[0]);
printf("*(&Names[0]+1)=%c,%c,%c,%c\n",(*(&Names[0]+1))[1],(*(&Names[1]))[1],
*(*(&Names[0]+1)+1),*(*(Names+1)+1));
printf("%c,%c,%c,%c\n",Names[2][2],*(Names[2]+2),(*(Names+2))[2],*((*(Names+2)+2)));return 0;
}
数组万能拆解法
任意的数组,不管有多复杂,其定义都由两部分组成:
第一部分:说明元素的类型,可以是任意的类型
第二部分 :说明数组名和元素个数
数组名涵义
-
数组名有两个含义:
-
第一含义是:整个数组
-
第二含义是:首元素地址
-
-
当出现以下情形时,那么数组名就代表整个数组:
-
在数组定义中
-
在 sizeof 运算表达式中
-
在取址符&中
-
-
其他任何情形下,那么数组名就代表首元素地址。即:此时数组名就是一个指向首元素的指针。
-
示例:
int a[3]; // 此处,a 代表整个数组 printf("%d\n", sizeof(a)); // 此处,a 代表整个数组 printf("%p\n", &a); // 此处,a 代表整个数组,此处为整个数组的地址 int *p = a; // 此处,a 代表首元素 a[0] 的地址,等价于 &a[0] p = a + 1; // 此处,a 代表首元素 a[0] 的地址,等价于 &a[0] function(a); // 此处,a 代表首元素 a[0] 的地址,等价于 &a[0] scanf("%d\n", a); // 此处,a 代表首元素 a[0] 的地址,等价 &a[0]
语言只有在第一含义的场合下表现为数组,其他大部分场合都表现为首元素的地址,当数组表现为首元素地址时,实际上就是一个指向其首元素的指针。数组运算实际上就是指针运算。
数组下标
-
数组下标实际上是编译系统的一种简写,其等价形式是:
a[i] = 100; 等价于 *(a+i) = 100;
-
根据加法交换律,以下的所有的语句均是等价的:
a[i] = 100; *(a+i) = 100; *(i+a) = 100; i[a] = 100;
-
数组运算,等价于指针运算。
字符串常量
-
字符串常量在内存中的存储,实质是一个匿名数组
-
匿名数组,同样满足数组两种涵义的规定
-
示例:
printf("%d\n", sizeof("abcd")); // 此处 "abcd" 代表整个数组 printf("%p\n", &"abcd"); // 此处 "abcd" 代表整个数组 printf("%c\n", "abcd"[1]); // 此处 "abcd" 代表匿名数组的首元素地址 char *p1 = "abcd"; // 此处 "abcd" 代表匿名数组的首元素地址 char *p2 = "abcd" + 1; // 此处 "abcd" 代表匿名数组的首元素地址
零长数组(预习:结构体)
-
概念:长度为0的数组,比如 int data[0];
-
用途:放在结构体的末尾,作为可变长度数据的入口
-
示例:
struct node { /* 结构体的其他成员 */ // 成员1 // 成员2 // ... ... int len; char *data[0]; }; // 给结构体额外分配 10 个字节的内存。 struct node *p = malloc(sizeof(struct node) + 10); p->len = 10; // 额外分配的内存可以通过 data 来使用 p->data[0] ~ p->data[9]
变长数组
-
概念:定义时,使用变量作为元素个数的数组。
-
要点:变长数组仅仅指元素个数在定义时是变量,而绝非指数组的长度可长可短。实际上,不管是普通数组还是所谓的变长数组,数组一旦定义完毕,其长度则不可改变。
-
示例:
int len = 15; int a[len]; // 数组元素个数 len 是变量,因此数组 a 是变长数组 int x = 2; int y = 3; int b[x][y]; // 数组元素个数 x、y 是变量,因此数组 b 是变长数组 int b[2][y]; // 数组元素个数 y 是变量,因此数组 b 是变长数组 int b[x][3]; // 数组元素个数 x 是变量,因此数组 b 是变长数组
-
语法:变长数组不可初始化,即以下代码是错误的:
int len = 5; int a[len] = {1,2,3,4,5}; // 数组 a 不可初始化
int len = 10; // 编译的时候不能确认len的值,并且申请空间的同时直接赋值,所以不能初始化 //int buf[len] = {10}; // 定义的时候没有使用此空间,所以不会报错 int buf[len]; buf[1] = 10; printf("%d\n",buf[1]);