数组与指针
数组的声明规则: int a[] ={元素1,元素2.......},或int a[数组长度]。前者会根据数组中元素个数自动分配数组大小。后者只规定了数组大小。数组声明后需对数组进行初始化,即对数组中元素赋值,若无赋值,储存的则是无意义的数据。现在的c标准支持指定初始化器可指定初始化数组的某个元素。若数组有一个元素被初始化,其他未指定数值的元素会被初始化为0。编译器也可以通过指定初始化器自动划定数组的最小大小。例: int a[]={25,[6]=88,89,90},这里将数组a的第七个元素(数组 的起始下标为0)指定初始化为88,并将之后的两个(8、9)个元素指定初始化为对应的值。这时数组自动分配大小,为9个元素。其余未指定的元素被初始化为0。
注意:编译器不会检查数组下标越界。这时可能会意外修改了其他变量或使用了一个错误的值。
用于变量类型的前缀也可以用于数组的定义,如声明一个只读数组:const int a[5]。
声明多维数组:外围数组的每个元素都是一个内层数组。如 int a[3][5] 实际共有15个元素,三个外层元素,每个外层元素都包含5个内层元素。多维数组也可以抽象成坐标系,常用的二维、三维数组可以视为坐标系中的坐标点。声明时可以这样写:
int a[] = {
{1,1},{1,2),
{2,1},{2,2}
}这样便声明了一个[4][2]形式的二维数组,二维数组同样可以使用指定初始化器,规则相同。:
指针:指针就是地址,某个对象的指针就是该对象在内存中的地址,使用数组下标时其实就是在使用指针。如数组a[],a[i]和*(a+i)是等价的。但由于指针表示法更贴近机器语言,编译器未进行优化的情况下,后者编译出的代码在执行上更高效。
数组名就是该数组首元素的地址,相同的,函数名就是该函数的入口地址。
指针操作:
对于一个指针变量,可以对其赋值,令一个变量储存某个地址。指针变量和地址必须是同一个类型的。地址运算符&
解引用指针*,取出储存在该指针指向地址上的值,若为函数的指针,则就是在调用函数。
取址,指针变量也是变量,指针变量也有自己的地址和值
加上或减去一个整数,这在数组中会用到。加上或减去一个整数,指针变量并不会改变相同的整数,而是会因为指针所指向类型的不同而改变,如一个指向int类型数组中的一个元素的指针,对其加一,则变为指向数组中下一个元素的指针,对应的内存地址就增加了一个int大小(若为64位的编译器,int占用了4个字节,32位,则内存地址就增加了32.)指针递增递减同理。
同类型指针可以求差,可以用来求数组中两个元素的间隔。
同类型指针也可以进行比较。
指针与const 常量和变量的地址都可以赋给const指针,但常量的地址不能赋给非const指针,不然通过改变指针就可以改变数据。
const指针无法指向别处,但可以改变const指针指向的地址中储存的值。
声明一个指向常量的的指针常量 int a = 9; const int * const pt = &a;
变长数组vla:变长数组不是说可以变更已存在的数组的长度。而是可以用变量代替数组的长度,一旦变量确定,数组被创建,数组长度就保持不变。
如一个处理数组的函数,传递一个二维数组的行数与列数,并使用该数组中的数据进行其他操作:
声明函数:int sum2d(int rows,int cols,int ar[rows][cols])或使用省略形参名的形式:int sum2d(int,int,int ar[*][*])
函数原型: int sum2d(int rows,int cols,int ar[rows][cols])
{
int sum;
for(int r=0;r<row;r++)
for(int c=0;c<cols;c++)
sum += ar[r][c];
return sum;
}
调用时:假设已经定义一个二维数组info[3][4];
caculate = sum2d(3,4,info);
复合字面变量:可以用于创建匿名数组,减少思考我这个数组叫什么名的时间(经常用于传递一个数组常量),一般用于计算格式要求以数组的形式输入数据,临时使用时。匿名数组一旦被创建就要被使用,可以使用一个指针指向该匿名数组。也可以直接作为参数被传递。匿名数组具有块作用域,离开语句块后,程序无法保证数组仍然存在。