C语言指针高阶

指针(指针高阶)

数组和指针

理解数组名

数组名表示数组首元素的地址

c 复制代码
#include <stdio.h>
int main() {
    int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    //arr和arr[0]的内存地址是一样的, 但是意义一样吗? 一样
    printf("&arr[0] = %p\n", &arr[0]);
    printf(&arr = %p\n", arr);
    return 0;
}

sizeof和数组名

c 复制代码
#include <stdio.h>
int main() {
    int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    printf("%d\n", sizeof(arr));    //表示求整个数组的字节大小
    printf("%d\n", sizeof(arr[0]));    //表示求这个数组中一个数字所占字节大小
    return 0;
}

输出的结果为: 40, 如果arr是数组首元素地址, 那输出应该是4或者8才对 总结

sizeof(数组名), sizeof中给单独存放数组名, 这里的数组名表示整个数组, 计算的是整个数组的大小, 单位是字节

&数组名

叫做取地址数组名 此时也表示整个数组, 取出的是整个数组的地址(整个数组的地址和数组首元素的地址是有区别的) 下面代码从结果看是一样的, 但是意义一样吗?

c 复制代码
#include <stdio.h>
int main() {
    int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    //值一样不代表意义一样
    //意义一样 表示数组首元素的地址
    printf("%d\n", sizeof(arr));
    printf("%d\n", sizeof(arr[0]));
    //意义不一样 表示整个数组的地址
    printf("&arr = %p\n", &arr);

    //表示数组首元素的地址
    printf("&arr[0] == %d\n", &arr[0]);
    printf("&arr[0] + 1 == %d\n", &arr[0] + 1);    //加了4个字节

    printf("arr == %d\n", arr);
    printf("arr + 1 == %d\n", arr + 1);    //加了4个字节
    //整个数组的地址
    printf("&arr == %d\n", &arr);
    printf("&arr + 1 == %d\n", &arr + 1);    //加了40个字节(数组内有10个值, 每个值占4个字节)
    return 0;
}

此时代表的意义分别是:

  • &arr0: 数组的第一个元素地址
  • arr: 数组的第一个元素地址
  • &arr: 整个数组的地址

总结

sieof(arr)表示的是整个数组的大小 &arr表示整个数组的地址

一维数组传参, 形参的部分可以写成数组的形式, 也可以写成指针的形式

二级指针

语法

c 复制代码
数据类型** 指针变量名

主要作用存储一级指针的地址

示例

c 复制代码
int a = 10;
int* p1 = &a;    //一级指针 存放a的地址
int** p2 = &p1;    //二级指针 存放p1的地址 间接访问两回
//可以一直写
int*** p3 = &p2;    //三级指针
int**** p4 = &p3;    //四级指针

结论

  • 对二级指针p2进行2次解引用, 实际上获取到的是一级指针p1指向内存中所存储的内容, 也就是变量a的值
  • 二级指针指向一级指针的地址(可以修改被指向的一级指针内存储的地址), 一级指针指向某个元素的地址
  • 注: 指针可以一直套, 可以有三级 四级 五级等级别的指针, 但是不建议

指针数组

指针数组是指针还是数组? 指针数组: 存放指针的数组, 所以是数组

c 复制代码
int arr[] = {1, 2, 3, 4, 5};    //整型数组, 存储的是整形

定义

c 复制代码
//int类型的指针数组
int* arr[];
int* arr[] = {&a, &b, &c};

练习

c 复制代码
int a = 1;
int b = 2;
int c = 3;
int d = 4;
int* arr[] = {&a, &b, &c, &d};
printf("%p\n", *(arr + 1));    //表示什么 输出下标1的地址
printf("%d\n", **(arr + 1));    //表示什么 输出下标1的内容

char* str[] = {"abcd", "hello", "world"};    //char*类型的数组, 通俗来说当前整个数组就是一个字符串数组

数组指针

数组指针: 是一个指针 指向数组的指针 整形指针: int* p; 指向整型的指针 字符型指针: char* p; 指向字符的指针

定义

c 复制代码
int(*p)[4]; 指针p指向一个长度为4的数组, 数组的每个元素是int类型的

int arr[] = {1, 2, 3, 4};
int(*p)[4] = &arr;    //指针指向数组地址

练习

c 复制代码
int main() {
    int arr[4] = {1, 2, 3, 4};
    int(*p)[4] = &arr;
    //表示什么?
    printf("%p\n", *p);    //首元素的地址
    //表示什么?
    printf("%d\n", (*p)[1]);    //2
    return 0;
}

二维数组传参

目前学会的:

c 复制代码
void print_arr(int arr[2][3], int row, int col) {
    for(int i = 0; i < row; i++) {
        for(int j = 0; j < col; j++) {
            printf("%d ", arr[i][j]);
        }
        printf("\n);
    }
}

int mian() {
    int arr[2][3] = {1., 2, 3, 4, 5, 6};
    print_arr(arr, 2, 3);
    return 0;
}

那么能否用指针实现? 二位数组的数组名: 数组首元素的地址(和数组一样, 二维数组是特殊的一维数组)

c 复制代码
void print_arr(int(*p)[3], int row, int col) {
    for(int i = 0; i < row; i++) {
        for(int j = 0; j < col; j++) {
            printf("%d ", arr[i][j]);
        }
        printf("\n);
    }
}

int mian() {
    int arr[2][3] = {1., 2, 3, 4, 5, 6};
    print_arr(arr, 2, 3);
    return 0;
}

printf("%d", p[i][j]); == printf("%d", *(*(p+i));

函数指针

实际上还有更加复杂的指针, 叫做函数指针数组, 这个数组里放的是函数指针

什么是函数指针变量呢?

根据前面学习整形指针, 数组指针的时候, 不难得出结论: 函数指针变量应该是用来存放函数地址的, 通过地址能够调用函数 下面验证一下函数是否有地址:

c 复制代码
void test() {
    printf("hehe\n");
}
int main() {
    printf("test: %p\n", test);
    printf("&test: %p\n", &test);
    return 0;
}

确实打印出来了地址, 所以函数是有地址的, 函数名就是函数的地址, 也可以使用&函数名的方式获得函数的地址 地址拿到了怎么去存储呢? 这就要创建函数指针变量了, 函数指针变量的写法和数组指针的写法非常相似: void test2() == void (*p) () = test2

c 复制代码
int add(int x, int y) {
    return x + y;
}
int main() {
    int (*p1) (int, int) = add    //定义的函数指针指向add函数
    int (*p1) (int, int) = &add    //定义的函数指针指向add函数
    return 0;
}

函数指针的调用

那么如何进行调用呢?

c 复制代码
int add(int x, int y) {
    return x + y;
}
int main() {
    int (*p1)(int, int) = add
    int ret = (*p1)(1, 2);

    printf("%d\n", ret);    //输出3
    //可以写成这样:
    printf("%d\n", p1(10, 20));    //输出30
    return 0;
}

函数指针的用途

一般用在一些回调函数上, 什么是回调函数: 玩游戏的时候一些功能按钮(如登录), 当点击该按钮会跳转到对应的页面(登录页面), 在登录过程中有可能调用其他功能, 那么这个时候就可以把这些功能当作参数传进来, 这就叫回调函数

函数指针数组

数组当中给存放的是函数指针

练习

二维数组是一个特殊的一维数组 对于任何一种指针, 在解题的时候, 一定要判断好它的类型

c 复制代码
int main() {
    int aa[2][5] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    int* ptr1 = (int*)(&arr + 1);
    int* ptr2 = (int*)(*aa + 1);
    printf("%d %d\n", *(ptr1 - 1), *(ptr2 - 1));    //10 5
    return 0;
}
c 复制代码
//注: x86(32位)模式下运行
int main() {
    int a[] = {1, 2, 3, 4};
    printf("%zu\n", sizeof(a));    //16 数组的字节大小
    printf("%zu\n", sizeof(a + 1));    //4 &a[1]
    printf("%zu\n", sizeof(a[1]));    //4 a[1]当中的数据字节大小
    printf("%zu\n", sizeof(&a));    //4 整个数组的地址, 不管是什么地址, 终究还是地址
    printf("%zu\n", sizeof(&a + 1));    //4 整个数组的地址+1, 加了整个数组, 终究还是地址
    printf("%zu\n", sizeof(&a[0]));    //4 首元素地址
    printf("%zu\n", sizeof(&a[0] + 1));    //4 首元素地址
    return 0;
}
c 复制代码
int main() {
    char *a[] = {"work", "at", "alibaba"};    //指针数组
    //a是数组名, 数组名表示数组首元素的地址, 数组的首元素此时是什么? 数组里边的元素是char*
    char** pa = a;
    pa++;    //对二级指针+1, 加4个字节
    printf("%s\n", *pa);
    return 0;
}
/*
指针+1加几个字节? 公式: 去掉*(一个*)求sizeof()
*/
char* pc = 'a';
char** pa = &pc;
printf("%d\n", pa);    //04
printf("%d\n", pa + 1);    //08
printf("%d\n", pa + 2);    //12

int arr[] = {1, 2, 3, 4};
arr[1] == *(arr + 1)

int arr[2][2] = {1, 2, 3, 4};
arr[1][1] == *(arr + 1)[1] == *(*(arr+1)+1)
相关推荐
the局外人1 小时前
学习 FastAPI 的 Day 2:用异步 ORM 完成增删改查
后端·python·fastapi
SamDeepThinking1 小时前
if嵌套最好控制在3层以内
java·后端·程序员
苏渡苇1 小时前
Spring Insight 里上报 Span 为什么用 JDK HttpClient 而不是 RestTemplate
java·后端·spring·spring cloud·springboot·性能监控
tonydf1 小时前
AI驱动历史项目安全审查
后端·ai编程
她的男孩1 小时前
同一个接口,为什么销售只看到自己的订单?我拆了 681 行数据权限拦截器的源码
java·后端·架构
元界metalite1 小时前
SpringBoot接口通用响应对象设计 - 一个 Resp 管理五种责任
后端
对象存储与RustFS1 小时前
把 RustFS 跑在 Kubernetes 上:官方 Helm Chart 从单机到分布式
后端·rust·开源
SimonKing2 小时前
将cURL 命令直接变成 Java 代码?用JQuick-Curl搞起来
java·后端·程序员
大牧师2 小时前
Nest.js 微服务入门教程
开发语言·javascript·后端·微服务·node.js·nest.js·nest