写在前面
最近决定重新复习一遍C语言,感觉大学前三年代码能力还是不好,决定复盘一下C语言和数据结构,把比较重要的点记录下来,最后希望可以做点好玩的小项目(五子棋、贪吃蛇之类的)吧。我把这取名为 “ C计划 ”,希望可以坚持下来。
最近复习到数组、字符串和指针,当时学的时候就感觉是语法重点,在这里进行一下汇总,有不对的地方希望各位同学指正补充,一起进步!
数组
数组的定义
数组的定义很平常,对于单纯的纯数组,定义要包括:数组的数据类型、数组名(地址)、数组元素个数。如果 [ ] 内未声明元素个数,则需要直接完成初始化,否则会报错。原因是计算机不知道要为数组开辟多大的存储空间。
下面展示一些 数组定义方式
。
float candy[365]; /* 内含365个float类型元素的数组 */
char code[12]; /*内含12个char类型元素的数组*/
int states[50]; /*内含50个int类型元素的数组 */
int m = 8; int n = 7;
float a1[5]; // 可以
float a2[5*2 + 1]; //可以
float a3[sizeof(int) + 1]; //可以
float a4[-4]; // 不可以,数组大小必须大于0
float a5[0]; // 不可以,数组大小必须大于0
float a6[2.5]; // 不可以,数组大小必须是整数
float a7[(int)2.5]; // 可以,已被强制转换为整型常量
float a8[n]; // C99之前不允许
float a9[m]; // C99之前不允许
int states[]; /*无法通过,要跟python区分开*/
int states[]={0,1,2}; /*没问题*/
数组的初始化
数组的初始化需要注意的点是:
- 如果对某一数组没有进行任何初始化,那么该数组里存储的是“垃圾值”。
- 如果对某一数组进行部分初始化,那么计算机自动把剩余的位初始化为 0。
- 如果未声明数组元素个数,那么在定义时一定要进行初始化,否则,计算机不知道要为这个数组开辟多大的存储空间,编译无法通过。
- 在引用时注意下标,数组初始索引为0。
C99 增加了一个新特性:指定初始化器(designated initializer)。利用该特性可以初始化指定的数组元素。例如,只初始化数组中的最后一个元素。对于传统的C初始化语法,必须初始化最后一个元素之前的所有元素, 才能初始化它;而C99规定,可以在初始化列表中使用带方括号的下标指明待初始化的元素。
int arr[6] = {0,0,0,0,0,212}; // 传统的语法
int arr[6] = {[5] = 212}; // 把arr[5]初始化为212
对于一般的初始化,在初始化一个元素后,未初始化的元素都会被设置为0。
多维数组
多维数组定义要包括:数组的数据类型、数组名(地址)、数组元素行和列。
float rain[5][12] = ; // 内含5个数组元素的数组,每个数组元素内含12个 float类型的元素
当然,我们可以继续定义高维的数组。
float arr[5][12][12] ; // 内含5个数组元素的数组,每个数组元素内含12个 float类型的元素
后面做项目可能会用到多维数组比较多,到时候再说。
指针
指针提供一种以符号形式使用地址的方法,指针的声明是要用到间接运算符‘ * ’。‘ & ’取地址,‘ * ’取所在地址的变量值。
int *ptr; / *定义*/
假设一个指针变量名是ptr,可以编写如下语句:
ptr = &pooh; // 把pooh的地址赋给ptr
对于这条语句,我们说ptr“指向”pooh。ptr和&pooh的区别是ptr是变量, 而&pooh是常量。或者,ptr是可修改的左值,而&pooh是右值。还可以把ptr 指向别处:
ptr = &bah; // 把ptr指向bah,而不是pooh
现在ptr的值是bah的地址。
一维数组与函数指针
- 数组的名称就是数组的首地址,在我们的系统中,是按字节编址的。int类型占4个字节,我们对指针加一,指向的是下一个数组的元素的地址,而不是指向下一个字节的地址。
int *sp;
int arr[6] = {1,2,3,4,5,6};
sp = arr;
for (int i = 0; i < 6; i++)
{
printf("第%d个元素%d,他的地址为%d\n", i,*(sp+i),sp+i);
}
- 函数传递的参数也可以是指针,比如下面这个程序
int sum(int* ar) // 相应的函数定义
{
int i; int total = 0;
for (i = 0; i < 10; i++) // 假设数组有10个元素
total += ar[i]; // ar[i] 与 *(ar + i) 相同
return total;
}
- 指针本身也是有地址的。因此&sp是指向sp的指针,而sp是指向arr的指针。
int main()
{
int *sp;
int *a;
int arr[6] = {1,2,3,4,5,6};
sp = arr;
for (int i = 0; i < 6; i++)
{
a = sp + i;
printf("第%d个元素%d,这个元素的的地址为%p,当前指针的地址为%p.\n", i,*a,a,&a);
}
}
切记:创建一个指针 时,系统只分配了储存指针本身的内存,并未分配储存数据的内存。因此, 在使用指针之前,必须先用已分配的地址初始化它。下面代码是不允许的。
int main()
{
int *sp;
*sp = 5; /*这是不允许的*/
}
多维数组的指针
如何声明一个指针变量pz指向一个二维数组(如,zippo)?在编写处理类似zippo这样的二维数组时会用到这样的指针。把指针声明为指向int的 类型还不够。因为指向int只能与zippo[0]的类型匹配,说明该指针指向一个 int类型的值。但是zippo是它首元素的地址,该元素是一个内含两个int类型 值的一维数组。因此,pz必须指向一个内含两个int类型值的数组,而不是指 向一个int类型值,其声明如下. 注意与第二行的区分。
int (* pz)[2]; // pz指向一个内含两个int类型值的数组
int * pax[2]; // pax是一个内含两个指针元素的数组,每个元素都指 向int的指针
这一部分还是比较难理解的,这里直接放上《C primer plus》中的解释,个人感觉解释的蛮清楚的。
关于指针的兼容性和多级解引用,在《C primer plus》中有着很多的介绍,可能学得还不够深入,觉得书中介绍的情况在工程生活中有点“刁钻”,先了解了一下,等遇到的时候再来细细研究一下。