Skip to content
Published at:

指针Pointer

如果你爱编程,请你爱C语言; 如果你爱C语言,请你爱指针; 如果你爱指针,请你爱指针的指针!

毫无疑问,指针是C语言的灵魂。指针也成了一部分人不可逾越的坎

学好指针需要的条件:耐心,笔,纸 --> 画内存图

两个符号*和&

*解地址

  • 在定义变量时,* 号表示所声明的变量为指针类型
  • 在指针使用时,* 号表示操作指针所指向的内存空间
    • 相当通过地址(指针变量的值)找到指针指向的内存,再操作内存
    • 放在等号的左边赋值(给内存赋值,写内存)
    • 放在等号的右边取值(从内存中取值,读内存)

&取地址符号

  • 取该变量的地址

pointer_demo.c

c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void) {
    int a = 100;
    int* p = NULL;

    // 指针指向谁,就把谁的地址赋值给指针
    p = &a;

    //通过*可以找到指针指向的内存区域,操作还是内存
    *p = 22;
    printf("a = %d, *p = %d\n", a, *p);

    // *放在=左边,给内存赋值,写内存
    // *放在=右边,取内存的值,读内容
    int b = *p;
    printf("b = %d\n", b);

    return 0;
}

pointer_demo1.c

c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void) {
    char* p = NULL;
    char buf[] = "abcdef";

    printf("p1 = %d\n", p);
    // 改变指针变量的值
    p = buf;
    printf("p2 = %d\n", p);

    //指针变量,和指向指向的内存是两个不同的概念
    p = p + 1;  //改变了指向变量的值,改变了指针的指向
    printf("p2 = %d\n", p);
    printf("buf = %s\n", buf);

    printf("*p = %c\n", *p);

    //改变指针指向的内存,并不会影响到指针的值
    printf("改变指针指向的内存,并不会影响到指针的值\n");
    buf[1] = '1';
    printf("p3 = %d\n", p);
    printf("buf2 = %s\n", buf);

    *p = 'm';
    printf("p4 = %d\n", p);
    printf("buf3 = %s\n", buf);

    return 0;
}

指针步长

指针加法:和指针类型相关

指针所指向的内存空间决定了指针的步长。数据类型一样,步长肯定一样,步长一样,不代表数据类型一样。

pointer_demo2.c

c
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char* argv[]) {
    char ch;
    char* p1 = &ch;
    printf("p1:%ld, p1+1: %ld\n", p1, p1 + 1);  //步长为1字节

    int16_t a;
    int16_t* p2 = &a;
    printf("p2:%ld, p2+1: %ld\n", p2, p2 + 1);  //步长为2字节

    int32_t b;
    int32_t* p3 = &b;
    printf("p3:%ld, p3+1: %ld\n", p3, p3 + 1);  //步长为4字节

    int64_t c;
    int64_t* p4 = &c;
    printf("p4:%ld, p4+1: %ld\n", p4, p4 + 1);  //步长为8字节
    return 0;
}

二级指针

img

pointer_demo3.c

c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int foo(char** pp) {
    char* tmp = (char*)malloc(100);
    strcpy(tmp, "ALGKJDLSJGLKDSJ");

    *pp = tmp;  //间接赋值
    return 0;
}

int main(int argc, char* argv[]) {
    char* p = NULL;
    int ret = 0;

    ret = foo(&p);
    printf("p = %s\n", p);

    free(p);
    p = NULL;

    return 0;
}

多级指针

指针和数组

数组名的值是一个指针常量,也就是数组第一个元素的地址

*[]关系

数组指针和指针数组

数组指针:是一个指针,指向数组的指针

指针数组:是一个数组,数组内容是指针

c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char* argv[]) {
    // 1.指针数组
    char* pointer_arr[] = {"hello", "world", "您好", "世界"};

    // 2.数组指针
    int arr[] = {100, 300, 400, 400};
    int(*arr_pointer)[] = &arr;  // 数组指针

    // (*arr_pointer);        // 数组
    // (*arr_pointer)[index]; // 元素

    for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++) {
        printf("%d -> %d\n", i, (*arr_pointer)[i]);
    }
    return 0;
}

内存图:

img

指针和字符串

指针和结构体

c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct persion {
    char name[64];
    int  age;
};

// 结构体里面套一级指针
struct persion_s {
    char* name;
    int   age;
};

// 结构体里面套二级指针
struct persion_ss {
    char** name;
    int    age;
};

int main(int argc, char* argv[]) {
    // 1.普通
    // struct persion p = {"shibin", 18};

    // 2.结构体里面套一级指针
    struct persion_s p;
    p.name = (char*)malloc(64);

    char* name = "shibin";
    strncpy(p.name, name, strlen(name));
    p.age = 18;

    printf("p.name = %s; p.age = %d\n", p.name, p.age);

    // 3.结构体里面套二级指针
    struct persion_ss pp;

    char* name_ss = (char*)malloc(64);
    strncpy(name_ss, name, strlen(name));
    pp.name = &name_ss;
    pp.age  = 18;
    printf("pp.name = %s; pp.age = %d\n", *pp.name, pp.age);

    return 0;
}

结构体资源释放问题:

释放顺序:由内向外

struct_and_pointer_demo1.c

c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 释放顺序

struct persion_s {
    char** name;
    int    age;
};

int main(int argc, char* argv[]) {
    char* name = "shibin";

    struct persion_s* p = malloc(sizeof(struct persion_s));

    char* name_s = (char*)malloc(64);
    strncpy(name_s, name, strlen(name));
    p->name = &name_s;
    p->age  = 18;
    printf("p.name = %s; p.age = %d\n", *(p->name), p->age);

    // release

    // 1.free name
    free(*(p->name));
    // 2.free struct persion
    free(p);

    return 0;
}

浅拷贝/深拷贝

copy_demo.c

c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct persion_s {
    char* name;
    int   age;
};

// 浅拷贝
void foo(void) {
    char* name = "shibin";

    struct persion_s shi;
    shi.name = (char*)malloc(64);
    strncpy(shi.name, name, strlen(name));
    shi.age = 18;

    struct persion_s xiaoshi = shi;
    printf("xiaoshi.name = %s; xiaoshi.age = %d\n", xiaoshi.name, xiaoshi.age);

    // 内存图
}

// 深拷贝
void bar(void) {
    char*            name = "shibin";
    struct persion_s shi;
    shi.name = (char*)malloc(64);
    strncpy(shi.name, name, strlen(name));
    shi.age = 18;

    struct persion_s xiaoshi = shi;
    xiaoshi.name             = (char*)malloc(64);
    strncpy(xiaoshi.name, shi.name, strlen(shi.name));
    printf("xiaoshi.name = %s; xiaoshi.age = %d\n", xiaoshi.name, xiaoshi.age);
}

int main(int argc, char* argv[]) {
    // foo();
    bar();
    return 0;
}

内存图

img

函数指针

顾名思义:指向函数的指针

函数指针基本定义与使用

function_pointer_demo.c

c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int add(int a, int b) {
    return a + b;
}
int division(int a, int b) {
    return a / b;
}

// 声明函数指针的类型
typedef int function(int a, int b);             // 函数类型
typedef int (*function_t)(int a, int b);        // 函数指针类型
typedef int (*function_arr_t[])(int a, int b);  // 函数指针数组

int main(int argc, char* argv[]) {
    int  a = 100;
    int* p = &a;  // 普通变量 指针

    // 1.函数指针
    function* f   = add;
    int       ret = f(10, 10);
    printf("ret = %d\n", ret);

    // 2.改进版本
    function_t ff = add;
    ret           = ff(20, 20);
    printf("ret = %d\n", ret);

    // 3.函数指针数组
    printf("\n");
    function_arr_t fff = {add, division};
    int            len = sizeof(fff) / sizeof(fff[0]);
    for (int i = 0; i < len; i++) {
        if (fff[i] != NULL) {
            ret = fff[i](100, 10);
            printf("array: ret = %d\n", ret);
        }
    }

    return 0;
}

函数指针作为函数参数:

function_pointer_as_arg_demo.c

c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 函数指针作为参数

int add(int a, int b) {
    return a + b;
}

// 声明函数指针的类型
typedef int function(int a, int b);             // 函数类型
typedef int (*function_t)(int a, int b);        // 函数指针类型
typedef int (*function_arr_t[])(int a, int b);  // 函数指针数组

// int foo(int (*fun)(int a, int b)) {// 1.普通版本
int foo(function_t fun) {  // 2.改进版本
    int ret = fun(100, 100);
    return ret;
}

int main(int argc, char* argv[]) {
    int ret = foo(add);
    printf("ret = %d\n", ret);
    return 0;
}

指针的特性:

通过地址,获取/操作所指向的内容

指针的作用:

  • 希望别人来修改我的值(内容),把地址给别人
  • 作为传参,减少copy量.指针数据量小
  • 调用函数:函数指针
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 1111111111
void foo(int* a) {
    *a = 1000;
}

// 2222222222
void bar(const char* str) {
    printf("str = %s\n", str);
}

// 3333333333
typedef int (*function_t)(int a, int b);
int add(int a, int b) {
    return a + b;
}

int foobar(function_t fun) {
    int ret = fun(100, 100);
    return ret;
}

int main(int argc, char* argv[]) {
    // 1.让其它函数改变这个值
    int a = 100;
    foo(&a);

    // 2.传参数的时候减少copy数据量
    char* str = "hello world";
    bar(str);

    // 3.调用函数:函数指针
    int ret = foobar(add);
    printf("ret = %d\n", ret);

    return 0;
}