指针与数组

指针与地址

指针是一种保存变量地址的变量。

&可用于取一个对象地址。此运算符只能应用于内存中的对象,不能作用于表达式、常量或register声明的变量。

int x = 1;
int *p = &x; // p指向x

int z[10];
p = &z[0]; // p指向z[0]
1
2
3
4
5

*是间接寻址运算符。当它作用于指针时,将访问指针所指向的对象。

int x = 1, y = 2;
int *p = &x; // p指向整型x
y = *p; // y的值现在为1
*p = 0; // x的值现在为0
1
2
3
4

指针只能指向某种特定类型的对象。void*例外,它能指向任何类型的指针,但它不能间接引用其自身。

指针与函数参数

C语言是以传值的方式将参数值传递给被调用函数。因此,被调用函数不能直接修改主调函数中变量的值。如:

// swap函数的本意是交换两个传参的值,但显然以下函数达不到目的。
void swap(int a, int b) {
    int temp = a;
    a = b;
    b = temp;
}
1
2
3
4
5
6
// 将形参类型改为int*
void swap(int* a, int* b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}
1
2
3
4
5
6

指针与数组

通过数组下标所能完成的任何操作都可以通过指针来实现。

int a[10];
int *p;
p = &a[0]; // p指向数组a的第0个元素
int x = *(p+1); // x的值为a[1]的值
1
2
3
4

如果指针p指向数组中的某个特定元素,那么p+1将指向下一个元素,p+i将指向之后的第i个元素,而p-i将指向之前的第i个元素。(注意数组的越界问题)

数组名就是一个数组第一个元素的地址。于是,在函数定义中,形参char s[]char *s是等价的,只要把数组名传递过去即可。

/* 返回字符数组s的长度(不算结尾的0字符) */
int strlen(char *s) {
    int n;
    for(n = 0; *s != '\0'; s++) {
        n++;
    }
    return n;
}
1
2
3
4
5
6
7
8

地址算术运算

可将指针p赋值给同类型的指针q。

指针可以和整数进行加减运算。如:p是一个指向数组中某个元素的指针,那么p++将对p进行自增运算并指向下一个元素,p+=i将对p进行加i的增量运算,并使其指向之后的第i个元素,p--p-=i同理。

如果指针p和q指向同一个数组的成员,那么它们之间就可以进行类似于==!=<>等关系比较运算

指针间的减法运算也是有意义的:如果p和q指向同一个数组的成员,且p<q,那么q-p+1就是p和q之间的元素数目。但指针间的加法是非法操作。

指针值为0表示无效的指针。

综上所述,有效的指针运算包括:

  • 相同类型指针之间的赋值运算。

  • 指针和整数之间的加减运算。

  • 指向相同数组中元素的两个指针之间的减法或比较运算。

  • 将指针赋值为0,指针与0之间的比较运算。

字符指针与函数

字符串常量是一个以'\0'结尾的字符数组。

字符串常量可通过一个指向其第一个元素的指针访问。

char *p = "I am daking.";
1

指针数组和指向指针的指针

由于指针本身也是变量,所以它们也可以像其他变量一样存储在数组中。

int *arr[NUM]; // 长度为NUM的数组arr,元素类型为int指针。
1

二维数组

二维数组实际上是一种特殊的一维数组,它的每个元素也是一个一维数组。

char double_dimen_arr[ROW][COL];
1

二维数组可以不指定ROW,只指定COL。

char double_dimen_arr[][COL] = {"daking", "vichy"};
1

数组可用花括号括起来的初值表进行初始化,二维数组的每一个行由相应的子列表进行初始化。

int daytab[2][3] = {
    {1, 2, 3},
    {4, 5, 6}
};
1
2
3
4

因为[]的优先级高于*,所以以下两种声明有所区别。

int *arr[13]; // 长度为13的数组,元素类型为int指针。
int (*p)[13]; // 一个指针,指向具有长度为13的整型数组。
1
2

指针数组的初始化

char *name[] = {
    "daking", "vichy"
};
char *first_name = name[0];
char *second_name = name[1];
1
2
3
4
5

指针与多维数组

比较int a[10][20]int *b[10]

  • a是一个真正的二维数组,它分配了10*20个int类型长度的存储空间。可通过常规的矩阵下标计算公式20*row+col计算出元素a[row][col]的位置。a的每个元素都指向一个具有20个元素的数组。

  • b仅仅分配了10个int类型的指针,且没有对它们初始化。b的每个元素不必都指向一个具有20个元素的向量,某些元素可指向只具有2个元素的向量,某些元素可指向具有50个元素的向量,甚至某些元素可不指向任何向量。

指向函数的指针

函数本身不是变量,但可以定义指向函数的指针。

指向函数的指针的声明格式如下:

返回值类型 (*指针名)(参数类型列表)
1
// comp是一个指向函数的指针,该函数有两个void*类型的参数,返回值类型为int。
int (*comp)(int *, int *)
1
2

指向函数的指针的使用:

// *comp代表一个函数,下面是一个函数的调用。
(*comp)(&x, &y)
1
2

这种类型的指针可以被赋值、存放在数组中、传递给函数以及作为函数的返回值等。