数组与指针
数组
一维数组
数组初始化之前数组元素是不确定的,当数值数量少于数组元素时,多余的数组元素被初始化为0
只读数组初始化后不能再对它赋值
如何计算一个数组的大小: sizeof(a)/sizeof(a[0])
即数组大小除以单个元素的大小
c99新特性:int arr[6]={12,[5]=22,23,[1]=35};
这一语句代表第1个元素值为35,第5个值为22,第6个值为23
数组的赋值方式:
- 只可以用循环来赋值
- 不可以用花括号赋值(仅限于数组初始化),不可以将一个数组赋给另一个数组
数组的边界问题需要程序员来保证,编译器检查索引的合法性,因为c语言信任程序员的原则,不检查边界可以让程序运行更快,解决方法是将数组大小全部定义为符号常量,使用数组大小的时候全部直接引用符号常量
c99之前声明数组时方括号内只能使用整数常量表达式, sizeof
被认为是一个整数常量,因此可以放在数组的声明里
指针
在c中,对一个指针加1的结果是对该指针增加1个存储单元。这就是为什么声明指针的时候必须声明它指向的对象类型。
将数组作为参数传入函数时,一般要同时传入数组的大小。因为作为参数传入的实际是一个指向数组元素类型的指针,在函数内部无法用sizeof
得出数组的大小,只能得出这个指针的大小。
函数原型允许省略名称:
int sum(int *ar,int n);
int sum(int *,int);
int sum(int ar[],int n);
int sum(int [],int);
函数定义不可以省略名称:
int sum(int *ar,int n){
//代码
}
int sum(int ar[],int n){
//代码
}
使用数组的函数有两种方式:
- 传入数组元素个数
- 传入两个指针,一个指向起始地址,一个指向结束地址(结束地址指向的是数组最后一个元素之后,c语言中指向数组之后的第一个元素的指针也是合法的,仅仅是指针合法,所指的值不合法!)
*
和++
的优先级:优先级相同,但是结合时从右往左进行。
指针操作
- 赋值
- 求值或取值。用
*
可取出指针指向地址中存储的数值 - 去指针地址
- 将一个整数加给指针。该整数会先和指针所指类型的字节数相乘,然后所得的结果加到初始地址上
- 增加指针的值。
ptr++
- 从指针中减去一个整数
- 减小指针的值
- 求差值。通常对指向同一个数组的内的两个元素的指针求差值,以求出元素之间的距离
- 比较。前提:两个指针具有相同的类型
常见错误:对未初始化的指针取值,其危害在于未初始化的指针中的初值未知,因此不知道会覆盖哪里的数据。
int *ptr;
*ptr=5;
切记:当创建了一个指针时,系统只是分配了用来存储指针本身的内存空间,并不分配用来存吃数据的内存空间!
指针最基本的功能在于同函数交换信息
保护数组内容
如果希望函数不改变数组的内容,可以在函数原型和定义的形式参量声明中使用关键字const
,作用是告诉编译器,函数应当将该数组作为包含常量数据的数组对待。好处是,这样做并不要求原数组是固定不变的
关于const
:
-
用const可以定义指向常量的指针,这样就不可以用该指针来修改它所指的数值,此时const放在 * 前
-
将常量或非常量数据的地址赋给指向常量的指针是合法的,但只有非常量数据的指针才可以赋给普通指针。原因:如果指向常量的指针可以赋给普通指针,那么就可以通过该普通指针修改被认为是常量的数据了。
用处:如果函数的原型中形式参数声明使用const,那么该函数既可以接收普通指针,又可以接收指向常量的指针。但是如果函数原型中不使用const,那么就不可以接收指向常量的指针!
-
用const可以保证指针不会指向别处,此时const放在 * 之后
-
同时用两个const表示既不可以修改所指的数值,又不可以更改所指地址
指针与多维数组
int zippo[4][2]
其中zippo是所指向对象的大小是两个int,zippo[0]所指向的对象是一个int
注意:*(zippo+2)
表示zippo[2][0]
的地址,*(*(zippo+2)+1)
表示zippo[2][1]
的值
如何定义指向二维数组的指针?
int (*pz)[2]; //一定要加括号,表示指向一个包含两个int值的数组
pz=zippo;
int *pax[2]; //表示定义两个指针组成的数组
区别于指针可以将非const
的指针赋给const
的指针,不可以将非const
的指针的指针赋给const
的指针的指针,这种赋值只允许一层间接关系
const int **pp2;
int *p1;
const int n=13;
pp2=&p1; //非法,但假设合法
*pp2=&n;//合法,二者都是const,同时也会让p1指向n
*p1=10;//合法,但此时会修改n的值
函数与多维数组
函数原型等价形式:
void somefunction(int (*pr)[4],int rows);
void somefunction(int pt[][4],int);
void somefunction(int [][4],int rows);
void somefunction(int [3][4],int rows); //合法,但是3会被忽略
每个函数都把pt看作指向包含4个int值的数组的指针,列数在函数体内定义,但行数需要作为参数传入
变长数组
C99标准引入变长数组,允许使用变量定义数组各维
注意:
- 变长数组的“变”表示维数可以用变量来指定,而不是创建数组之后可以修改其大小
- 变长数组必须是自动存储类的,意味着它们必须在函数内部或作为函数参量声明,而且声明时不可以进行初始化。即不能定义在函数的外部,而且不可以初始化!
int sum2d(int rows,int cols,int ar[rows][cols]); //合法
int sum2d(int,int,int ar[*][*]); //合法,可以省略维数参量的名称,用星号代替
int sum2d(int ar[rows][cols],int rows,int cols); //非法,顺序不对
复合文字
C99新增复合文字,文字是非符号常量,复合文字没有名称,因此必须在创建它们的同时通过某种方法使用它们
如何定义:
(int [2]){10,20}
(int [])={10,20,30} //可以省略数组大小
使用方法:
- 文字常量同时代表首元素的地址,可以用它给一个指向int的指针赋值
- 可以作为实参传递给带有类型与之匹配的形式参数的函数。该方法较为常用,好处在于可以给函数传递信息而不必先创建数组