1.数组初阶
掌握数组需求:
1.数组的定义和数组的初始化
2.数组下标的使用
3.数组名的含义
基本概念
-
数组的概念
- 由相同类型的多个元素所组成的一种复合数据类型
- 在工程中同时定义多个相同类型的变量时,重复定义,可以使用数组
-
逻辑:一次性定义多个相同的变量,并存储到一片连续的内存中
-
格式
- 类型说明符 数组名[整型常量表达式];
- 类型说明符:指定数组元素的数据类型,任意c语言合法类型都可以
- 数组名 : c语言标识符,其值为该数组的首地址(常量)
#include <stdio.h> int main(int argc, char const *argv[]) { char a[5]; printf("a size is %d\n",sizeof(a)); // 输出a的地址,数组名相当于这个数组的首元素地址 printf("a addr : %p\n",a); // 输出a的首元素地址 printf("a[0] addr : %p\n",&a[0]); printf("a[1] addr : %p\n",&a[1]); // 数组的下标地址与首地址的偏移量有关,与数组的大小无关 // 比如,int a[3] = a的基地址+偏移量2 printf("a[2] addr : %p,%p,%p\n",&a[2],a+2,&a[0]+2); // 输出变量b的地址 int b = 10; printf("%p\n",&b); return 0; }
- 整型常量表达式:指定数组元素的个数
-
示例:
int a[5];//定义一个数组a,该数组一个由五个元素,每个元素都是int类型
int size = 4;
int array[size];//变长数组,如果式局部变量则正确,全局变量编译出错
- 语法释义:
- a是数组名,即这片连续内存的名称
- [5]代表这片连续内存总共分成5个相等的格子,每个格子称为数组的元素
- int代表每个元素的类型,可以是任意基本类型,也可以是组合类型,甚至可以是数组
- 数组中所有元素的类型都是一致
- 数组申请的空间是连续的,从低地址到高地址依次连续存放数组中的每个元素
- 数组定义
#include <stdio.h>
int main(int argc, char const *argv[])
{
// 申请5块连续的空间,将a称为数组
int a[5];
// 将这5块空间逐一赋值,注意,数组下标从0开始
a[0] = 1;
a[1] = 20;
a[2] = 30;
a[3] = 40;
a[4] = 50;
//a[5] = 60; // 越界,无法使用
printf("%d\n",a[4]);
// 循环给数组a赋值
for(int data = 10,i = 0; i < 5; i++,data+=10)
{
a[i] = data;
}
// 遍历输出
for(int i = 0; i < 5; i++)
{
printf("%d\n",a[i]);
}
return 0;
}
- 初始化:在定义的时候赋值,称为初始化
// 正常初始化
int a[5] = {100,200,300,400,500};
int a[5] = {100,200,300,400,500,600}; // 错误,越界了
int a[ ] = {100,200,300}; // OK,自动根据初始化列表分配数组元素个数
int a[5] = {100,200,300}; // OK,只初始化数组元素的一部分
// 不能在使用变长数组的情况下初始化数组
int a[size] = {1,2,3};//编译出错
// 变长数组只能先定义再使用
int a[size]; // 正确的
a[0] = 10;
数组的真实存储
#include <stdio.h>
int main(int argc, char const *argv[])
{
// 定义数组并初始化
int Array[5] = {10,25,31,48,60};
printf("%d\n",Array[2]);
// 初始化的时候确定数组空间大小
int Array0[] = {10,20,39};
printf("%d\n",Array0[2]);
// 初始化一部分空间,剩余的空间默认初始化为0
int Array1[10] = {68,70};
// 定义数组并清空数组
int Array2[10] = {0};
// 变长数组
int len = 3;
// 错误,初始化的时候,数组大小必须为常量
//int Array3[len] = {100,200,300};
// 哪怕不确定len的大小,但是可以确定Array3的空间最小值为一个
// int类型的空间,所以至少可以存放一个int类型的数据
int Array3[len];
Array3[0] = 100;
printf("Array3[0] = %d\n",Array3[0]);
return 0;
}
- 如果是定义的时候初始化,但是初始化列表个数小于数组的大小,那么无论这个数组是全局变量还是局部变量,没有赋值的成员都是0
int array[3] = {100,200,300,400};// 错误,越界访问没有申请到的空间
int array[3];// 局部变量为随机数,全局变量为0
int array[3] = {0};//清空数组
测量数组的总大小:sizeof(array)
测量数组元素个数:sizeof(array)/sizeof(array[0])
#include <stdio.h>
int main(int argc, char const *argv[])
{
// 根据初始化的时候分配空间从而确定数组的大小
int Array[] = {10,20};
printf("%d\n",sizeof(Array));
// 计算数组的元素个数
int count = sizeof(Array) / sizeof(Array[0]);
printf("数组空间个数: %d\n",count);
return 0;
}
数组元素的引用
-
存储模式:一片连续的内存,按数据类型分割成若干相同大小的格子
-
元素下标:数组开头位置的偏移量
元素下标偏移量
数组名[下标]
"下标":C语言的下标是从0开始,下标必须是>=0的整数
a[0]、a[1]、a[n]
引用数组元素a[i]和普通变量一样,既可以作为左值,也可以作为右值
下标最小值为0,最大值为 元素个数 -1
- 示例
int a[5]; // 有效的下标范围是 0 ~ 4
a[0] = 1;
a[1] = 66;
a[2] = 21;
a[3] = 4;
a[4] = 934;
a[5] = 62; // 错误,越界了
a = 10; // 错误,不可对数组名赋值
a是数组的名字,代表最大元素类型的首地址
demo2
#include <stdio.h>
#include <string.h>
int main(int argc, char const *argv[])
{
// "jack"
// 定义数组
char str[5];
// 通过下标访问,下标从0开始
str[0] = 'j';
str[1] = 'a';
str[2] = 'c';
str[3] = 'k';
str[4] = '\0';
// 输出
for(int i = 0; i < 5; i++)
printf("%c",str[i]);
printf("\n");
// 数组名相当于这个数组的首元素地址
printf("%s\n",str);
printf("%s\n",&str[0]);
// str地址
printf("str addr = %p\n",str);
printf("str[0] addr = %p\n",&str[0]);
printf("str[1] addr = %p\n",&str[1]);
printf("str[2] addr = %p\n",&str[2]);
// 数组初始化效率比先定义后赋值高
char str1[5] = {'j','a','c','k','\0'};
char str2[] = {'j','a','c','k','\0'};
char str3[5] = {"jack"};
char str4[5] = "jack";
char str5[128];
printf("用户输入: ");
// 缺点是遇到空格结束,后面的内容无法获取
//scanf("%s",str5);
// 从键盘获取到字符串放到str5,空格也能读取
gets(str5);
printf("%s\n",str5);
// 给字符串赋值
// 因为str5本身起始就是一个地址常量,不可修改
// 所以不能给常量赋值
// str5 = "rose"; //等价于 100 = 200;
// 字符串拷贝
strcpy(str5,"rose");
printf("%s\n",str5);
return 0;
}
#include <stdio.h>
int main()
{
int size = 4;
//1)多个元素具有相同的数据类型,可以用一个集合来表示,数组
//int val1,val2,val3,val4;
//int arr[size];
//2)数组的初始化
//int arr[4] = {10,20,30,40};
//3)清空数组
int arr[4] = {0}; //数组中 arr[0] == 0,后面没有赋值的默认都是0
//数组元素的引用,通过数组的名字 + 下标 arr[0] 下标一定是大于等于0,下标最大值等于 元素的个数 -1
//arr[0] ---》val1
//arr[1] ---》val2
//arr[2] ---》val3
//arr[3] ---》val4
//arr[4] --内存访问越界,此时程序不会给你报错
for(int i=0; i<4; i++)
{
printf("arr[%d] addr:%p value:%d\n",i,&arr[i],arr[i]); //arr[0]
}
printf("&arr[0] :%p\n",&arr[0]);
printf("&arr[0]+1 :%p\n",&arr[0]+1);
return 0 ;
}
练习1
#include <stdio.h>
int main(int argc, char const *argv[])
{
char ch[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
// 输入
printf("请输入十进制数: ");
int data;
scanf("%d",&data);
// 存放取余后的结果
int index[16] = {0}; // 清空缓冲区
// 记录取数个数
int count = 0;
while(1)
{
index[count++] = data % 16;
data /= 16;
if(data == 0)
break;
}
for(int i = count-1; i >= 0; i--)
{
printf("%c",ch[index[i]]);
}
printf("\n");
return 0;
}
字符数组
- 概念:专门存放字符的数组,称为字符数组
- 初始化与元素引用:
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'
#include <stdio.h>
int main(int argc, char const *argv[])
{
char Array[6] = {'D','A','V','I','D','\0'};
printf("%c\n",Array[3]); // 'k'
for(int i = 0; i < 6; i++)
{
printf("%c",Array[i]);
}
printf("\n");
// 输出字符串用%s
printf("%s\n",&Array[0]);
printf("%s\n",Array);
char Array1[6] = {"DAVID"};// ""表示字符串,字符串末尾有\0
printf("%s\n",Array1);
// 最常用的方法
char Array2[6] = "DAVID";
printf("%s\n",Array2);
return 0;
}
练习: 定义一个char str[6] = {‘h’,‘e’,‘l’,‘l’,‘o’}; //把这个数组的内存图画出来,并且把数组的名字的地址,还有每个元素的地址都打印出来,每个元素的地址相差几个字节?
#include <stdio.h>
int main(int argc, char const *argv[])
{
char str[6] = {'h','e','l','l','o','\0'};
// 数组地址+1是偏移一个对象的大小,比如char地址+1表示偏移1字节
printf("%p,%p\n",str,&str[0]);
printf("%p,%p\n",str+1,&str[1]);
printf("%p,%p\n",str+2,&str[2]);
// 对地址进行解引用,起始就是访问地址里面的内容
printf("%c,%c\n",*(str+0),str[0]);
printf("%c,%c\n",*(str+1),str[1]);
printf("%c,%c\n",*(str+2),str[2]);
// str是地址,类型与格式输出不匹配
//printf("%c\n",str);
return 0;
}
练习1:定义一个整型数组,遍历数组中的每个元素,并把数组中的每个元素的地址都打印出来
// 数组地址+1是偏移一个对象的大小,比如int地址+1表示偏移4字节
int array[5] = {10,20,30,40,50};
printf("%p,%p\n",array,&array[0]);
printf("%p,%p\n",array+1,&array[1]);
printf("%p,%p\n",array+2,&array[2]);
// 对地址进行解引用,起始就是访问地址里面的内容
printf("%d,%d\n",*(array+0),array[0]);
printf("%d,%d\n",*(array+1),array[1]);
printf("%d,%d\n",*(array+2),array[2]);
数组偏移量
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]));
练习2:定义一个整型数组,存储从键盘上获取的多个整型数据(比如输入 10 40 23 13),输入-1输入完成,并打印出这一排数据的最小值和最大值
#include <stdio.h>
int main(int argc, char const *argv[])
{
int array[100] = {0};
// count为数组下标
int count = 0;
while(1)
{
scanf("%d",&array[count]);
if(array[count] == -1)
break;
count++;
}
// 将-1从数组中剔除
array[count] = 0;
// 假设array[0]是最大最小值
int max_value = array[0];
int min_value = array[0];
// 遍历,如果后面的内容比max大,或者内容比min小则替换
for(int i = 0; i < count; i++)
{
if(max_value < array[i])
{
max_value = array[i];
}
if(min_value > array[i])
{
min_value = array[i];
}
}
printf("max_value = %d\n",max_value);
printf("min_value = %d\n",min_value);
return 0;
}
字符串常量
- 字符串常量在内存中的存储,实质是一个匿名数组
- 匿名数组,同样满足数组两种涵义的规定
- 示例:
printf("%s\n","hello");
printf("'h' addr:%p\n","hello");
printf("'e' addr:%p\n","hello" + 1); //地址+1
printf("'o' addr:%p value:%c\n","hello" + 4,*("hello" + 4)); //地址+1
printf("%d\n", sizeof("abcd")); // 此处 "abcd" 代表整个数组
printf("%p\n", &"abcd"); // 此处 "abcd" 代表整个数组
printf("%c\n", "abcd"[1]); // 此处 "abcd" 代表匿名数组的首元素地址
#include <stdio.h>
int main(int argc, char const *argv[])
{
// "abc"是字符串"abc"的数组名也是首元素地址
printf("%s\n","abc");
printf("%p\n","abc");
// 0["abc"]可读性比较差,用的比较少
printf("%c,%c,%c\n","abc"[0],*("abc"+0), 0["abc"]);
printf("%c,%c,%c\n","abc"[1],*("abc"+1), 1["abc"]);
printf("%c,%c,%c\n","abc"[2],*("abc"+2), 2["abc"]);
char array[] = "jack";
printf("%c,%c\n",*(array+1),array[1]);
printf("%c,%c\n",*(1+array),1[array]);
return 0;
}
多维数组
- 概念:若数组元素类型也是数组,则该数组称为多维数组,就是一维数组的集合
- 示例:
int a1[3];
int a2[3];
int a[2][3];//a[0]-->a1 a[1]-->a2
第一种解释:
定义一个二维数组。该数组是由2个一维数组组成,分别是a[0] a[1]
每个一维数组由3个元素组成,所以二维数组有6个元素
数据类型 二维数组的名字[有多少个一维数组][每个一维数组有多少个元素]
// 代码释义:
// 1, a[2] 是数组的定义,表示该数组拥有两个元素
// 2, int [3]是元素的类型,表示该数组元素是一个具有三个元素的整型数组
第二种解释:
该数组一共有2行,每行由3个元素组成(2行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}; 错误
二维数组解引用
// 二维数组初始化字符串
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));
数组地址偏移
练习1 : 定义二维数组,int buf[2] [3];将二维数组中每个元素的值和地址都打印出来
#include <stdio.h>
int main(int argc, char const *argv[])
{
char buf[2][4] = {"abc","efg"};
printf("abc addrs : %p\n",buf[0]);
printf("a addrs : %p\n",&buf[0][0]);
printf("abc addr + 1 : %p\n",buf[0]+1);
printf("&abc addr + 1 : %p\n",&buf[0]+1);
printf("efg addrs : %p\n",buf[1]);
printf("efg addrs : %p\n",&buf[1][0]);
printf("buf addrs : %p\n",buf);
printf("buf+1 addrs : %p\n",buf+1);
printf("&buf addr : %p\n",&buf);
printf("&buf+1 addr : %p\n",&buf+1);
return 0;
}
demo2:
#include <stdio.h>
int main(int argc, char const *argv[])
{
// 定义一个
int array[2][3] = {{10,20,30},{100,200,300}};
printf("%d\n",array[1][1]);
// 通过地址方式访问元素
printf("%d\n",array[0][0]);
printf("%p,%p,%p,%p,%d,%d\n",array,array[0],&array[0][0],*array,**array,*array[0]);
// 练习:用以上printf 6种方式输出200
printf("%d\n",array[1][1]);
printf("%d\n",*(array[1]+1));
printf("%d\n",*(array[1]+1));
printf("%d\n",*(*(&array[0]+1)+1));
printf("%d\n",*(*(array+1)+1));
printf("%d\n",*(*(&array[0]+1)+2));
return 0;
}
练习2 : 定义一个3行4列的二维数组,并初始化,输出该二维数组的和、最小值的下标(该最小值在第几行第几列)
/*
**练习2** : 定义一个3行4列的二维数组,并初始化,
输出该二维数组的和、最小值的下标(该最小值在第几行第几列)
*/
#include <stdio.h>
int main(int argc, char const *argv[])
{
int array[3][4] = {
{1,-1,5,6},
{10,11,12,13},
{18,-15,3,8}
};
int sum = 0;
int min_value = array[0][0];
int minRowIndex = 0; // 最小行
int minColIndex = 0; // 最小列
for(int row = 0; row < 3; row++) // 遍历行
{
for(int col = 0; col < 4; col++) // 遍历列
{
// 计算总数
sum += array[row][col];
if(min_value > array[row][col])
{
min_value = array[row][col];
minRowIndex = row;
minColIndex = col;
}
}
}
printf("sum:%d\t array[%d][%d] = %d\n",sum,minRowIndex,minColIndex,min_value);
return 0;
}
- 二维数组的名字其实也是地址
#include <stdio.h>
int main(int argc, char const *argv[])
{
int arr[2][3] = {{10,20,30},{40,50,60}};
for(int i = 0; i < 2; i++)// 表示有多少个一维数组
{
for(int j = 0; j < 3; j++)// 表示每个一维数组有多少个元素
{
printf("arr[%d][%d] arr:%p \t",i,j,&arr[i][j]);
}
printf("\n");
}
printf("\n");
printf("arr数组名: %p\n",arr);
printf("arr+1: %p\n",arr+1);
printf("&arr[0][0]+4:%p\n",&arr[0][0]+4);
printf("arr[0]+1:%p\n",arr[0]+1);
printf("&arr[0]+1:%p\n",&arr[0]+1);
printf("arr[0]+5:%p\n",arr[0]+5);
/**************************/
printf("**************************\n");
printf("&arr:%p\n",&arr);
printf("&arr+1:%p\n",&arr+1);
return 0;
}
练习3:
char str[3][5] = { {'h','e','a','l','o'},
{'w','o','r','c','d'},
{'h','d','g','b','k'}};
画出 该二维数组的内存图 , 数组的名字代表的地址范围 是哪个,还有 &str[1] [2] +2 代表的地址范围
str[2] +2 代表的地址范围 &str代表的地址范围 &str + 1代表的地址范围
数组万能拆解法
- 任意的数组,不管有多复杂,其定义都由两部分组成。
- 第1部分:说明元素的类型,可以是任意的类型(除了函数)
- 第2部分:说明数组名和元素个数
- 示例:
int a[4]; // 第2部分:a[4]; 第1部分:int
int b[3][4]; // 第2部分:b[3]; 第1部分:int [4]
int c[2][3][4]; // 第2部分:c[2]; 第1部分:int [3][4]
int *d[6]; // 第2部分:d[6]; 第1部分:int *
int (*e[7])(int, float); // 第2部分:e[7]; 第1部分:int (*)(int, float)
- 注解:
- 上述示例中,a[4]、b[3]、c[2]、d[6]、e[7]本质上并无区别,它们均是数组
- 上述示例中,a[4]、b[3]、c[2]、d[6]、e[7]唯一的不同,是它们所存放的元素的不同
- 第1部分的声明语句,如果由多个单词组成,C语言规定需要将其拆散写到第2部分的两边
课后作业:
1、定义一个二维数组,存入 5 个学生的数学、语文、英语、物理、化学 5 门课程的成绩,计算并输出每一门课程的平均成绩和每一位学生的平均成绩。
扩展:
2、设计一个程序,实现 学生信息录入系统(姓名、年龄、分数)
1、可以制作一个简单的登录系统
账号、密码的验证 (其实就是字符串的比较 strcmp)
2、录入信息之后,可以查询
#include <stdio.h>
int main(int argc, char const *argv[])
{
int score[5][5] = {
{80,90,95,70,75},
{85,80,70,85,75},
{90,90,90,80,90},
{80,85,90,70,75},
{95,90,85,80,75}
};
// 平均分
float aver = 0;
// 每位学生的成绩
int sum = 0;
// 整理界面
printf("序号\t数学\t语文\t英语\t物理\t化学\t平均分\n");
for(int i = 0; i < 5; i++) // 学生
{
// 输出序号
printf("%d\t",i+1);
// 计算5个科目的总数
for(int j = 0; j < 5; j++)
{
printf("%d\t",score[i][j]);
sum += score[i][j];
}
// 计算平均分
aver = sum / 5;
printf("%.2f",aver);
printf("\n");
sum = 0;
}
return 0;
}
拓展:
strcmp,strcpy..
2.数组进阶
数组名涵义
- 数组名有两个含义:
- 第一含义是:整个数组
- 第二含义是:首元素地址
- 当出现以下情形时,那么数组名就代表整个数组:
- 在数组定义中
- 在 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]);
此处 “abcd” 代表匿名数组的首元素地址
[外链图片转存中...(img-kUC4X5B9-1721045067228)]
### **零长数组(预习:结构体)**
- 概念:长度为0的数组,比如 int data[0];
- 用途:放在结构体的末尾,作为可变长度数据的入口
- 示例:
```c
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]);