物协 C Gateway Course 7 指针
一、指针的概念
1.1 变量和地址
在C语言中,变量在内存中存储,每个变量有一个地址,可以类比为酒店中的房间号。例如:
void main() {
int x = 10, y = 20;
}
这段代码中,变量x
和y
分别被赋值为10和20,可以将内存看作是酒店,每个变量就是一个房间。变量的地址可以暂时用px
和py
表示。
1.2 指针变量和指针的类型
指针变量存储着地址信息,就像一张房卡存储着房间号。指针变量的类型需要与所指向的变量类型匹配,类似于只能用特定类型的房卡打开相应类型的房间。
int *px; // int类型的指针变量
float *pf; // float类型的指针变量
char *pc; // char类型的指针变量
二、变量的指针与指针变量
2.1 指针变量的定义及使用
2.1.1 指针变量的定义
- 指针变量的定义形式如:
数据类型 *指针名;
// 分别定义了 int、float、char 类型的指针变量
int *x;
float *f;
char *ch;
- 在定义指针变量时,必须确定指针的类型,就像普通变量需要确定数据类型一样。
2.1.2 指针变量的使用
- 取地址运算符
&
: 用于取操作对象的地址。
int a;
int *pa; // 声明一个指针变量
pa = &a; // 通过取地址符&,获取 a 的地址,赋值给指针变量
- 指针运算符
*
(间接寻址符): 通过操作对象的地址,获取存储的内容。
// 通过间接寻址符,获取指针指向的内容
printf("%d", *pa);
2.1.3 “&” 和 “*” 的结合方向
&
和*
都是右结合的。
int x = 10;
int *px = &x;
// *&x 的含义是,先获取变量 x 的地址,再获取地址中的内容
// 因为 "&" 和 "*" 互为逆运算,所以 x = *&x
2.2 指针变量的初始化
2.2.1 指针变量与变量的关系
- 指针变量与变量的关系可以类比为房卡和房间的关系。
// 利用取地址获取 x 的地址,在指针变量 px 定义时,赋值给 px
int x;
int *px = &x;
// 定义指针变量,分别赋值“NULL”和“0”
int *p1 = NULL, *p2 = 0;
2.2.2 指针运算
2.2.2.1 赋值运算
- 指针变量可以相互赋值,也可以赋值某个变量的地址,或者赋值一个具体的地址。
int *px, *py, *pz, x = 10;
px = &x; // 赋予某个变量的地址
py = px; // 相互赋值
pz = 4000; // 赋值具体的地址
2.2.2.2 指针与整数的加减运算
- 指针变量的自增自减运算。
int x, y, z;
int *px = &x; // 定义一个指针,指向 x
printf("x = %d", *px); // 获取 x 的内容
printf("y = %d", *(px + 1)); // px + 1,指向 x 的下一个单元,即 y
printf("z = %d", *(px + 2)); // px + 2,指向 x 的下两个单元,即 z
2.2.2.3 关系运算
- 关系运算符用于比较指针的大小或与常量比较。
int num[2] = {1, 3};
int *px = &num[0], *py = &num[1];
int *pz = &num[0];
int *pn;
if (py > px) {
printf("py 指向的存储地址大于 px 所指向的存储地址");
}
if (pz == px) {
printf("px 和 pz 指向同一个地址");
}
if (pn == NULL || pn == 0) {
printf("pn 是一个空指针");
}
三、指针与数组
3.1指向数组的指针
- 数组名即为数组的首地址,通过指针可以访问数组的元素。
int nums[5] = {4, 5, 3, 2, 7};
int *p = nums; // 或者 p = &nums[0];
printf("nums[0] = %d\n", *p); // 输出结果为 nums[0] = 4
printf("nums[1] = %d\n", *(p + 1)); // 输出结果为 nums[1] = 5
for (int i = 0; i < 5; i++) {
printf("nums[%d] = %d", i, *(p + i));
}
3.2 字符指针与字符数组
- 字符指针和字符数组用于存储字符串。
char *sentence = "Do not go gentle into that good night!";
printf("%s", sentence); // 输出整个字符串
printf("%c", sentence[0]); // 获取字符串的第一个字符
printf("%d", strlen(sentence)); // 获取字符串的长度
- 利用字符指针将字符数组内容复制到字符数组。
char sentence[] = "Do not go gentle into that good night!", word[100];
char *ch = word;
for (int i = 0; sentence[i] != '\0'; i++) {
*(ch + i) = sentence[i];
}
*(ch + i) = '\0';
printf("ch = %s, word = %s", ch, word);
3.3 多级指针及指针数组
3.3.1 多级指针
- 多级指针是指向指针的指针,形成多级关系。
int *pi, i = 10;
int **ppi
;
pi = &i;
ppi = π
printf("i = %d", **ppi); // 获取 i 的内容
3.3.2 指针数组
- 指针数组是由指针变量组成的数组,可以操作多个数组。
int nums[5] = {2, 3, 4, 5, 2};
int *p[5];
int **pp;
for (int i = 0; i < 5; i++) {
p[i] = &nums[i];
}
pp = p;
for (int i = 0; i < 5; i++) {
printf("%d", **pp);
pp++;
}
3.4 指针与多维数组
3.4.1 多维数组的地址
- 多维数组可以看成一维数组的数组,不同维度的关系需要逐级分析。
int nums[2][2] = {
{1, 2},
{2, 3}
};
printf("数组首地址为 %d", nums); // 数组首地址
printf("第二个维度的首地址为 %d", nums[0]); // 第二个维度的首地址
printf("第三个维度的首地址为 %d", nums[0][0]); // 第三个维度的首地址
3.4.2 多维数组的指针
- 利用指针数组可以操作多维数组。
int nums[2][2] = {
{1, 2},
{2, 3}
};
int *p[2] = {nums[0], nums[1]};
printf("nums[0][0] = %d", **p);
printf("nums[1][0] = %d", **(p + 1));
printf("nums[0][1] = %d", *(*p + 1));
3.5总结与注意事项
- 指针变量是一种特殊的变量,存储的是地址而非具体的值。
- 指针和数组有密切关系,通过指针可以操作数组的元素。
- 多级指针和指针数组可以用于处理复杂的数据结构和多维数组。
注意:指针变量在使用前必须初始化,未初始化的指针可能导致未知的错误。
四、指针与函数
在C语言中,函数参数可以是int
、char
、float
等基本数据类型,但通过指针,我们可以实现更灵活的函数参数传递。本节将学习指针作为函数参数、函数返回值以及指向函数的指针。
4.1 函数参数为指针
考虑一个简单的交换两个变量值的函数:
void swap(int *x, int *y);
int main() {
int x = 20, y = 10;
swap(&x, &y);
printf("x = %d, y = %d", x ,y);
return 0;
}
void swap(int *x, int *y) {
int t;
t = *x;
*x = *y;
*y = t;
}
在这个例子中,swap
函数的参数是指针,通过传递变量的地址,实现了变量值的交换。
4.2 函数的返回值为指针
函数可以返回指针,从而实现返回值为数组的效果:
int s;
int *sum(int x, int y) {
s = x + y;
return &s;
}
int main() {
int *result = sum(10, 9);
printf("10 + 9 + %d", *result);
return 0;
}
这里,sum
函数返回一个指向s
的指针,实现了返回值为数组的目的。
4.3 指向函数的指针
虽然C语言中函数不能嵌套定义,也不能将函数作为参数传递,但是函数有一个特性,即函数名为该函数的入口地址。我们可以定义一个指针指向该地址,将指针作为参数传递。
#include <string.h>
void check(char *x, char *y, int (*p)());
int main() {
// string.h库中的函数,使用之前需要声明该函数。字符串比较函数
int strcmp();
char x[] = "Zack";
char y[] = "Rudy";
// 定义一个函数指针
int (*p)() = strcmp;
check(x, y, p);
return 0;
}
void check(char *x, char *y, int (*p)()) {
if (!(*p)(x, y)) {
printf("相等");
} else {
printf("不相等");
}
}
在这个例子中,p
是一个函数指针,指向strcmp
函数。通过check
函数将函数指针作为参数传递,实现了对字符串的比较。
函数指针的定义形式为数据类型 (*函数指针名)();
,在进行“*”操作时,可以理解为执行该函数。需要注意,函数指针不同于数据指针,不能进行+整数操作。