1.数组的声明
char name[10];
int number[200], value[5][12];
name是一个长度为10的数组,其中每一个元素的类型是char。number是一个长度为200的数组,其中每一个元素的类型是int。
value是一个5*12的二维数组每个元素的类型是int
数组元素的下标从0开始,即name[0],number[0],value[0][0]分别表示第一个元素。
数组中最后一个元素的下标是size-1,size是数组的大小。
2.数组的初始化
int number[10]={1,2,3,4,5,6,7,8,9,10}; /*从ANSI C开始支持这种初始化方式 */
int value[5][12]=
{
{初始化value[0][0]-value[0][11]};
{初始化value[1][0]-value[1][11]};
{初始化value[2][0]-value[2][11]};
{初始化value[3][0]-value[3][11]};
{初始化value[4][0]-value[4][11]};
}
更高维的数组声明与此类似。
如果想只读取数组中的元素的值而不对数组中的元素进行改变的话需要使用
const对数组进行声明。
const int int number[10]={1,2,3,4,5,6,7,8,9,10};
使用数组之前也应改先对数组进行初始化。如果不初始化数组那么数组中存储的是垃圾值。如果部分初始化数组,那么剩余的元素被初始化为0.
可以省略方括号中的数字,让编译器自动匹配数组大学校和初始化列表中的项数。例如:
const int int number[10]={1,2,3,4,5,6,7,8,9,10};
指定初始化器:
int number[10]={[9]=10};
将number[9]赋值成10,其他元素初始化为0.
指定初始化的两个重要特性:
1. 如果指定初始化器后面有更多的值,那么后面这些值将被用于初始化指定元素后面的元素。例如:
[4]=31,30,31,表示的意思是[5]=30,[6]=31
2. 如果再次初始化指定的元素,那么最后的初始化将会取代之前的初始化。例如:
int days[]={1 ,2, [1]=30 };那么day[1]=30而不是2.
注意:
1. c语言不允许把数组作为一个单元赋给另一个数组,除初始化外也不允许使用花括号列表的形式赋值;
2. 在使用数组时,要确保数组下标没有越界,因为编译器不会检查出这种错误。如果数组下标越界那么他们会改变程序中其他变量的值。
3. 声明数组时使用符号常量来表示数组的大小。
3. 指针和数组
数组名是数组首元素的地址。也就是说如果value是一个数组那么下面的语句成立:
value== &value[0].
注意:
a. 指针的值是它所指向对象的地址。
b. 在指针前面使用*运算符可以得到该指针所指向对象的值。
c. 指针加1,指针的值递增它所指向类型的大小(以字节为单位)——指针加1,指向的是数组中的下一个元素的地址而不是下一个字节的地址。
例如:
value+2 == &value[2] /* value 的类型是short
*(value+2)==value[2]
上面的两个语句都是正确的。
4. 函数、数组和指针
首先举一个例子:
定义一个求数组中元素总和的函数sum,数组的类型是int。函数的定义如下:
----------------------------------------------------------------------------------------------------------------------------------------
int sum(int * ar, int n)
{
int i,
int total = 0;
for (i=0;i<n; i++)// 使用n个元素
total += ar[i];// ar[i]和*(ar+i)相同
return total;
}
这里第一个形参告诉函数该数组的地址和数据类类型,第2个形参告诉函数该数组中元素的个数。
关于函数的形参,还有一点要注意。只有在函数原型或函数定义头中,才可以用int ar[i] 代替int *ar:
int sum(int ar[], int n);
int *ar形式和int ar[]形式都表示ar是一个指向int的指针。但是,int ar[]只能用于声明形式参数。第2中形式(int ar[])提醒读者
指针ar指向的不仅仅是int类型还是一个int类型数组的元素。
注意 声明数组形参
因为数组名是该数组首元素的地址,作为实际参数的数组名要求形式参数是一个与之相匹配的指针。
只有在这种情况下,c才会把int ar[]和int * ar解释成一样。也就是说,ar是指向int的指针。由于函数原型
可以省略参数名,所以下面4种原型都是等价的:
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语言保证超过数组最后一个元素的第一个位置的指针有效。
------------------------------------------------------------------------------------------------------------------------------------------------------------------
声明:两条‘------------------’线中间的内容摘自《c primer plus中文版(第6版)》的第290-291页。
5. 指针操作
指针的基本操作:
a. 赋值:可以把地址赋给指针。
b. *解引用:*运算符给出指针指向地址上存储的值。
c. 取值:指针变量也有自己的地址和值。对指针而言,&运算符给出指针本身的地址。
d. 指针与整数相加:可以使用+运算符把指针与整数相加,或整数与指针相加。
e. 递增指针:递增指向数组元素的指针可以让该指针移动至数组的下一个元素。
f. 指针减去一个整数:可以使用“-”运算符从一个指针中减去一个整数。指针必须是第一个运算对象,整数是第二个运算对象。
g. 指针求差:可以计算两个指针的差值。
h. 比较:使用关系运算符可以比较两个指针的值,前提是两个指针都指向相同类型的对象。
注意:千万不要解引用未初始化的指针。
编写一个处理基本类型(如,int)的函数时,要选择是传递int类型的值还是传递指向int的指针。
通常都是直接传递数值,只有程序需要在函数中改变数值时,才会传递指针。对于数组别无选择,
必须传递指针,因为这样做效率高。
注意:
a. 把const数据或非const数据的地址初始化为指向const的指针或为其赋值是合法的;
b. 只能把非const数据的地址赋给普通指针。
下面的语句声明并初始化一个不能指向别处的指针:
int rates[5]={1,2,3,4,5};
int * const pc = rates; // pc 指向数组的开始
pc =&rates[2]; //不允许,因为该指针不能指向别处
*pc = 92; //没问题----更改rates[0]的值
在创建指针时还可以使用const两次,该指针既不能更改它所指向的地址,也不能修改指向地址上的值
int rates[5]={1,2,3,4,5};
const int * const pc =rates;
pc = &rates[2]; //不允许
pc = 92; //不允许
把const指针赋值给非const指针不安全,因为这样可以使用新的指针改变const指针指向的数据。
但是把非const指针赋给const指针没问题,前提是只进行一级解引用。
6. 指针和多维数组
int zippo[4][2];
int (*pz)[2]; //pz指向一个内含两个int类型值的数组(注意括号不能省略)
pz = zippo;
虽然pz是一个指针不是数组,但是也可以使用pz[m][n]这样的写法来访问数组中的值。
可以用数组表示法或指针表示法来表示一个数组元素,既可以使用数组名也可以使用指针名:
zippo[m][n]与*(*(zippo+m)+n)所表示的值一样;
pz[m][n]与*(*(pz+m)+n)所表示的值一样。
指针之间的赋值比数值之间的赋值更严格。例如,不同类型转换就可以把int类型的值赋给double
类型的变量,但是两个类型的指针不能这样做。
7. 变长数组
变长数组中的“变”不是指可以修改已创建数组的大小。一旦创建了变长数组,它的大小则
保持不变。这里的“变”指的是:在创建数组时,可以使用变量指定数组的维度。