Skip to content

9 、指针


一、基本🧀

1
2
3
int     -4byte
char    -1byte
float   -4byte

定义:储存其他变量 地址 的变量

1、写法🧀

  • 假如a=5,地址在204\(p\) 是指向 \(a\) 的指针,地址是 \(64\)
int main() {
    int a = 5;
    int *p;
    p = &a;

    printf("p    = %p\n", p);      // 输出指针 p 的值(即 a 的地址)    - 204
    printf("&a   = %p\n", &a);     // a 的地址,和 p 的值一样          - 204
    printf("&p   = %p\n", &p);     // p 自己的地址(不同于 a 的地址)    - 64
    printf("*p   = %d\n", *p);     // 解引用 p,等价于 a 的值          - 5 

    *p = 8;                        // 通过指针修改 a 的值
    printf("a    = %d\n", a);      // 输出 a,值已变为 8

    return 0;
}

Note

p → 地址

*p → 储存在这个地址的变量

指针定义
1
2
3
4
5
6
7
8
int main() {
    int a = 10;
    int *p;
    p = &a;
    printf("%d\n", p);
    printf("%d\n", *p);
    printf("%d\n", &a);
}

/ -551552636 10 -551552636 /

```c++
int main() {
    int a = 10;
   int *p;
    p = &a;
    printf("%d\n", p);
    printf("%d\n", *p);
    printf("%d\n", &a);
    int b = 20;
    *p = b; // 此处p仍然指向a,所以修改a的值变成b了
}

指针大小
1
2
3
4
5
6
7
int main() {
    int a = 10;
    int *p;
    p = &a;
    printf("%d\n", p);
    printf("%d\n", p + 1);
}

/ 两次输出相差4,也就是1个int的大小 / ```


二、指针类型🧀

1、一般指针🧀

Warning

  • 指针强调类型,因为c中数据类型大小不同
int*   int
char*  char
int main() {
    int a = 1025;
    int *p;
    p = &a;
    char *p0;
    p0 = (char *)p; // 类型转换
    printf("Address of p0 = %d\n", p0);
    printf("Address of p = %d\n", p);
    printf("Value of p = %d\n", *p);
    printf("Value of p0 = %d\n", *p0);
}

/*
Address of p0 = 1210055916
Address of p = 1210055916
Value of p = 1025
Value of p0 = 1    1025 存为四个字节 最后一个字节是1 取完地址 由于char类型大小1byte 只取最后一个字节的值 即1
*/

2、void 指针🧀

不强调类型,但不能解指针,也不能做算术运算

3、指向指针的指针🧀

int main() {
    int x = 5;
    int *p = &x;
    *p = 6;
    int **q = &p;
    int ***r = &q;
    printf("%d\n", *p);    // x的值
    printf("%d\n", *q);    // p的值
    printf("%d\n", *(*q)); // 相当于*p ->6
    /*
    ***r,**q,*p,可以修改x的值,等价
    */
}

三、函数传值/传引用🧀

Failure

// 错误做法
void increment(int a) { // 形参:被调用函数中的变量
    a += 1;
    cout << "Address of a is" << &a << endl;
}

int main() {
    int a = 10;
    increment(a); // 实参:调用函数中的变量
    cout << "Address of a is" << &a;
}
void increment(int *p) {
    *p += 1;
}

int main() {
    int a = 10;
    increment(&a);
    cout << a << endl;
}

/*
函数先解地址,然后再这个变量上+1
*/

四、数组指针🧀

1、取值/取地址🧀

取地址 两种

&a[i] // 直接取地址
(a+i) // 数组自身即为指针,取地址

取值 两种

a[i]
*(a+i)
1
2
3
4
5
6
7
int main() {
    int a[] = {2, 4, 5, 6, 8};
    cout << a << endl;      // 取第一个的地址
    cout << &a << endl;     // 取地址
    cout << a[0] << endl;   // 取第一个的值
    cout << *a << endl;     // 也是取第一个的值
}

2、数组作为参数🧀

int SumOfELem(int a[], int size) {
    int sum = 0;
    for (int i = 0; i < size; i++) {
        sum += a[i];
    }
    return sum;
}

int main() {
    int a[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
    int size = sizeof(a) / sizeof(a[0]);
    int total = SumOfELem(a, size);
    printf("The sum of the elements is : %d", total);
}

如果改写成如下,则不能正常工作

int SumOfELem(int a[]) { // 等价于int* a
    int sum = 0;
    int size = sizeof(a) / sizeof(a[0]);
    for (int i = 0; i < size; i++) {
        sum += a[i];
    }
    return sum;
}

int main() {
    int a[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
    int total = SumOfELem(a);
    printf("The sum of the elements is : %d", total);
}

Tip

在函数中调用数组,编译器只会复制数组首个元素的地址,而不是整个数组

void Double(int *a, int size) {
    for (int i = 0; i < size; i++) {
        a[i] *= 2;
    }
}

int main() {
    int a[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
    int size = sizeof(a) / sizeof(a[0]);
    Double(a, size);
    for (int i = 0; i < size; i++) {
        cout << a[i] << " ";
    }
}

五、指针和字符串数组🧀

储存字符串🧀

\[ \text{size of array}>=\text{num of characters} \]

最后一个是\0

1
2
3
4
5
6
7
8
9
int main() {
    char C[20];
    C[0] = 'J';
    C[1] = 'I';
    C[2] = 'L';
    C[3] = 'L';
    C[4] = '\0';
    printf("%s", C);
}

Note

  • 指针和数组是不同的类型,但是用起来类似
  • 数组传给函数,永远传的是引用
void print(char *C) {
    int i = 0;
    while (*(C + i) != '\0') {
        cout << C[i];
        i++;
    }
    cout << endl;
}

void print(char *C) {
    int i = 0;
    while (*C != '\0') { // 也可以这样写
        cout << C[i];
        C++; // 自增加一个char类型大小
    }
    cout << endl;
}

int main() {
    char C[20] = "Hello";
    print(C);
}
void print(const char *C) {
    int i = 0;
    while (*C != '\0') {
        cout << C[i];
        C++;
    }
    cout << endl;
}

int main() {
    char *C = "Hello";
    print(C);
}

六、多维数组🧀

1、二维🧀

int B[2][3];
int (*p)[3] = B;
1
2
3
4
5
B[0] // B[0][0],B[0][1],B[0][2]
B[1] // B[1][0],B[1][1],B[1][2]
/*
这两个都是有三个整数的一维数组
*/
#include <stdio.h>
int main() {
    int B[3][4] = {
        {1, 2, 3, 4},
        {5, 6, 7, 8},
        {9, 10, 11, 12}};

    printf("B        = %p\n", B);        // 指向 B[0],等价于 &B[0]
    printf("*B       = %p\n", *B);       // B[0],等价于 &B[0][0]
    printf("B[0]     = %p\n", B[0]);     // B[0],等价于 &B[0][0]
    printf("&B[0][0] = %p\n", &B[0][0]); // 明确写出 B[0][0] 的地址

    return 0;
}

// 结果是一样的 都是指向B[0]的地址,即B[0][0]的地址
B[x] + y = &B[x][y]
B[i][j] = *( B[i] + j ) = *( *( B + i ) + j )

2、三维🧀

1
2
3
int c[3][2][2];
// 拆解 三个二维 每个二维又是两个一维
int (*p)[2][2] = c;
c[i][j][k] = *(c[i][j] + k) = *(*(c[i] + j) + k) = *(*(*(C + i) + j) + k)

数组

c[] = {2, 5, 7, 9, 3, 4, 6, 1, 0, 8, 11, 13};
*(c[0][1] + 1) = c[0][1][1] = 9 * (c[1] + 1) = c[1][1] = &c[1][1][0] = // 数字6的地址

数组作参数

void Func(int A[][3]) {
}

int main() {
    int C[3][2][2] = {
        {{2, 5}, {7, 9}},
        {{3, 4}, {6, 1}},
        {{0, 8}, {11, 13}}};
    int A[2] = {1, 2};
    int B[2][3] = {{2, 4, 6}, {5, 7, 8}};
    // 如果要程序接受B作为参数,B是返回 int (*)[3],要写Func(int (*)[3])或者Func[][3]
    /*
     *注意要指定列数,列数相同的数组可以被当作参数传入
     */
    int X[5][3];
    Func(*B);
}

七、动态内存🧀

内存结构 储存内容
进行分配的部分
局部变量
静态数据段 全局变量
代码段 指令

栈帧中存储着动作,分步执行,完成就pop,有自己的分配和销毁规则

栈溢出(Stack overflow)→ 缺点:大小有限制

堆:大小不固定,可以任意使用堆上内存(空闲内存段)👉 动态分配的就是上的内存

Info

C C++
malloc new
calloc delete
realloc
free

动态内存分配

int main() {
    int a; // 在栈上
    int *p;
    *p = &a;
    p = (int *)malloc(sizeof(int)); // 在堆上分配了4byte的内存,void型指针
    *p = 10;                        // 用堆上数据的唯一方式,传引用(解指针)
    free(p);                        // 释放堆上内存,好习惯

    p = (int *)malloc(20 * sizeof(int)); // 在堆上又分配了4*20byte的内存,*p指向数组起始元素,没有free的话原来指针任然存在,可能会内存泄露
    *p = 20;
}
1
2
3
4
5
6
7
8
9
int main() {
    int a; // 在栈上
    int *p;
    p = new int;     // 在堆上分配了4byte的内存,void型指针
    *p = 10;         // 用堆上数据的唯一方式,传引用(解指针)
    delete p;        // 释放堆上内存,好习惯
    p = new int[20]; // 在堆上又分配了4byte的内存,但原来指针任然存在,可能会内存泄露
    delete[] p;
}

1、malloc🧀

1
2
3
4
malloc -  void * malloc(size_t _Size);
// void 型指针
// size_t 可以理解为unsigned int
// 此处接受一个参数,就是需要分配的内存大小
void *p = malloc(10 * sizeof(int))           // 返回void型指针,不能直接用,要类型转换
int *p = (int *)malloc(10 * sizeof(int))     // 此处进行类型转换(int*)

2、calloc🧀

1
2
3
calloc - void * calloc(size_t num , size_t size);
// void 型指针
// 此处接受两个参数,一个是特定类型的元素数量,一个是类型大小

Note

malloc区别:malloc在分配之后不会对内存初始化,calloc会将分配的内存初始化为\(0\)

void *p = calloc(10  sizeof(int))           // 返回void型指针,不能直接用,要类型转换
int *p = (int *)calloc(10  sizeof(int))     // 此处进行类型转换(int*)

3、realloc🧀

realloc - void * realloc (void*ptr,size_t size);
malloc
int main() {
    int n;
    cin >> n;
    int *A = (int *)malloc(n * sizeof(int));
    // c++: int *arr = new int[n];
    for (int i = 0; i < n; i++) {
        *(A + i) = i + 1;
    }
    for (int i = 0; i < n; i++) {
        cout << *(A + i) << " ";
    }
}
realloc
int main() {
    int n;
    cin >> n;
    int *A = (int *)malloc(n * sizeof(int));
    for (int i = 0; i < n; i++) {
        *(A + i) = i + 1;
    }
    // free(A);
    int *B = (int *)realloc(A, 2 * n * sizeof(int));
    for (int i = 0; i < n; i++) {
        cout << *(B + i) << " ";
    }
}

八、函数返回指针🧀

int add(int a, int b) {
    cout << "Address of a in add=" << &a << endl;
    return a + b;
}

int main() {
    int x = 2;
    int y = 3;
    cout << "Address of x in main=" << &x << endl;
    cout << add(x, y); // 此处x,y在主函数中的值被传递给了add函数
}
int add(int *a, int *b) {
    cout << "Address of a in add=" << &a << endl;
    return *a + *b;
}

int main() {
    int x = 2;
    int y = 3;
    cout << "Address of x in main=" << &x << endl;
    cout << add(&x, &y);
}