数组与指针

C语言中数组和指针经常会放在一起讲,因为对于数组遍历的时候,可以通过指针的移动来代替。所以,数组和指针的关系有时候会让人很模糊,甚至让人混淆。本篇博文将试图说明数组的特性,指针的特性,以及数组和指针的联系与区别。

一维数组

一维数组初始化

float candy[365], char code[12], int states[50], int powers[8] = {1, 2, 4, 6, 8, 16, 32, 64}。若使用const关键字,则数组为只读数组,不能被改变。
特殊说明:

  • 若数组未初始化(如前三种实例),则数组中的值可为任意值。int some_data[4] = {1492, 1066},此种情况,若只部分初始化,则其余值设置为0。
  • 声明数组时省略数组大小,const int days[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31},编译器自动确定数组大小,为10。
  • C99新特性:指定初始化。int arr[6] = {[5] = 212},初始化arr[5] = 212,其他为0。

二维数组

float rain[5][12]; 二维数组,也可以说是有12个float的一维数组。声明方式就是指定行和列大小。

数组与指针

若flizeny为一维数组名,flizny == &flizny[0]成立,说明一维数组名为数组首元素的地址。并且,filzny为常量,可以进行加减运算,但不能进行自加或自减(变量才能进行)。但数组名可以赋值给指针变量,从而指针变量可以进行各种运算操作。

#include<stdio.h>
#define SIZE 4
int main(void)
{
    short dates [SIZE];
    short * pti;
    short index;
    double bills[SIZE];
    double * ptf;

    pti = dates;
    ptf = bills;
    printf("%23s %15s\n", "short", "double");
    for (index = 0; index < SIZE; index++)
    {
        printf("pointers + %d: %10p %10p\n", index, pti + index, ptf + index);
    }

    return 0;
}

上例中在for循环中可以使用数组名进行代替。由于数组名即为地址,则可以进行诸如类似指针的运算,如*(arr + 1),取数组第二个元素。

函数,数组,指针

指针变量和数组都可以作为函数参数类型。但若在函数中进行数组的遍历操作,则应指明数组长度,如int sum(int * ar, int n)或int sum(int ar[], int n),否则产生数组越界的问题。第二种方式更直观,一看就知道是数组操作。
在函数声明时,以下4种方式都可以。
int sum(int *ar, int n);
int sum(int *, int n);
int sum(int ar[], int n);
int sum(int [], int);

使用指针变量作为函数参数

除了指定遍历长度,也可以使用头指针和尾指针的方式:int sump(int * start, int * end);在函数内进行start < end; start++,以避免数组越界的问题。

指针操作

  • 赋值:将一个地址赋值给指针变量。
  • 获取指针变量指向地址的值,如*ptr。
  • 获取指针的地址,如&ptr,若用ptt = &ptr,则ptt为指向指针的指针。
  • 指针+/- integer:指针移位操作。
  • 自增自减操作
  • 两个指针变量做差值,得到地址差值,然后除以字节数(相应的数据类型)。
  • 关系运算进行比较。

获取未初始化指针所指向的值

实例:int * pt; *pt = 5(严重错误)。原因:将pt指向的地址保存的值赋值为5,但pt未初始化,只有一个随机的值,可能指向内存中的任意位置,即有可能覆盖系统重要数据,造成数据覆盖,甚至程序崩溃。

函数参数中使用const

int sum(const int ar[], int n); 在函数体内不允许通过ar改变数组内的数据,但实际传入的函数参数不一定为常量数组。使用const以保护数组内的数据不发生改变。
常量可以使用#define定义,但const可以定义诸如常量数组,常量指针,指针常量等。

常量指针

double rates[5] = {1.1, 2.2, 3.3, 4.4, 5.5}; const double *pd = rates;
数组为普通数组,指针为常量指针,不能通过指针来修改指针指向的内容。*pd = 29.89; // not allowed,pd[2] = 222.22; // not allowed,rates[0] = 99.99; // allowed because rates is not const。
无论是否使用指针表示法或数组表示法,总是不允许使用pt(常量指针)来改变指针指向的数据的值。

规则

常量数据和非常量数据的地址都可以赋值给常量指针。
double rates[5] = {1.1, 2.2, 3.3, 4.4, 5.5};
const double locked[4] = {0.08, 0.075, 0.0725, 0.07};
const double *pd = rates; // valid
pc = locked; // valid
pc = &rates[3];

然而,只有非常量数据的地址可以赋值给一般指针变量。
double rates[5] = {1.1, 2.2, 3.3, 4.4, 5.5};
const double locked[4] = {0.08, 0.075, 0.0725, 0.07};
double *pnc = rates; // valid
pnc = locked; // not valid
pnc = &rates[3]; // valid
很好理解,如果pnc = locked被允许,那么可以使用pnc修改常量数组的值。

指针常量

const有更多可能的用途。 例如,您可以声明并初始化指针,以使其无法指向其他位置。
double rates[5] = {1.1, 2.2, 3.3, 4.4, 5.5};
double * const pc = rates; // pc 指向数组的首元素地址
pc = &rates[2]; // invalid,不允许指向其他位置
*pc = 92.99 // valid,可以通过指针修改指向的值。

指向常量的指针常量

double rates[5] = {1.1, 2.2, 3.3, 4.4, 5.5};
const double * const pc = rates;
pc = &rate[2]; // invalid
*pc = 92.99; // invalid
说明指针一旦初始化,则指针指向的地址不能改变,同时也不能通过指针修改其指向的值。

指针和多维数组

int zippo[4][2] = {{2, 4}, {6, 8}, {1, 3}, {5, 7}};

数据 描述
zippo the address of the first two-int element
zippo+2 the address of the third two-int element
*(zippo+2) the third element, a two-int array, hence the address of its first element, an int
*(zippo+2) + 1 the address of the second element of the two-int array, also an int
*(*(zippo+2) + 1) the value of the second int in the third row(zippo[2][1]

如何声明一个指向二维数组的指针呢?int (* pz)[2]; // pz 指向一个包含2个int类型的数组,数组每个元素为2个int型的数组。
int * pax[2]; // pax是一个数组指针,每个指针均为指向int型。

指针兼容性

将一个指针指定给另一个指针的规则比数字类型的规则更严格。例如,可以将int类型值赋值给double类型的变量,但不能将指向int型的指针变量赋值给指向double类型的指针变量。
int n = 5; double x; int * pl = &n; double *pd = &x; x= n(隐式类型转换); pd = pl(编译错误,不能转换);
这些限制扩展到更复杂的类型,以下例子可以说明。
int * pt; int (*pa)[3]; int ar1[2][3]; int ar2[3][2]; int **p2(指向指针的指针);
pt = &ar1[0][0]; // both pointer-to-int
pt = ar1[0]; // both pointer-to-int
pt = ar1; // not valid, ar1 is a array of three-int
pa = ar1; // both pointer-to-int[3]
pa = ar2; // not valid, pa is a array of three-int ar2 is a array of two-int
p2 = &pt // both pointer-to-int *
p2 = ar2; // not valid, p2 is a pointer-to-int *, ar2 is array of two-int

函数和多维数组

声明方式
void sum_row(int ar[][COLS], int rows);
void sum_cols(int [][COLS], int);
int sum2d(int (*ar)[COLS], int rows);

Variable-Length Arrays (VLAs)

C99新特性,int sum2d(int rows, int cols, int ar[row][cols]);
int sum2d(int, int, int ar[*][*]);
注意,行和列的int型应该放在数组参数的前面,使用VLA可以灵活使用可变行列的数组。

posted @ 2019-01-04 00:51  Jeffrey_Yang  阅读(224)  评论(0编辑  收藏  举报