数组
数组由一系列类型相同的元素构成,数组声明中包括数组元素的数目和元素的类型。
int main() {
float candy[365];
int arr[3]={1,2,3}; //数组初始化:只有初始化的时候才可以这样赋值
int arr[3]={[2]=1}; //指定初始化第三个数为1
arr={1,2,3};//不起作用
}
- 方括号[]表示candy为数组,方括号内的数字指明了数字所包含的元素数目。
- 如果要访问数组元素,可以使用下标数字来表示单个元素。下标数字也称索引,从0开始计数。
只读数组:数组中的每个元素都是常量,需要在声明时进行初始化,因为声明之后,不可以再对它赋值。
const int arr[3] = {1,2,3};
指针
指针提供了一种用来使用地址的符号方法。数组标记实际上是一种变相使用指针的形式。
数组名同时也是该数组首元素的地址。(常量)
int arr[3] = {1,2,3};
arr == &arr[0];
//数组名是该数组首元素的地址
arr, &arr[0]
都代表首元素的内存地址,两者都是常量,可以将其作为赋给指针变量的值。
在C中,对一个指针加1的结果是对该指针增加一个存储单元,对于数组而言,地址会增加到下一个元素的地址,而不是下一个字节。
声明指针时必须声明它所指向对象的类型,因为计算机需要知道存储对象所用的字节数,指针自增和自减时与字节数有关。
- 指针的数值就是它所指向的对象的地址。地址的内部表现方式是由硬件来决定的。
- 在指针前运用运算符*就可以得到该指针所指向的对象的数值。
- 对指针加1,等价于对该指针的值加上它指向的对象的字节大小。
dates + 2 == &date[2];
//相同的地址,dates+index
是元素date[index]
的地址。
*(dates + 2) == dates[2];
// 相同的值
函数,指针和数组
可以在指针符号中使用数组名,也可以在数组符号中使用指针。
//第一个参数是数组的地址,第二个参数是数组长度
int sum(int *ar,int n){
int total = 0;
for(int i = 0;i < n;i++)
total += ar[i];
return total;
}
只有在函数原型或者函数定义头的场合中,可以用int *ar
代替int ar[]
。无论在任何情况下,形式int *ar
都表示ar
是指向int
的指针。int ar[]
可以表示ar
是指向int
的指针,但是只是在声明形式参量时才可以使用。使用第二种形式可以提醒读者ar
不仅是指向一个int
数值,而且它指向的这个int
是一个数组中的元素。
正常定义的数组,其数组名存储的是数组第一个元素的地址,是一个常量,赋值或者自增自减运算不合法。
把一个定义好的数组作为函数的形参时,此时数组名在编译时就会被当作指针变量来处理(形参肯定是一个指针变量,只有指针变量才能存放地址),此时这个形参的赋值或者自增自减运算是合法的。
以指针传进去,而指针是一个地址变量,可以进行操作。
指针变量的操作
- 赋值
int *p = &a
- 求值
int a = *p
- 取指针地址
int **q = &p
- 将一个整数加给指针,这个整数都会和指针所指类型的字节数相乘,然后所得的结果会加到初始地址上,如果相加的结果超出了初始指针所指向的数组范围,那么这个结果是不确定的。
保护数组内容
在处理普通变量时,可以进行值传递,不改变原本的值。对于处理数组的函数,只能传递指针,原因是这样能使程序的效率更高,如果通过值向函数传递数组,那么函数中必须分配足够存放一份原数组的拷贝的存储空间,然后把原数组的所有数据赋值到这个新数组中,效率不够高。
如果我们不希望修改该数组的值,可以用const
int sum(const int ar[],int n);
//函数声明
此时数组的值无法修改。
指针和多维数组的关系
int arr[4][2];
数组名arr
是数组首元素的地址,首元素包含了两个int
的数组,因此arr
也是包含两个int
的数组的地址。
arr=&arr[0], arr[0]=&arr[0][0]
,arr+1
下一行,arr[0]+1
,下一个arr[0]
是首元素arr[0][0]
的地址,所以*arr[0] == arr[0][0],
*arr = &arr[0][0]
,**arr = *&arr[0][0] = arr[0][0].
图中的arr作为指针使用
定义指向多维数组的指针
int (* pz)[2];
pz指向一个包含两个int
值的数组。[]
的优先级高于*
,如果不加括号,那么定义的就是由两个指针组成的数组。
参考《c primer(第五版)》