C的数组与指针

数组

如果数组中只有部分元素被初始化,则其他未被初始化的元素将会被设为0

C99之后,如果只想初始化数组中的一个元素,可以:int array[6] = { [5] = 212 }; 这样,其他的元素会被设为0

int days[7] = {31, 28, [4] = 31, 30, 31, [1] = 29};

结果:

[0]:31,[1]:29,[2]:0,[3]:0,[4]:31,[5]:30,[6]:31,[7]:0

说明:初始化是从标注出标号的开始,如果一个元素被初始化多次,则有效的是最后一次初始化

用大括号列出数值对数组进行赋值只能用于初始化,而不能用于数组元素赋值

C99和C11都支持变长数组(VLA)。VLA有一些限制,例如不能在声明数组时进行初始化


指针

不能解引用(dereference)一个未初始化的指针。因为创建一个指针变量时,只是分配了存储这个指针的内存,但并没有分配存储数据的内存(即并未给指针所将要指向的变量分配空间)。所以在使用指针之前,它所指向的变量应该是已经被分配了内存的(两种方法,一是将已经存在的变量的地址赋给指针;二是使用malloc)

例如double *p; *p = 2.1; 这就是一个错误的用法


函数,指针与数组

指针的值+1时,加的是一个以byte为单位,所指向的存储单元的值

函数调用数组的格式(其中array是数组名):int sum(int* array); 或 int sum(int array[], int n); 还可以不传入数组的大小,sizeof(array)可以获得整个数组所占的空间,再除以每个元素的大小(如sizeof(array[0]))即可得到数组的元素个数

(可以这么理解,(int *)代表函数的参数是一个指针,其值应该是指针变量本身的值,是一个地址,而array恰好是数组中第一个变量的地址)

C保证在为数组分配空间时,指向数组最后一个元素后的第一个位置的指针是合法的

"*"和"++"的优先级是相同的,并且从右到左应用:*start++ == *(start++)

array[i] == *(array+i),但是array++只有当array是指针变量时才能使用(也就是说,如果array是一个数组名,则不能使用array++这种改变array自身值的用法)

只有当两个指针都指向同一个数组时,将两个指针相减才是合法的

如果一个函数不应该改变所引用数组的值时,最好在变量列表中使用"const"关键词,这样在函数尝试修改数组内容时,编译器会报错。这样使用"const"关键词并不要求原数组也是const,只是强调函数不应该改变所调用的数组的值

const double* p = rates;  

指向const的指针不能用于改变所指向变量的值,*p的值不能改变, 但可以指向另一个变量;同时,也可以把const或不是const的数据赋给指向const的指针。只有non-const的数据能被赋给普通指针,也就是说,如果一个变量是const的,那它不能赋给普通的指针,只能赋给指向const的指针(所以将函数形参变量声明为const不仅可以保护数据,还使函数可以处理const的变量)

double* const p = rates;

这种情况下指针只能指向rates而不能再指向其他变量,但是*p的值可以被改变

const double* const p = rates;

也可以使用const两次,表示指针所指向的变量值不能被更改,并且也不能指向其他变量

例:

const int y;
const int* p2 = &y;
int* p1;
p1 = p2;  // error in C++,possible warning in C

C中可以将一个指向const的指针赋给一个non-const指针,但当用non-const指针修改const变量的行为是无法定义的(C++直接不允许这种赋值)


指针与多维数组

int zippo[4][2];

因为zippo[0]是数组第一个元素,所以zippo的值和&zippo[0]相同;因为zippo[0]是一个长度为2的int型数组,所以zippo[0]的值与&zippo[0][0]相同;又因为int和长度为2的int型数组都从内存中的同一个位置开始,所以zippo和zippo[0]的数值是相等的(zippo == zippo[0],二者本质上是同一个变量;但zippo+1和zippo[0]+1的结果是不一样的)

逻辑上,**zippo == *zippo[0] == zippo[0][0],*zippo == zippo[0] == &zippo[0][0]

数值上,zippo == zippo[0] == &zippo[0][0],也就是说&zippo[0] == zippo[0]

*(*(zippo+2)+1) == zippo[2][1]



声明一个指向二维数组指针的方法:int (* p)[2];  (2是二维数组中,最低维数组的大小)

由于"[]"的优先级比"*"高,所以:int * p[2]  //表示p是一个长度为2的数组,数组的元素是指向int型的指针

例:int ar[3][2]; int **p;

则*p = ar[0];  // 合法

     p = ar;      // 不合法 

因为p是一个指针,指向的是指向int型指针;虽然ar也是一个指针,但它指向的是指向长度为2的int型数组的指针。所以,p与ar类型不同,不能互相赋值;但*p和ar[0]都是指向int型的指针,所以可以互相赋值


函数与多维数组

函数使用二位数组作为形参的方法:(第二种方法比较好)

1. void func (int (*p)[4], int row);

2. void func (int p[][4], int row);  // 第一个中括号内可以填上数字,但编译器会无视它

多维数组:void func(int ar[][12][20][30], int row);



变长数组(VLA):

VLA必须是自动存储(atuomatic storage)类,也就是说它们在函数内部或作为函数形参都不能使用static或extern进行声明,也不能在声明时进行初始化。VLA一旦被创建,大小就不能再改变。

声明以二维变长数组作为形参的函数:void func(int row, int col, int ar[row][col]);

因为变长数组的声明要使用行、列大小,所以"row"和"col"必须要在"ar"之前声明

需要注意的一点是,在函数声明中声明VLA并未实际创建数组,数组的名字只是一个指针。这也意味着所有对数组的更改都会对原始数据进行更改


复合文字(compound literal):

C99开始支持,可以这样声明:(int [2]){10, 20};   // "[]"中的数字可以省略

由于没有名字,所以只能在声明时使用(例如:int *p; p = (int [2]){10, 20};),也可以直接传给函数作为参数例如:

void sum(const int ar[], int n);
sum( (int []){1,2,3,4,5}, 6)



你可能感兴趣的:(C的数组与指针)