一、基本概念
1.数组概述
数组的概述 (1)数组在内存中是连续存储的空间,且每个元素所占的内存空间相同,因此方便指针的移动与取值,适合与指针结合; (2)数组定义时必须确定数组的大小,可以通过int a[10];方式或int a[] = {1,2,3,4};的方式(此方式编译器会根据初始化值自动确定数组大小),操作数组元素时必须单独访问,不能用数组名a同时对整个数组赋值操作; (3)例int a[10],从编译器角度,每个数组元素也是变量,变量名为数组元素a[i];变量类型为int类型,变量长度为sizeof(int) * 10;明确 a, a[0], &a, &a[0] 之间的区别。以int a[10]为例: (1) a:做右值时代表数组首元素的首地址,在意义与数值都等同于&a[0];无法做左值,因为其是一个地址是常量无法赋值; (2) a[0]:做右值时代表数组第0个元素的值;做左值时代表数组第0个元素的内存空间; (3) &a:做右值时代表整个数组的首地址,在数值上和a, &a[0]相等,但意义不同,a和&a仅代表首元素地址;无法做左值,因为是一个地址常量无法赋值。 (4) &a[0]:代表数组首元素地址,做右值等同与a;无法做左值;访问数组元素的方法 (1)数组下标访问:数组名[i],如printf("a[5] = %d\n", a[5]);; (2)指针访问:(指针+偏移量):如printf("a[5] = %d\n", *(a +5)); 数组访问的本质就是指针访问,数组访问知识编译器提供的一种便捷方式。 注意: 1、int *p;,p + 1 代表的是p + 1 * (sizeof(int)); 2、int a[10]; int *p; p = &a; p的类型是int ()[10]类型,是数组指针类型。
2、二维数组
二维数组概述 (1)二位数组的定义:int a[2][4] = {{0,1,2,3}, {4,5,6,7}};或者 int a[2][4] = {0,1,2,3,4,5,6,7}; (2)从内存的角度看,二位数组和一位数组在内存中的存储方式及访问效率等并无区别,只是为了方便组织管理; (3)应用:如有10个学生成绩要统计;如果这10个学生没有差别的一组,就用b[10];如果这10个学生天然就分为2组(如男和女)每组5个,就适合用int a[2][5]来管理。二位数组的维数划分:以int a[2][4]为例 (1)第一维:由两个数组元素组成第一维数组,每个元素是由四个元素组成的一维数组,即第一个元素{0,1,2,3},第二个元素{4,5,6,7}; (2)第二维:由四个普通元素组成一维数组;区分a, &a, a[0], &a[0], a[0][0], &a[0][0] (1)a是整个二维数组的数组名,代表第一维数组的第一个数组元素的地址,数值和意义等同于&a[0],其作用域是整个a[0]数组; (2)&a代表整个二维数组的首地址,其作用域是整个a二维数组; (3)a[0]是第一维数组的第一个数组元素的数组名,代表a[0]的首地址,即数值和意义等同于&a[0][0],其作用域是a[0][0]; (4)&a[0]代表第一维数组第一个数组元素a[0]的整个数组的首地址,数值和意义等同于a,其作用域是整个a[0]数组;; (5)a[0][0]代表第一维数组a[0]的第一个数组元素的值。 (6)&a[0][0]代表第一维数组a[0]的第一个元素的地址,即数值和意义等同于&a[0][0],其作用域是a[0][0];
int a[2][4] = {{0,1,2,3}, {4,5,6,7}};
a[1][3] == *(*(a + 1) + 3);
/*a表示二维数组地址,即二维数组中第一个数组的地址
/*a + 1表示第二个数组的地址
/* *(a+1)表示第二个数组的第一个元素的地址
/* *(*(a+1) + 3)表示第二个数组的第三个元素的地址
int *p = a[1]; //二维数组中第一个数组名,代表第一个数组的首元素地址
int a[1][3] == *(p + 3);
二、数组与其他类型结合
1.指针数组与数组指针
区别 指针数组:本质是数组,元素为指针类型的数组; 数组指针:本质为指针,指向的是数组类型的指针;表达式 指针数组:int * p[10] (1)由于[]的优先级高于 *的优先级,因此p先与[10]结合成为含有10个元素的数组; (2)int与*结合成为指向int类型的指针; (3)int*与p[10]结合成为含有10个int*类型元素的数组; 数组指针:int (*p)[10] (1)由于()的优先级最高,因此p先与 *结合成为一个指针; (2)然后根据优先级再与[10]结合成为指向10个元素数组的指针; (3)再与int结合成为指向含有10个int类型元素数组的指针; 注意: 判断是指针数组还是数组指针方法,就是取决于变量名p先与* 结合成为指针还是先与[]结合成为数组,先结合的决定变量的数据类型。指针数组指针——二重指针 (1)二重指针:本质上也是指针变量,和数组的指针、结构体指针、函数指针、结构体指针无区别。其指向的是一个指针变量的地址,地址中存储的变量指向其他类型的变量,即二重指针指向了一个一重指针; (2)定义方法:int** p1:p1指向一个int *类型的变量的地址,该地址存放的是一个int类型的变量的地址; (3)二重指针一般与指针数组结合在一起,也有将二重指针作为参数传递给函数调用的用法。
2.字符数组与字符串
字符串概述 (1)C语言中没有原生的字符串(string)类型,而是通过指针和字符数组间接实现字符串; (2)字符串的构建:指针指向首个字符、固定尾部‘\0’、一段连续的内存:char *str = "linxu"; (3)字符串指针str与“linux”字符串无关,str中存储的是字符串首个字符‘l’的地址而已,而非“linux”这个字符串的值; (4)结束符‘\0’的ascll码值是0代表Null(空字符),与字符'0'有区别,字符'0'的ascll码值是48;当字符串遇到'\0'时,就认定为字符串结束,因此字符串无法定义'\0'本身这个字符,但'\0'结束符不属于字符串的范围内;
字符串与字符数组的区别 (1)组成方式:字符串是由字符串指针pc与字符串"linux"组成,二者各自占用内存空间,pc只是指向字符串首个字符‘l’的地址;字符数组是将“linux”的字符本身放到数组中; (2)存储方式:字符串指针pc存放在栈/堆或静态存储区中(若定义的是全局变量),“linux”存放在.text段中;字符数组存放在栈/堆中或静态存储区中(若定义的是全局变量);
int main(void)
{
char *pc
= "linux";
char a
[] = "linux";
}
sizeof与strlen (1)sizeof:是一个C语言的关键字和运算符而非函数,作用是返回一个变量或数据类型所占的内存字节数; (2)strlen:是一个C语言的库函数,原型为size_t strlen(const char *s),作用是接收一个字符串后,返回字符串(不包括结束符‘\0’)所占的字节大小。
int strlen(char *ps
)
{
int cnt
= 0;
while(*ps
!= '\0')
cnt
++;
return cnt
;
}
int main(void)
{
char *ps
= "linux";
int len
= strlen(ps
);
pritnf("len = %d\n", len
);
return 0;
}
字符串与字符数组的sizeof与strlen (1)sizeof(数组名)得到的是整个数组占内存的大小,与数组的类型、是否初始化无关;strlen(数组名)只能计算字符数组中的字符元素所占内存的大小,传入其他数据类型的指针是无意义操作; (2)sizeof(字符指针)得到的是字符指针本身所占的4byte内存,而非字符串的大小,因为字符指针本身与字符串无关;strlen(字符指针)可以得到字符串所占的大小;
int main(void)
{
char a
[] = "linux";
char *s
= "linux";
printf("sizeof(a) = %d\n", sizeof(a
));
printf("sizeof(s) = %d\n", sizeof(s
));
printf("sizeof(linux) = %d\n", sizeof("linux"));
printf("strlen(a) = %d\n", strlen(a
));
printf("strlen(s) = %d\n", strlen(s
));
}