c++ 指针总结

概述

  • 内存地址
  • 在计算机内存中,每个存储单元都有一个唯一的地址(内存编号)。
  • 通俗理解,内存就是房间,地址就是门牌号

  • 指针和指针变量
  • 指针(Pointer)是一种特殊的变量类型,它用于存储内存地址。
  • 指针的实质就是内存“地址”
  • 指针变量就是存储这个地址的变量。

  • 指针作用
  • 可间接修改变量的值

指针变量的定义和使用

  • 指针也是一种数据类型,指针变量也是一种变量
  • 指针变量指向谁,就把谁的地址赋值给指针变量
  • 语法格式:

类型 变量;
类型 * 指针变量 = &变量;

  • & 叫取地址,返回操作数的内存地址
  • * 叫解引用,指操作指针所指向的变量的值
  • 在定义变量时,* 号表示所声明的变量为指针类型
  • 指针变量要保存某个变量的地址,指针变量的类型比这个变量的类型多一个*
  • 指针使用时,* 号表示操作指针所指向的内存空间

#include <stdio.h>

int main() {

    // 定义一个int类型的变量,同时赋值为10
    int a = 10;
    // 打印变量的地址
    printf("&a = %p\n", &a);
    // 定义一个指针变量,int *保存int的地址
    // int *代表是一种数据类型,int *指针类型,p才是变量名
    int* p;
    // 指针指向谁,就把谁的地址赋值给这个指针变量
    p = &a;
    // 打印p, *p, p指向了a的地址,*p就是a的值
    printf("p = %p, *p = %d\n", p, *p);

    return 0;
}

通过指针间接修改变量的值

  • 指针变量指向谁,就把谁的地址赋值给指针变量
  • 通过 *指针变量 间接修改变量的值

#include <stdio.h>

int main() {
    // 定义一个int类型变量a,同时赋值为0
    int a = 0;
    // 定义int *指针变量,同时赋值a的地址
    int *p = &a;
    // 通过指针间接修改a的值
    *p = 123;
    printf("a = %d\n", a);
    // 定义一个int类型变量b,同时赋值为5
    int b = 5;
    // p 保存 b的地址
    p = &b;
    // 通过指针间接修改b的值
    *p = 250;
    printf("b = %d\n", b);

    return 0;
}

指针与常量

  • 语法格式
int a = 1;
const int *p1 = &a;	// 等价于 int const *p1 = &a;
int * const p2 = &a;
const int * const p3 = &a;
  • 从左往右看,跳过类型,看修饰哪个字符
  • 如果是*, 说明指针指向的内存不能改变
  • 如果是指针变量,说明指针的指向不能改变,指针的值不能修改
#include <stdio.h>

int main() {
    int a = 1;
    int b = 2;
    // p1 可以改,*p1不能改
    const int *p1 = &a; // 等价于 int const *p1 = &a;
    // p1 = &b;    // ok
    // *p1 = 555;  // err

    // p2 不能修改,*p2可以修改
    int *const p2 = &a;
    // p2 = &b;    //err
    // *p2 = 555;  // ok

    // p3 和 *p 都不能改
    const int *const p3 = &a;
    // p3 = &b;    // err
    // *p3 = 555;  // err

    return 0;
}

指针大小

  • 使用sizeof()测量指针的大小,得到的总是:4或8
  • sizeof()测的是指针变量指向存储地址的大小
  • 在32位平台,所有的指针(地址)都是32位(4字节)
  • 在64位平台,所有的指针(地址)都是64位(8字节)
#include <stdio.h>

int main() {
    int *p1;
	int **p2;
	char *p3;
	char **p4;
	printf("sizeof(p1) = %llu\n", sizeof(p1));
	printf("sizeof(p2) = %llu\n", sizeof(p2));
	printf("sizeof(p3) = %llu\n", sizeof(p3));
	printf("sizeof(p4) = %llu\n", sizeof(p4));
	printf("sizeof(double *) = %llu\n", sizeof(double *));

    return 0;
}

指针步长

  • 指针步长指的是通过指针进行递增或递减操作时,指针所指向的内存地址相对于当前地址的偏移量。
  • 指针的步长取决于所指向的数据类型。
  • 指针加n等于指针地址加上 n 个 sizeof(type) 的长度
  • 指针减n等于指针地址减去 n 个 sizeof(type) 的长度
#include <stdio.h>

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

    int a;
    int *p2 = &a;
    printf("p2:%p, p2+1: %p\n", p2, p2 + 1); // 步长为4字节

    double d;
    double *p3 = &d;
    printf("p3:%p, p3+1: %p\n", p3, p3 + 1); // 步长为8字节

    return 0;
}

野指针和空指针

  • 指针变量也是变量,是变量就可以任意赋值
  • 任意数值赋值给指针变量没有意义,因为这样的指针就成了野指针
  • 此指针指向的区域是未知(操作系统不允许操作此指针指向的内存区域)
  • 野指针不会直接引发错误,操作野指针指向的内存区域才会出问题
  • 为了标志某个指针变量没有任何指向,可赋值为NULL
  • NULL是一个值为0的宏常量
#include <stdio.h>

int main() {
    int *p;
    p = 0x12345678; // 给指针变量p赋值,p为野指针, ok,不会有问题,但没有意义
    // *p = 1000;      // 操作野指针指向未知区域,内存出问题,err
    printf("111111111111111111\n");

    int *q = NULL;  // 空指针

    return 0;
}

多级指针

  • C语言允许有多级指针存在,在实际的程序中一级指针最常用,其次是二级指针。
  • 二级指针就是指向一个一级指针变量地址的指针
#include <stdio.h>

int main() {

    int a = 100;

    // 一级指针
    int* p1 = &a;
    printf("&a=%p\n", &a);
    printf("p1=%p\n", p1);
    printf("&p1=%p\n", &p1);

    // 二级指针,可以存储一级指针变量的地址
    int** p2 = &p1;
    printf("p2=%p\n", p2);
    printf("&p2=%p\n", &p2);

    // 三级指针,可以存储二级指针变量的地址
    int*** p3 = &p2;
    printf("p3=%p\n", p3);
    printf("&p3=%p\n", &p3);

    printf("---------------------\n");

    // 通过一级指针访问100,打印出来
    printf("*p1=%d\n", *p1);
    
    // 通过二级指针访问100,打印出来
    printf("**p2=%d\n", **p2);
    
    // 通过三级指针访问100,打印出来
    printf("***p3=%d\n", ***p3);
    
    return 0;
}

函数参数传值


●传值是指将参数的值拷贝一份传递给函数,函数内部对该参数的修改不会影响到原来的变量

示例代码:

// 函数参数传值,函数内部交换2个变量的值,验证函数外部的变量有没有改变
#include <stdio.h>

// 函数定义
void func(int m, int n) {
    // 函数内部交换2个变量的值
    int temp = m;
    m = n;
    n = temp;
    printf("函数内部 m = %d, n = %d\n", m, n);
}

int main() {
    int a = 10;
    int b = 20;
    // 调用函数,值传递
    func(a, b);
    printf("函数外部 a = %d, b = %d\n", a, b);

    return 0;
}

运行结果:

函数内部 m = 20, n = 10
函数外部 a = 10, b = 20

函数参数传址

  • 传址是指将参数的地址传递给函数,函数内部可以通过该地址来访问原变量,并对其进行修改。

示例代码:

// 函数参数传地址,函数内部交换2个指针指向内存的值,验证函数外部的变量有没有改变
#include <stdio.h>

// 函数定义
void func(int *m, int *n) {
    // 函数内部交换2个指针指向内存的值
    int temp = *m;
    *m = *n;
    *n = temp;
    printf("函数内部 *m = %d, *n = %d\n", *m, *n);
}

int main() {
    int a = 10;
    int b = 20;
    // 调用函数,地址传递
    func(&a, &b);
    printf("函数外部 a = %d, b = %d\n", a, b);

    return 0;
}

运行结果:

函数内部 *m = 20, *n = 10
函数外部 a = 20, b = 10

函数参数传址

  • 传址是指将参数的地址传递给函数,函数内部可以通过该地址来访问原变量,并对其进行修改。
// 函数参数传地址,函数内部交换2个指针指向内存的值,验证函数外部的变量有没有改变
#include <stdio.h>

// 函数定义
void func(int *m, int *n) {
    // 函数内部交换2个指针指向内存的值
    int temp = *m;
    *m = *n;
    *n = temp;
    printf("函数内部 *m = %d, *n = %d\n", *m, *n);
}

int main() {
    int a = 10;
    int b = 20;
    // 调用函数,地址传递
    func(&a, &b);
    printf("函数外部 a = %d, b = %d\n", a, b);

    return 0;
}

运行结果:

函数内部 *m = 20, *n = 10
函数外部 a = 20, b = 10

函数指针

函数名

  • 一个函数在编译时被分配一个入口地址,这个地址就称为函数的指针,函数名代表函数的入口地址
#include <stdio.h>

void func(int a) {
    printf("func a = %d\n", a);
}


int main() {
    // 函数名字,就是代表函数的入口地址,函数地址
    printf("%p, %p, %p\n", func, &func, *func);

    // 3种调用方法都可以
    func(1); // 最简便,最常用
    (&func)(2);
    (*func)(3);

    return 0;
}

函数指针

  • 函数指针:它是指针,指向函数的指针
  • 语法格式:
返回值 (*函数指针变量)(形参列表);

函数指针变量的定义,其中返回值、形参列表需要和指向的函数匹配 

#include <stdio.h>

void func(int a) {
    printf("a = %d\n", a);
}

int main() {
    // 函数指针变量的定义,同时初始化
    void (*p1)(int a) = func;
    // 通过函数指针变量调用函数
    p1(10);

    // 先定义函数指针变量,后面再赋值
    void (*p2)(int);
    p2 = func;
    // 通过函数指针变量调用函数
    p2(20);

    return 0;
}

回调函数 

  • 函数指针变量做函数参数,这个函数指针变量指向的函数就是回调函数
  • 回调函数可以增加函数的通用性
  • 在不改变原函数的前提下,增加新功能
#include <stdio.h>

// 定义函数,函数指针做形参
int calc(int a, int b, int (*p)(int, int)){
    // 通过函数指针变量调用函数,获取返回值
    int res = p(a, b);
    
    return res;
}

// 定义加法函数
int add(int x, int y) {
    return x + y;
}

// 定义减法函数
int sub(int x, int y) {
    return x - y;
}

int main() {
    int result;

    // 回调加法函数
    result = calc(1, 2, add);
    printf("result = %d\n", result);

    // 回调减法函数
    result = calc(10, 5, sub);
    printf("result = %d\n", result);

    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/540200.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java springmvc 参数名用is开头导致为null

因为最近在整理一些源码和编写规范&#xff0c;这里写一下只是记录几年前自己遇到的问题&#xff0c;好久都忘了&#xff0c;还是写下来比较好。 问题记录&#xff1a;由于变量使用了boolean&#xff0c;并且变量名是is开头的&#xff0c;由于java机制boolean默认是false&#…

阿里云大学生免费服务器申请流程,2024年最新

阿里云学生服务器免费申请&#xff0c;之前是云翼计划学生服务器9元/月&#xff0c;现在是高校计划&#xff0c;学生服务器可以免费申请&#xff0c;先完成学生认证即可免费领取一台云服务器ECS&#xff0c;配置为2核2G、1M带宽、40G系统盘&#xff0c;在云服务器ECS实例过期之…

python入门(一)配置环境和选择IDE

Python&#xff0c;作为一种简洁易懂的编程语言&#xff0c;近年来在全球范围内受到了广泛的关注和追捧。它不仅语法简单明了&#xff0c;易于上手&#xff0c;而且拥有强大的第三方库和广泛的应用领域。从数据分析、机器学习到Web开发&#xff0c;Python都能发挥出色的性能&am…

算法题 - 双指针

目录 125. 验证回文串392. 判断子序列167. 两数之和 Ⅱ - 输入有序数组11. 盛最多的水15. 三数之和 125. 验证回文串 LeetCode_link 如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后&#xff0c;短语正着读和反着读都一样。则可以认为该短语是一个 回文串 …

面相对象设计

设计原则 创建性模式 结构性模式 行为模式

C++11 设计模式3. 工厂方法模式

简单工厂模式的遗留问题 //从上面的代码可以看到&#xff0c;简单工厂模式确实实现了new 出来具体对象&#xff0c; 和 业务逻辑的分离&#xff0c; //但是不符合 "开闭原则" //"开闭原则"说的是代码扩展性问题——对扩展开放&#xff0c;对修改关…

算法设计与分析实验报告c++实现(TSP问题、哈夫曼编码问题、顾客安排问题、最小生成树问题、图着色问题)

一、实验目的 1&#xff0e;加深学生对贪心算法设计方法的基本思想、基本步骤、基本方法的理解与掌握&#xff1b; 2&#xff0e;提高学生利用课堂所学知识解决实际问题的能力&#xff1b; 3&#xff0e;提高学生综合应用所学知识解决实际问题的能力。 二、实验任务 用贪心算…

C# 两种方法截取活动窗口屏幕,实现窗体截图

方法1&#xff0c;截屏内容仅包括活动窗口界面&#xff0c;而方法2是从屏幕范围取图&#xff0c;截屏内容会包括屏幕上所有内容。例如有一些程序在桌面顶层显示半透明的悬浮窗&#xff0c;用方法2截屏就会包括这些内容&#xff0c;并不是单纯的活动窗口内容。 方法1&#xff0c…

开源代码分享(19)-配电网孤岛优化划分方法

参考文献&#xff1a; DING Tao, LIN Yanling, LI Gengfeng, et al. A new model for resilient distribution systems by microgrids formation[J]. IEEE Transactions on Power Systems, 2017, 32(5): 4145-4147. 1.基本原理 通过分布式电源&#xff08;DGs&#xff09;形…

蓝桥杯 前一晚总结 模板 新手版

《准备实足&#xff0c;冲冲冲 省一》https://www.yuque.com/lenyan-svokd/hi7hp2/hfka297matrtsxy2?singleDoc# 《准备实足&#xff0c;冲冲冲 省一》 #include<bits/stdc.h> // 包含标准库头文件using namespace std; using ll long long; // 定义 long long 数据类…

218基于matlab的有限差分法求解泊松方程

基于matlab的有限差分法求解泊松方程&#xff0c;采用SOR超松弛迭代法。模型采用方形区域&#xff0c;划分网格数为100*100&#xff0c;网格数可以很方便的更改。程序已调通&#xff0c;可直接运行。 218有限差分法 泊松方程 SOR超松弛迭代法 - 小红书 (xiaohongshu.com)

基于React封装Handsontable并兼容antd

背景 其实Handsontable官方也提供了React的版本&#xff0c;但是官方的版本再编辑和渲染的时候并不能够很好的嵌入第三方的组件库。这就导致了&#xff0c;使用了Handsontable就没有办和普通的react项目一样轻松引用其他第三方组件。因此对其react的版本进行了二次的封装&#…

定制个性化的 openEuler 系统镜像:打造独特的安装体验

前言 标准的操作系统镜像可能无法完全满足特定用户群体或特定应用场景的需求。通过定制化&#xff0c;可以根据具体需求预装特定软件、配置特定网络设置&#xff0c;甚至设置特定的用户权限&#xff0c;以确保系统能够满足用户的需求。定制化系统镜像可以优化安装流程&#xf…

PandasAI的应用与实战解析(二):PandasAI使用流程与功能介绍

文章目录 1.使用PandasAI进行开发的流程2.配置文件解析3.支持的数据库类型4.支持的LLMs5.其他 PandasAI这个工具最突出的优点就是通过结合了Pandas和生成式LLMs&#xff0c;极大地为开发人员降低了工作量。 传统的开发调用流程&#xff08;数据分析相关&#xff09;&#xff1a…

(UDP)其他信息: 通常每个套接字地址(协议/网络地址/端口)只允许使用一次。

“System.Net.Sockets.SocketException”类型的异常在 mscorlib.dll 中发生&#xff0c;但未在用户代码中进行处理其他信息: 通常每个套接字地址(协议/网络地址/端口)只允许使用一次。这个异常表示端口已经被占用了&#xff0c;需要释放端口或者使用其他端口来建立连接。您可以…

CLion 2024:为Mac与Win打造的卓越跨平台集成开发环境

CLion 2024作为一款跨平台IDE&#xff0c;CLion 2024不仅完美支持Mac和Windows两大操作系统&#xff0c;更在细节之处展现了其出色的跨平台兼容性。无论你是在Mac的优雅界面下工作&#xff0c;还是在Windows的实用环境中编程&#xff0c;CLion 2024都能为你提供一致且流畅的开发…

如何使用 Grafana 监控文件系统状态

当 JuiceFS 文件系统部署完成并投入生产环境&#xff0c;接下来就需要着手解决一个非常重要的问题 —— 如何实时监控它的运行状态&#xff1f;毕竟&#xff0c;它可能正在为关键的业务应用或容器工作负载提供持久化存储支持&#xff0c;任何小小的故障或性能下降都可能造成不利…

ES6: set和map数据结构以及使用场景

ES6:set和map数据结构 一、Set 数据结构&#xff1a;二、使用场景&#xff1a;使用Set 进行去重三、Map 数据结构四、使用场景&#xff1a;使用Map进行树型数据懒加载刷新五、Set和Map的区别六、Map、Set的实际使用场景 Set 和 Map 是 ES6 中引入的两种新的数据结构&#xff0c…

设计模式代码实战-装饰者模式

1、问题描述 小明喜欢品尝不同口味的咖啡&#xff0c;他发现每种咖啡都可以加入不同的调料&#xff0c;比如牛奶、糖和巧克力。他决定使用装饰者模式制作自己喜欢的咖啡。 请设计一个简单的咖啡制作系统&#xff0c;使用装饰者模式为咖啡添加不同的调料。系统支持两种咖啡类型…

ANSYS Electromagnetics Suite 2023 R2 三维电磁(EM)仿真软件下载

Ansys家最新的三维电磁&#xff08;EM&#xff09;仿真软件ANSYS Electromagnetics Suite 2023 R2日前发布了&#xff0c;老wu这次分享得有点晚 &#xffe3;ω&#xffe3;&#xff0c;现在已经将资源上传到了网盘供大家免费下载&#xff0c;同时&#xff0c;为了让大家都能与…