数组是C内建的基本数据结构,数组表示法和指针表示法紧密关联。一种常见的错误认识是数组和指针彻底能够互换,尽管数组名字有时能够当作指针来用,但数组的名字不是指针。数组和指针的区别之一就是,尽管数组的名字能够返回数组地址,可是名字不能做为赋值操做的目标。html
数组是能用索引访问的同质元素连续集合。数组的元素在内存中是相邻的,并且元素都是同一类型的。数组的长度是固定的,realloc函数和变长数组提供了应对长度须要变化的数组的技术。数组
一维数组是线性结构,用一个索引访问成员。取决于不一样的内存模型,数组的长度可能会有不一样。数据结构
int vector[5];
数组的名字只是引用了一块内存,对数组使用sizeof操做符会获得为该数组分配的字节数。要知道数组的元素数,只要对其除以元素长度便可。函数
int vector[5]; printf("vector is %p\nsize is %d\n", &vector, sizeof(vector)); //vector is 0xbffb8c08 //size is 20
能够用一个块语句初始化一维数组。post
int vector[5] = {1,2,3,4,5};
声明一个二维数组。能够将二维数组当作数组的数组,只使用一个下标访问二维数组获得的是对应行的指针。url
int matrix[2][3] = {{1,2,3},{4,5,6}}; printf("0 is %p\n1 is %p\n", &matrix[0], &matrix[1]); //0 is 0xbf984044 //1 is 0xbf984050
能够看到,两个地址恰好差12个字节,也就是matrix数组一行的长度。 spa
把数组地址赋给指针。指针
int* pv = vector;
注意这种写法和&vector[0]是等价的,不一样于&vector是返回的整个数组的指针。一个是数组指针,一个是整数指针,指针类型不一样。pv实际表明的是一个地址,*(pv+i)的写法等价于pv[i],前者称做指针表示法,后者称做数组表示法。htm
printf("pv[0] is %d\n*(pv+1) is %d\n ",pv[0],*pv); //pv[0] is 1 //*(pv+1) is 1
pv指针包含一个内存块的地址,方括号表示法会取出pv中包含的地址,用指针算数运算符把索引i加上,而后接引新地址返回其内容。给指针加上一个整数实际是给它加了整数与数据类型长度的乘积,这一点对数组名字也一样适用,*(pv+1)和*(vector+1)是等价的。所以数组表示法能够理解为“偏移并解引”操做。vector[i]和*(vector+i)两种方法结果相同,仅仅在实现上略有差异,能够忽略。注意指针能够做为左值,可是数组名字不能够做为左值使用,vector=vector+1是错误的写法。blog
若是从堆上分配内存并把地址赋给一个指针,那就确定能够对指针使用下标并把这块内存当作一个数组。用malloc建立的已有数组的长度能够经过realloc函数来调整。C99支持变长数组,可是变长数组只能在函数内部声明。因此若是数组的生命周期须要比函数长,或者你没有使用C99,那就只能使用realloc。
下面这个函数接受用户的输入,而且使用realloc函数动态的申请所需的内存,按回车结束输入。
char* getLine(void) { const size_t sizeIncrement = 10; char* buffer = malloc(sizeIncrement); char* currentPosition = buffer; size_t maximumLength = sizeIncrement; size_t length = 0; int character; if(currentPosition == NULL){return NULL;} while(1) { character = fgetc(stdin); if(character == '\n'){break;} if(++length >= maximumLength) { maximumLength += sizeIncrement; char* newBuffer = realloc(buffer,maximumLength); if(newBuffer == NULL){free(buffer); return NULL;} currentPosition = newBuffer + (currentPosition - buffer); buffer = newBuffer; } *currentPosition++ = character; } *currentPosition = '\0'; printf("buffer is %s\n", buffer); return buffer; } getLine();
将一维数组做为参数传递给函数实际是经过值来传递数组的地址,咱们须要告诉函数数组的长度,不然函数只有一个地址,不知道数组到底有多长。对于字符串来讲,能够依靠NUL字符来判断其长度。对于有些数组则没法判断。
声明一个指向数组的指针和指针数组、数组指针是不一样的。指向数组的指针是一个指针指向数组下标为0的元素,指针数组是一个元素为指针的数组,数组指针是数组类型的指针。
int vector[2] = {1,2}; int* pv1 = vector;//指向数组的指针 int* pv2[2];//指针数组,能够用一个循环来为每一个指针分配内存 int (*pv3)[2];//数组类型的指针
如今再来区分一下数组表示法和指针表示法在指针数组的应用。
int* array[5]; array[0] = (int*) malloc (sizeof(int)); *array[0] = 100; *(array+1) = (int*) malloc (sizeof(int)); **(array+1) = 200;
在这里array[1]和*(array+1)是等价的,实际都是指针类型,使用malloc函数为指针在堆上分配内存,解引指针来为数据赋值,所以**(array+1)其实不难理解,一个*解引获得一个指针,对指针再解引才获得数据,而[]括号前面已经 解释过,就是至关于一个取地址和加索引的操做。固然,还可使用array[0][0]来代替*array[0]。
能够将多维数组的一部分看作子数组,好比二维数组的每一行当作一个一维数组。数组按行-列顺序存储,第二行的第一个元素的内存地址紧跟在第一行最后一个元素后面。
int matrix[2][3] = {{1,2,3},{4,5,6}}; int (*pmat)[3] = matrix;//3是二维数组的列数 printf("size of pmat[0] is %d\n", sizeof(pmat[0])); //size of pmat[0] is 12
能够看到,该数组指针的第一个元素的长度是12个字节,也就是说是第一行的长度。若是要访问第一行第一个元素,须要用pmat[0][0]来访问。array[i][j]等于 array+i*sizeof(row) + j* sizeof(element)。sizeof(row)是一行的总大小,sizeof(element)是单个元素的大小,array是array[0][0]的地址。
给函数传递数组参数时,要考虑如何传递数组的维数以及每一维度的大小。下面是两种传递二维数组的方法:
void display2DArray(int arr[][3], int rows){} void display2DArray(int (*arr)[3], int rows){}
两种方法都指定了行数。若是传递的数组维度超过了二维,除了一维的部分,须要指定其它维度的长度。若是传递一个array3D[3][2][4]的数组:
void display3DArray(int (*arr)[2][4], int rows){}
当使用malloc分别为二维数组的不一样子数组分配内存时,可能致使内存分配不连续的问题。使用块语句一次性初始化不会有这个问题。使用malloc来为二维数组分配连续的内存有两种策略。假设二维数组有三行四列。第一种一次性分配全部内存3*4*sizeof(element),而后使用的时候经过前面提到的如何访问array[i][j]的方法手动计算要访问的内存地址。第二种方法分为两步。第一步先使用malloc分配一块内存用来存放指向二维数组每一行的指针的指针。第二步为array[0][0]的指针分配全部内存,而后计算array[1][0]和array[2][0]的位置,分别给这两个关键指针赋值,这样就能够根据每一行的指针来使用下标访问了。
int rows = 3; int columns = 5; //第一种方法 int* matrixx = (int*) malloc (rows * columns * sizeof(int)); //第二种方法 int **matrixy = (int**) malloc (rows * sizeof(int*)); matrixy[0] = (int*) malloc (rows * columns * sizeof(int)); int i = 1; while(i<rows) { i++; matrixy[i] = matrix[0] + i * columns; }
不规则数组是每一行的列数不同的二维数组。可使用复合字面量来初始化不规则数组。
(const int) {100} (int [3]) {10, 20, 30}
不规则数组的访问和维护都比较麻烦,使用前应慎重考虑。