C/C++ 指针

 

概念

指针是定义, C/C++中每个变量在内存里都有唯一地址, 该地址也称为指针

指针变量是指针具体实现, 其存储其他变量内存地址, 并可通过变量地址间接访问修改变量

操作

定义

int *p;
char *p;
double *p;

指针变量是变量, 值是所存储变量在内存中地址

取地址

使用&运算符可获取变量地址, 将地址值赋值给指针变量完成初始化

  • 示例, 获取变量地址, 初始化指针变量
graph LR;
    subgraph S1[int a]
        direction LR
        V2(value: 0xFF)-->A2(address: 0x7fff6be9543c)
    end
    subgraph S2[int* p]
        direction LR
        V1(value: 0x7fff6be9543c)
    end
    A2--"&"-->S2
#include <stdio.h>

int main() {
    int a = 0xFF;
    int *p = &a;
    printf("a address: %p\n", &a);
    printf("p value: %p\n", p);
    return 0;
}

解引用

使用*运算符可解析该地址对应变量值

  • 示例, 获取指针存储变量地址对应值
graph LR;
    subgraph S1[int a]
        direction LR
        V2(value: 0xFF)
        A2(address: 0x7fff6be9543c)
        A2-->V2
    end
    subgraph S2[int* p]
        direction LR
        V1(value: 0x7fff6be9543c)
    end
    S2--"\*"-->A2
#include <stdio.h>

int main() {
    int a = 0xFF;
    int *p = &a;
    printf("a value: %d\na address: %p\n", a, &a);
    printf("p value: %p\n*p value: %d\n", p, *p);
    return 0;
}

属性

指针指向(指针值)

指针值是其所存储变量地址, 也称指针指向该变量

  • 示例, 初始化指针指向
#include <stdio.h>

int main() {
    int a = 10;
    // 等价 int *p; p = &a;
    int *p = &a;
    printf("a = %d, &a = %p\n", a, &a);
    printf("*p = %d, p = %p\n", *p, p);

    return 0;
}
graph LR;
    subgraph S1[int a]
        direction LR
        V2(value: 10)
        A2(address: 0x7fffed97d56c)
    end
    subgraph S2[int* p]
        direction LR
        V1(value: 0x7fffed97d56c)
    end
    V1-->A2

改变指针指向

改变指针值存储新变量地址, 也称改变指针指向

  • 示例, 改变指针值

指针变量p原本存储变量a地址, 现让其存储变量b地址, 改变指针指向

#include <stdio.h>

int main() {
    int a = 0xFF;
    int* p = &a;
    printf("p = %p, *p = %d\n", p, *p);

    int b = 0xAA;
    p = &b;
    printf("p = %p, *p = %d\n", p, *p);
    return 0;
}
graph LR;
    subgraph S1[int a]
        direction LR
        V2(value: 0xFF)
        A2(address: 0x7ffe3ee184b8)
    end
    subgraph S2[int* p]
        V1(value: 0x7ffe3ee184b8)
    end
    V1--->A2
    
graph LR;
    subgraph S3[int b]
        direction LR
        V3(value: 0xAA)
        A3(address: 0x7ffe3ee184bc)
    end
    subgraph S2[int* p]
        V1(value: 0x7ffe3ee184bc)
    end
    V1-->A3

改变指针指向值(间接修改变量)

通过指针所存储变量地址, 可间接操作修改变量值

  • 示例, 间接修改

指针p存储变量a地址, 通过地址间接修改变量a

#include <stdio.h>

int main() {
    int a = 0xFF;
    int* p = &a;
    printf("a = %d, &a = %p\n",a, &a);
    printf("p = %p, *p = %d\n", p, *p);

    *p = 0xAA;
    printf("p = %p, *p = %d\n", p, *p);
    printf("a = %d, &a = %p\n",a, &a);

    return 0;
}
graph LR;
    subgraph S1[int a]
        direction LR
        V2(value: 0xFF)
        A2(address: 0x7ffd85a743bc)
    end
    subgraph S2[int* p]
        V1(value: 0x7ffd85a743bc)
    end
    V1-->A2
graph LR;
    subgraph S1[int a]
        direction LR;
        V2(value: 0xAA)
        A2(address: 0x7ffd85a743bc)
    end
    subgraph S2[int* p]
        V1(value: 0x7ffd85a743bc)
    end
    V1-->A2==>V2

类型

指针类型

指针变量去掉变量名后是指针类型

指针变量 指针类型
int *p1 int *
char *p2 char *
double **p3 double **

指针变量大小只与系统位数有关, 与类型无关, 因为不论何类型指针都是存储地址值

32位系统指针大小为4字节, 64位系统指针大小位8字节

#include <stdio.h>

int main() {
    int* p = NULL;
    double* p1 = NULL;
    printf("sizeof p = %d\nsizeof p1 = %d\n", sizeof(p), sizeof(p1));
    return 0;
}

指针指向类型

指针变量去掉* 变量名表示指向类型

指针变量 指针指向类型
int *p1 int
char *p2 char
double **p3 double *

指向区域大小

// 指针指向4字节大小区域
int a = 3;
int *p = &a;

// 指针指向1024 字节 大小区域
const int SIZE = 1024;
char *p = (char *)malloc(sizeof(char) * SIZE); 

特殊指针

常量指针

常量指针是指针

指向常量(存储常量地址), 指向可变(可存储其他变量), 指向值不可变(不能通过地址间接修改变量值)

const int *p;

指针类型 int *, 指针指向类型 const int

graph LR;
    subgraph S1[const int a]
    direction LR
        V[value: 不可变]
        A[address]

    end
    subgraph S2[const int* p]
        V2[value]
        direction LR
    end
    S2-->A==>V
  • 示例, 尝试改变常量指针指向和指向值
#include <stdio.h>

int main(void) {
    const int a = -1;
    const int b = 1;
    const int *p = &a;
    printf("%d\n", *p);

    // 尝试改变指针指向
    p = &b;
    printf("%d\n", *p);

    // 尝试改变指针指向值, 报错
    *p = -1;
    printf("%d\n", *p);
    return 0;
}

指针常量

指针常量是常量

指针本身是常量(指针是常量类型), 指向不可变(不能再存储其他变量地址), 指向值可变(可通过地址间接修改变量值)

int *const p;

指针类型 int *const, 指针指向类型 int

graph LR;
    subgraph S1[int a]
        V[value]
        A[address]
    end
    subgraph S2[int *const p]
        direction LR
        V2[value: 不可变]
    end
    S2==绑定==>A
  • 示例, 修改指针常量
#include <stdio.h>

int main(void) {
    int a = -1;
    int *const p = &a;
    printf("%d\n", *p);
    // 尝试改变指针指向值
    *p = 1;
    printf("%d\n", *p);
    
    int b = 1;
    // 尝试改变指针指向, 报错
    p = &b;
    printf("%d\n", *p);
    return 0;
}

函数指针

指向函数(存储函数地址), 可间接调用函数

返回值 (*)(参数...,)
  • 示例, 通过指针间接调用函数
#include <stdio.h>

int GetMax(int x, int y) {
    return x > y ? x : y;
}

int main() {
    int (*p)(int, int) = NULL;
    p = GetMax;
    printf("%d\n", p(1, 2));
    return 0;
}

指针数组

指针数组是数组, 数组中元素为指针

int *p[3];

指针类型 int *, 指针指向类型 int

  • 示例, 指针数组使用
#include <stdio.h>

int main(void) {
    int *p[3];
    int a = 1;
    int b = 2;
    int c = 3;
    p[0] = &a;
    p[1] = &b;
    p[2] = &c;
    for (int i = 0; i < 3; i++) {
        printf("p[%d] = %d, &p[%d] = %p\n", i, *p[i], i, &p[i]);
    }
    return 0;
}

对象指针

指针指向一个对象(存储对象地址)

二级指针

二级指针指向指针, 其值是另一个指针地址

类型 **名称;
  • 示例, 二级指针使用
graph LR;
    subgraph S1[int a]
        direction LR
        V1(value: 0xFF)
        A1(address: 0x7ffeb5102ea4)
    end
    subgraph S2[int* p]
        direction LR
        V2(value: 0x7ffeb5102ea4)
        A2(address: 0x7ffeb5102ea8)
    end
    subgraph S3[int** sp]
        direction LR
        V3(value: 0x7ffeb5102ea8)
    end
    V3-->A2
    V2-->A1
#include <stdio.h>

int main() {
    int a = 0xFF;
    int *p = &a;
    int **sp = &p;
    printf("a: %d, &a: %p\n", a, &a);
    printf("*p: %d, p: %p, &p: %p\n", *p, p, &p);
    printf("**sp: %d, *sp: %p, sp: %p\n", **sp, *sp, sp);
    return 0;
}

数组指针

数组指针是指针, 指针指向数组(存储数组首元素地址)

int (*p)[3];

指针类型 int *, 指针指向类型 int [3]

  • 示例, 数组指针使用
graph LR;
    subgraph S1["int a[]"]
        subgraph A1[a0]
            V0["value"]
            A0["address"]
        end

        subgraph A2[a1]
            V12["value"]
            A12["address"]
        end

        A0-->A12
    end
    subgraph S2["int (*p)[]"]
        V2[value]
        direction LR
    end
    S2==>A0
#include <stdio.h>

int main(void) {
    int a[3] = {1, 2, 3};
    int (*p)[3] = &a;

    for (int i = 0; i < 3; i++) {
        printf("&a[%d] = %p, a[%d] = %d\n", i, &a[i], i, a[i]);
    }
    for (int i = 0; i < 3; i++) {
        printf("(*p + %d) = %p, *(*p + %d) = %d\n", i, (*p + i), i,*(*p + i));
    }
    return 0;
}