10   学习指针

1  指针核心定义与本质

1.1 指针与指针变量

        1、指针即地址,指针变量是存放地址的变量,其大小与操作系统位数相关:64 位系统中占 8 字节,32 位系统中占 4 字节。

        2、指针的核心功能是通过地址间接访问目标变量,实现灵活的内存操作。

1.2 指针类型的关键作用

        1、承载地址信息与内存解析规则,基类型必须与指向数据类型严格一致。

        2、决定内存访问步长:char*偏移 1 字节,int*偏移 4 字节,double*偏移 8 字节。

1.3 核心要求

        指针变量本身及指向的内存空间必须确定,严禁使用野指针(未初始化)或悬垂指针(指向已释放空间)。

2  指针的基本操作规则

2.1 地址获取与传递

        1、通过&(取地址运算符)可获取变量在内存空间中的首地址,只有左值(可被赋值的变量)能进行&操作,常量、表达式和寄存器变量(register修饰)不能。

        2、例如int Num = 0; &Num可得到Num在内存中 4 字节空间的首地址,其类型为int *(由基类型int升级而来)。

        3、在函数传参中,使用&传递变量地址,能实现在被调函数中修改主调函数的变量值,突破值传递 “单向传递” 的限制(典型应用:函数返回多个值)。

2.2 指针访问与解引用

        1、通过*运算符可获得指针指向的空间或对应空间中的值,*连接的内容必须为指针类型(否则编译报错)。

        2、若直接使用*对应的表达式,其值为指针指向空间中的值,类型为指针类型降级后的基类型,如int *p; *p的类型为int

        3、变量有两种访问形式:直接访问(通过变量名,如Num = 5)和间接访问(通过指针,如*p = 5)。

3  字符串函数与指针应用(基于指针实现的库函数)

        字符串操作函数的底层实现依赖指针对字符数组的遍历与修改,核心是通过char*指针访问内存中的字符序列(以'\0'结尾)。

函数功能核心实现逻辑关键点
strlen计算字符串长度指针遍历至'\0',返回指针差值const char*保证只读,不包含'\0'
strcpy复制字符串逐个字符复制(含'\0'需保证目标空间足够,禁止地址重叠
strcat拼接字符串先移动指针至目标末尾,再复制源字符串目标需足够大,源和目标均需'\0'结尾
strcmp比较字符串按 ASCII 值逐个比较,遇不同字符返回差值非长度比较,一旦不同立即返回

3.1 strlen:计算字符串长度

        1、功能:返回字符串中'\0'前的字符个数(不包含'\0')。

        2、底层实现:

size_t strlen(const char *str) 
{const char *p = str; // 用指针遍历字符串while (*p != '\0') { // 遍历至结束符p++;}return p - str; // 指针差值即长度(字符个数)
}

        3、关键点:const char*保证不修改原字符串;通过指针自增遍历,通过指针相减计算长度。

3.2 strcpy:字符串复制

        1、功能:将源字符串(src)复制到目标空间(dest),包括'\0',返回目标地址。

        2、底层实现:

char *strcpy(char *dest, const char *src) 
{char *p = dest; // 保存目标首地址(用于返回)while ((*p++ = *src++) != '\0'); // 逐个复制字符,包括'\0'return dest;
}

        3、注意事项

                需保证dest空间足够大,否则会导致缓冲区溢出。

                源字符串必须以'\0'结尾,否则会复制随机数据。

                禁止源地址与目标地址重叠(如strcpy(a, a+1)会导致未定义行为)。

3.3 strcat:字符串拼接

        1、功能:将源字符串(src)追加到目标字符串(dest)的末尾(覆盖dest原有的'\0'),返回目标地址。

        2、底层实现:

char *strcat(char *dest, const char *src) 
{char *p = dest;// 1. 移动指针到dest的末尾('\0'位置)while (*p != '\0') {p++;}// 2. 复制src到dest末尾,同strcpy逻辑while ((*p++ = *src++) != '\0');return dest;
}

        3、注意事项:

        dest必须有足够空间容纳拼接后的字符串。

        destsrc都必须以'\0'结尾。

3.4 strcmp:字符串比较

        1、功能:按 ASCII 值比较两个字符串,返回差值(0表示相等,正数表示str1大于str2,负数表示str1小于str2)。

        2、底层实现:

int strcmp(const char *str1, const char *str2) 
{// 遍历字符,直到遇到不同字符或'\0'while (*str1 != '\0' && *str2 != '\0' && *str1 == *str2) {str1++;str2++;}// 返回对应字符的ASCII差值return (unsigned char)*str1 - (unsigned char)*str2;
}

        3、关键点:

                比较的是字符的 ASCII 值,而非字符串长度。

                一旦遇到不同字符立即返回差值,不继续比较后续字符。

4  指针偏移与内存访问

        1、指针偏移大小由基类型大小决定:char *偏移 1 字节,int *偏移 4 字节,double *偏移 8 字节,结构体指针偏移整个结构体大小。

        2、两个同类型指针相减的结果为地址间相差的数据类型元素个数(非字节数),例如int *p1 = a; int *p2 = a+3; p2-p1结果为 3(表示相差 3 个int元素)。

        3、指针不能与非指针类型运算,也不能跨类型相减(编译报错)。

5  野指针与空指针

        1、野指针成因:未经初始化的指针(如int *p;)、指向已释放空间的指针(如free(p);后未置空)、越界访问的指针(如数组越界)。

        2、空指针:指向内存地址0x0的指针,用NULL(本质为(void*)0)表示,该空间为系统保留的只读区域,对空指针解引用(*p = 10)会导致程序崩溃。

        3、预防野指针:未使用的指针初始化为NULL;释放内存后立即置空(free(p); p = NULL;);避免指针越界访问。

6  指针赋值与修改

        1、对指针变量本身赋值(如p = q;):改变指针的指向,使其指向新的地址。

        2、对指针解引用赋值(如*p = *q;):改变指针指向空间的值,指针指向不变。

        3、示例:int a=1, b=2; int *p=&a, *q=&b; p=q;p指向b*p结果为 2;*p=*q;a的值变为 2,p仍指向a

7  动态内存分配与管理

7.1 核心函数

函数功能特点
malloc(size_t size)申请size字节的连续内存未初始化,内容为随机值;返回void*,需强转;失败返回NULL
calloc(size_t n, size_t size)申请nsize字节的连续内存自动初始化为 0;效率略低于malloc
realloc(void *ptr, size_t size)调整已分配内存的大小可能在原地址扩展或重新分配;失败返回NULL,原内存不变
free(void *ptr)释放动态分配的内存仅释放ptr指向的空间,不改变指针值;不能重复释放或释放非动态内存

7.2 内存管理规则

        1、动态内存必须手动释放,否则导致内存泄漏(程序运行中内存占用持续增长)。

        2、释放后指针需置空(p = NULL;),避免成为野指针。

        3、申请内存后必须检查返回值是否为NULL(防止内存不足导致崩溃):

int *p = (int*)malloc(10*sizeof(int));
if (p == NULL) { /* 内存申请失败处理 */ }

        4、避免 “内存碎片”:频繁申请和释放小块内存会导致内存碎片,可通过内存池优化。

8  指向函数的指针与指针函数

类型定义特点
指针函数返回指针的函数(类型* 函数名(参数)不可返回局部变量地址,可返回动态内存、全局变量地址
函数指针指向函数的指针(返回类型 (*指针名)(参数列表)需匹配函数返回类型、参数类型和个数,用于回调函数(如qsort比较器)

8.1 指针函数

        1、定义:返回值为指针的函数,格式为类型 *函数名(参数列表)

        2、注意:不能返回局部变量的地址(局部变量在函数结束后释放,返回后成为野指针),可返回动态分配内存、全局变量或静态变量的地址。

        3、示例:

int *createArray(int n) 
{int *arr = (int*)malloc(n*sizeof(int));return arr; // 返回动态内存地址,需外部释放
}

8.2 函数指针

        1、定义:指向函数的指针,格式为返回类型 (*指针名)(参数类型列表),需严格匹配函数的返回类型、参数类型和个数。

        2、函数名本质是函数入口地址,可直接赋值给函数指针(无需&)。

        3、应用:实现回调函数(如排序函数qsort的比较器)、函数接口封装,降低模块耦合性。

        4、示例:

int add(int a, int b) 
{return a + b; 
}
int (*funcPtr)(int, int) = add; // 函数指针指向add
int result = funcPtr(3, 4); // 调用函数,结果为7

9  指针与数组的关系

9.1数组名的特殊性

        1、数组名是指向首元素的指针常量(不可修改指向),如int a[5]; a等价于&a[0],类型为int *

        2、例外情况:

    sizeof(数组名):获得数组总字节数(如int a[5]; sizeof(a) = 20)。

    &数组名:类型为指向整个数组的指针(如int (*)[5]),偏移量为整个数组大小。

               数组名作为sizeof参数或取地址时,不退化为首元素指针。

9.2 数组作为函数参数

        1、三种传递形式等价:int fun(int a[5]);int fun(int a[]);int fun(int *a);,函数内均按指针处理,丢失数组长度信息。

        2、必须显式传递数组长度:int fun(int *a, int len),避免越界访问。

9.3 字符数组与字符串

        1、字符串本质是'\0'结尾的字符数组,传参时可直接传递数组名(即char *指针)。

        2、遍历字符串:while (*p != '\0') { printf("%c", *p++); }

        3、字符串常量存储在只读区,不能通过指针修改(如char *p = "hello"; *p = 'H';会崩溃)。

10  const 指针

10.1 三种形式及特性

定义含义指针值是否可改指向空间是否可改必须初始化
const int *p; 或 int const *p;const 修饰*p
int *const p;const 修饰p
const int *const p; 或 int const *const p;const 修饰p*p

10.2 应用场景

    1、const int *p:保护指向的数据不被修改(如函数参数传递只读数据)。

    2、int *const p:确保指针始终指向同一变量(如硬件寄存器地址)。

    3、const int *const p:既固定指针指向,又保护数据(如常量配置参数)。

11  指针数组与数组指针

11.1 概念区分

类型本质定义形式内存占用(64 位)示例
指针数组数组,元素为指针int *a[5];5×8=40 字节char *strs[] = {"apple", "banana"};
数组指针指针,指向数组int (*a)[5];8 字节int (*p)[3] = &arr;arrint[3]数组)

11.2数组指针与二维数组

        1、二维数组名是指向第一行的数组指针,类型为int (*)[列数],如int a[2][3];a的类型为int (*)[3]

        2、访问元素的方式:a[i][j]*(a[i] + j)*(*(a + i) + j)(*(a + i))[j]

        3、遍历二维数组:

int a[2][3] = {{1,2,3}, {4,5,6}};
int (*p)[3] = a; // p指向第一行
for (int i=0; i<2; i++) 
{for (int j=0; j<3; j++) {printf("%d ", *(*(p+i) + j));}
}

12  二级指针

12.1 定义与本质

        1、二级指针是指向一级指针变量的指针,格式为类型 **p,64 位系统中占 8 字节,用于存储一级指针的地址。

        2、示例:int a=10; int *p=&a; int **q=&p;q是二级指针,*q等价于p**q等价于a

12.2 使用场景

        1、函数内部修改外部指针的指向:

void allocMemory(int **p, int size) 
{*p = (int*)malloc(size); // 修改外部指针p的指向
}
int *arr;
allocMemory(&arr, 10*sizeof(int)); // 传递一级指针的地址

        2、指针数组传参:指针数组的数组名是二级指针,如char *strs[] = {"a", "b"};传参时类型为char **

        3、动态二维数组:int **arr = (int**)malloc(3*sizeof(int*));用于创建行长度可变的二维数组。

13  指针作为函数参数

13.1 传递方式对比

传递方式特点适用场景
值传递形参是实参的副本,修改形参不影响实参函数仅使用参数值,不修改原变量
地址传递(一级指针)形参指向实参地址,可修改实参的值函数需修改原变量的值(如交换两个变量)
二级指针传递形参指向一级指针的地址,可修改一级指针的指向函数需为外部指针分配内存或改变其指向

13.2典型应用:交换两个变量

void swap(int *a, int *b) 
{int temp = *a;*a = *b;*b = temp;
}
int x=1, y=2;
swap(&x, &y); // 调用后x=2, y=1

14  学习总结

1、野指针操作:对未初始化、已释放或越界的指针解引用,导致程序崩溃或数据损坏。
解决:初始化指针为NULL,释放后立即置空,避免越界。

2、类型不匹配:用char*指针访问int变量(如char *p = (char*)&a;)可能导致数据截断或解析错误。
解决:严格保证指针类型与指向数据类型一致。

3、内存泄漏:动态分配的内存未释放,长期运行导致系统内存耗尽。
解决:遵循 “谁申请谁释放” 原则,使用智能指针(C++)或内存池管理。

4、重复释放内存:对同一指针多次调用free,导致内存管理混乱。

        解决:释放后立即置空,释放前检查是否为NULL

5、字符串函数使用错误

        (1)使用strcpy时目标空间不足,导致缓冲区溢出(如char dest[3]; strcpy(dest, "hello");)。
解决:使用strncpy限制复制长度,或确保目标空间足够。

        (2)对非'\0'结尾的字符序列使用strlen,导致遍历越界(如未初始化的字符数组)。
解决:确保字符串以'\0'结尾,或手动指定长度。

6、const 指针违规操作:对const int *p尝试修改指向空间的值(*p = 5),编译报错。
解决:明确const修饰的对象,避免违规修改。

7、混淆指针数组与数组指针:错误定义(如int (*a)[5]写成int *a[5])导致内存访问错误。
解决:记住优先级:[]高于*,数组指针需加括号。

8、返回局部变量地址:函数返回栈上变量的地址(如int *func() { int a=1; return &a; }),返回后地址失效。
解决:返回全局变量、静态变量或动态分配内存的地址。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/90968.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/90968.shtml
英文地址,请注明出处:http://en.pswp.cn/web/90968.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Anaconda创建环境报错:CondaHTTPEFTOT: HTTP 403 FORBIDDEN for url

一、快速解决方案这类报错的原因通常是由于 conda 无法访问镜像源或权限被服务器拒绝&#xff0c;以下是常见原因和对应的解决方案&#xff1a;检查镜像源拼写是否正确conda config --show channels清华源镜像示例如果不正确&#xff0c;先清除旧配置del %USERPROFILE%\.condar…

亚马逊地址关联暴雷:新算法下的账号安全保卫战

2025年Q3&#xff0c;上千个店铺因共享税代地址、海外仓信息重叠等问题被批量冻结&#xff0c;为行业敲响了“精细化合规”的警钟。事件复盘&#xff1a;地址成为关联风控的“致命开关”税代机构违规引发“多米诺效应”事件的导火索指向税代机构“saqibil”&#xff0c;其为降低…

在本地环境中运行 ‘dom-distiller‘ GitHub 库的完整指南

在本地环境中运行 ‘dom-distiller’ GitHub 库的完整指南 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家&#xff0c;觉得好请收藏。点击跳转到网站。 1. 项目概述 ‘dom-distiller’ 是一个用于将网页…

11. isaacsim4.2教程-Transform 树与Odometry

1. 前言学习目标在本示例中&#xff0c;你将学习如何&#xff1a;使用 TF 发布器将相机作为 TF 树的一部分发布在 TF 上发布机械臂&#xff0f;可动结构&#xff08;articulation&#xff09;的树状结构发布里程计&#xff08;Odometry&#xff09;消息开始之前前置条件已完成 …

安宝特新闻丨安宝特与Logivations正式建立合作伙伴关系,共筑物流新未来

近日&#xff0c;安宝特与物流创新企业Logivations签署合作协议&#xff0c;双方将深度融合技术专长&#xff0c;共同为客户提供高效、精准的智能物流解决方案&#xff0c;助力企业实现从人工巡检到智能管控的跨越式升级。 关于Logivations Logivations是一家深耕物流与供应链…

第三阶段—8天Python从入门到精通【itheima】-139节(pysqark实战-前言介绍)

目录 139节——pysqark实战-前言介绍 1.学习目标 2.spark是什么 3.如下是详细介绍 PySpark 的两种使用方式&#xff0c;并提供具体的代码示例【大数据应用开发比赛的代码熟悉如潮水一般冲刷我的记忆】&#xff1a; 一、本地模式&#xff08;作为 Python 第三方库使用&#…

redis数据库的四种取得 shell方法

Redis作为高性能内存数据库&#xff0c;若配置不当&#xff08;特别是未授权访问&#xff09;&#xff0c;将面临极高安全风险。攻击者可利用漏洞实现远程代码执行&#xff08;GetShell&#xff09;&#xff0c;严重威胁数据安全与服务器控制权。本文深入剖析此类漏洞的核心原理…

墨者:SQL过滤字符后手工绕过漏洞测试(万能口令)

1. 墨者学院&#xff1a;SQL过滤字符后手工绕过漏洞测试(万能口令)&#x1f680; 2. 漏洞背景分析&#x1f50d; 近期发现某登录系统存在SQL注入漏洞&#xff0c;攻击者可通过构造特殊用户名admin,a,a)#绕过身份验证。本文将深入解析其工作原理&#xff0c;并演示完整渗透测试流…

Kafka 顺序消费实现与优化策略

在 Apache Kafka 中&#xff0c;实现顺序消费需要从 Kafka 的架构和特性入手&#xff0c;因为 Kafka 本身是分布式的消息系统&#xff0c;默认情况下并不完全保证全局消息的顺序消费&#xff0c;但可以通过特定配置和设计来实现局部或完全的顺序消费。以下是实现 Kafka 顺序消费…

CSP-J 2022_第三题逻辑表达式

题目 逻辑表达式是计算机科学中的重要概念和工具&#xff0c;包含逻辑值、逻辑运算、逻辑运算优先级等内容。 在一个逻辑表达式中&#xff0c;元素的值只有两种可能&#xff1a;0&#xff08;表示假&#xff09;和 1&#xff08;表示真&#xff09;。元素之间有多种可能的逻辑运…

从释永信事件看“积善“与“积恶“的人生辩证法

博客目录起心动念皆是因&#xff0c;当下所受皆是果。"起心动念皆是因&#xff0c;当下所受皆是果。"这句古老的智慧箴言&#xff0c;在少林寺方丈释永信涉嫌违法被调查的事件中得到了令人唏嘘的印证。一位本应六根清净、持戒修行的佛门领袖&#xff0c;却深陷贪腐丑…

图片格式转换

文章目录 背景目标实现下载 背景 格式碎片化问题 行业标准差异&#xff1a;不同领域常用格式各异&#xff08;如设计界用PSD/TIFF&#xff0c;网页用JPG/PNG/WEBP&#xff0c;系统图标用ICO/ICNS&#xff09;。 设备兼容性&#xff1a;老旧设备可能不支持WEBP&#xff0c;专业…

Flutter实现Android原生相机拍照

方法1&#xff1a;使用Flutter的camera插件&#xff08;完整实现&#xff09; 1. 完整依赖与权限配置 # pubspec.yaml dependencies:flutter:sdk: fluttercamera: ^0.10.52path_provider: ^2.0.15 # 用于获取存储路径path: ^1.8.3 # 用于路径操作permission_handler:…

记录几个SystemVerilog的语法——随机

1. 随机稳定性(random stability)随机稳定性是指每个线程(thread)或对象(object)的random number generator(RNG)是私有的&#xff0c;一个线程返回的随机值序列与其他线程或对象的RNG是无关的。随机稳定性适用于以下情况&#xff1a;系统随机方法调用&#xff1a;$urandom()和…

初识 docker [下] 项目部署

项目部署Dockerfile构建镜像DockerCompose基本语法基础命令项目部署 前面我们一直在使用别人准备好的镜像&#xff0c;那如果我要部署一个Java项目&#xff0c;把它打包为一个镜像该怎么做呢&#xff1f; …省略一万字 站在巨人的肩膀上更适合我们普通人,所以直接介绍两种简单…

Android15广播ANR的源码流程分析

Android15的广播ANR源码流程跟了下实际代码的流程&#xff0c;大概如下哈&#xff1a;App.sendBroadcast() // 应用发起广播→ AMS.broadcastIntentWithFeature() // 通过Binder IPC进入system_server进程→ AMS.broadcastIntentLocked() // 权限校验广播分类&#xff08;前…

密码学中的概率论与统计学:从频率分析到现代密码攻击

在密码学的攻防博弈中&#xff0c;概率论与统计学始终是破解密码的“利器”。从古典密码时期通过字母频率推测凯撒密码的密钥&#xff0c;到现代利用线性偏差破解DES的线性密码分析&#xff0c;再到侧信道攻击中通过功耗数据的统计特性还原密钥&#xff0c;统计思维贯穿了密码分…

力扣刷题977——有序数组的平方

977. 有序数组的平方 题目&#xff1a; 给你一个按 非递减顺序 排序的整数数组 nums&#xff0c;返回 每个数字的平方 组成的新数组&#xff0c;要求也按 非递减顺序 排序。示例 1&#xff1a; 输入&#xff1a;nums [-4,-1,0,3,10] 输出&#xff1a;[0,1,9,16,100] 解释&…

应用加速游戏盾的安全作用

在数字娱乐产业蓬勃发展的今天&#xff0c;游戏已从单纯的娱乐工具演变为连接全球数十亿用户的社交平台与文化载体。然而&#xff0c;伴随游戏市场的指数级增长&#xff0c;网络攻击的频率与复杂性也呈爆发式上升。从DDoS攻击导致服务器瘫痪&#xff0c;到外挂程序破坏公平竞技…

linux安装zsh,oh-my-zsh,配置zsh主题及插件的方法

这是一份非常详细的指南&#xff0c;带你一步步在 Linux 系统中安装 Zsh、配置主题和安装插件。 Zsh&#xff08;Z Shell&#xff09;是一个功能强大的 Shell&#xff0c;相比于大多数 Linux 发行版默认的 Bash&#xff0c;它提供了更强的自定义能力、更智能的自动补全、更漂亮…