跳转至

『C语言教程』5. 指针

T18:42:00+08:00

指针是一种特殊的变量类型,有 int* double* 等类型。

声明指针变量:

C
int *a; // 声明一个 int 的指针变量,名为 a

int* aint * a 都可,随意空格

一、指针变量可以存一个变量地址(指针的第一句话)

C
1
2
3
int x = 1;

int *p = &x; // 声明一个 int 的指针变量,名为p。并将x的地址赋给p

& 在此是取址运算符,&varriable 可以得到 varriable 变量的地址。

C
printf("%d\n", p);

如果我们使用 %d 输出 p 的值(p的值是一个地址),它会输出地址对应的整数。

在我的电脑上编译运行得到:6487572

二、指针变量的特殊操作 * (指针的第二句话)

使用 *p 可以访问指针变量 p 所存地址对应的变量。

C
1
2
3
4
5
6
7
8
9
int x = 1;

int *p = &x;

printf("%d %d\n", x, *p);
x = 5;
printf("%d %d\n", x, *p);
*p = 1;
printf("%d %d\n", x, *p);

输出:

C
1
2
3
1 1
5 5
1 1

三、一些例子

3.1 交换两个数的函数

C
1
2
3
4
5
int swap(int a, int b) {
    int t = a;
    a = b;
    b = t;
}
C
1
2
3
4
5
6
7
8
9
int main() {
    int x = 1, y =2;

    printf("%d %d\n", x, y);
    swap(x, y);
    printf("%d %d\n", x, y);

    return 0;
}

输出:

C
1 2
1 2

在使用 swap(x, y) 调用 swap 函数的时候,是将 xy 的值取出来,分别交给 a b

也就是说 a bx y 是不同的变量。

我们可以通过输出其地址来判断是否是同一个变量:

  1. swap 中输出 ab 的地址:
C
printf("In swap: %d %d\n", &a, &b);
  1. main 中输出 xy 的地址:
C
printf("In main: %d %d\n", &x, &y);

在我的电脑上编译运行得到:

In main: 6487580 6487576

In swap: 6487536 6487544

所以 a bx y 并不是相同的变量,只是在调用的时候将 x y 的值赋给了 a b 而已。

a b 的操作就只是对 a b 操作,而不是对 x y 操作。

所以上述函数并不能交换 x y 的值,只是将参数变量 a b 的值交换了。

那么我们可以使用指针来实现交换两个变量:

C
1
2
3
4
5
int swap(int *a, int *b) {
    int t = *a;
    *a = *b;
    *b = t;
}
C
1
2
3
4
5
int main() {
    int x = 1, y = 2;
    swap(&x, &y);
    return 0;
}

函数的参数为指针变量,所以在调用的时候要对应地传入变量地址。

在这个例子中传入了 xy 的地址,也就是将 x y 地址的值赋给了 ab 指针变量。(指针的第一句话)

随后我们使用的 *a *b 就分别是访问 a b 所存地址对应的变量,也就是想到于对原本的 x y 进行操作。(指针的第二句话)

这样就可以达到交换 x y 变量的值了。

喂,前面可是深渊啊

一、在你声明一个数组的时候发生了什么

C
char arr[4];
  1. 第一步 —— 在内存中申请一片空间

    C
    [1Byte] [1Byte] [1Byte] [1Byte]
    
  2. 声明一个常量指针并令其指向那片空间的起始位置

    C
    1
    2
    3
    4
    5
    地址A
    
    [1Byte] [1Byte] [1Byte] [1Byte]
    
    const char *arr = 地址A;
    

这就是我们所说的“直接使用一个数组,其实是指向首元素的指针”。

二、指针与数组

对于一个声明好的数组:

C
int arr[4] = {0, 1, 2, 3};

之前提到,arr 是指向数组起始位置的指针,为 int * 类型。

我们所说的使用指针来访问数组:

C
int *ptr = arr;

其实是将常量指针 arr 的值赋给了 ptr,其实就是把 arr 数组起始位置的地址赋给了 ptr。 那么之后的操作就一模一样了。

ptr 相当于 arr 相当于 &arr[0] ptr + 1 相当于 arr + 1 相当于 &arr[1] ptr + 2 相当于 arr + 2 相当于 &arr[2]

ptr[0] 相当于 arr[0] 相当于 *(arr) ptr[1] 相当于 arr[1] 相当于 *(arr + 1)

不同就是,ptr 的值还可以更改,但是 arr 是固定的指向那个数组其实位置的指针:

C
int arr2[] = {3, 2, 1, 0};
ptr = arr2;

同样的,再来看字符串

C
1
2
3
int arr[2][3];
int (*pt)[3];
int *pt[3];

arrpt 均为 int (*)[3] 类型 如果把类型扣出来声明,其实是这样:

C
1
2
3
4
5
(int [3]) arr[2];
      (int [3])[2] arr;
(int [3]) *arr;
(int*)    arr[3];
      (int*)[3] arr;
C
int (*pt)[3]; // int (*)[3] 类型,是指向 int [3] 类型的指针
int *pt[3];   // int (*)*   类型,是指向 int *   类型的指针

评论