【C语言】(12)指针

指针在C语言中是一个非常重要的概念,它为程序员提供了直接访问内存的能力,使得数据操作更加灵活高效。理解并正确使用指针是掌握C语言的关键之一。

1. 指针的基本概念

指针本质上是一个变量,其存储的是另一个变量的内存地址。通过指针,我们可以间接访问和操作这个内存地址所对应的数据。指针的大小是根据cpu位数来决定的,如果是64位,一般占8字节。

指针的声明

在C语言中,声明一个指针需要使用*操作符,语法格式如下:

type *pointer_name;

这里type是指针所指向的数据的类型,pointer_name是指针变量的名称。例如:

int *ip;    // 指向整型数据的指针
double *dp; // 指向双精度浮点型数据的指针
char *cp;   // 指向字符型数据的指针

指针的初始化

指针变量在使用之前最好进行初始化,否则它可能指向一个随机的内存地址,从而导致程序崩溃。指针的初始化通常是将其指向一个已存在的变量的地址,或者是动态分配的内存区域的地址。例如:

int var = 10;
int *ip = &var; // ip现在指向变量var的地址

指针的解引用

解引用指针是通过指针访问或修改它指向的内存地址中存储的数据的过程。解引用指针也使用*操作符,例如:

int value = *ip; // 获取ip指向的内存地址中存储的整型数据

2. 指针的计算

在C语言中,指针的计算涉及到指针的加法、减法、比较以及与整数的算术运算。这些运算基于指针指向的数据类型的大小进行。这是因为,当你对指针进行算术运算时,它实际上是对指向的内存地址进行计算。

2.1 指针与整数的加减

当你给一个指针加上或减去一个整数时,实际上是在移动指针的位置。指针向前或向后移动的距离取决于它所指向的数据类型的大小。

int arr[5] = {10, 20, 30, 40, 50};
int *p = &arr[0]; // p 指向 arr 的第一个元素p = p + 1; // 现在 p 指向 arr 的第二个元素,即 arr[1]

在上面的例子中,p + 1不仅仅是简单地将地址值增加1,而是将地址值增加sizeof(int)个单位,因为p是一个指向int的指针。

2.2 指针之间的减法

两个类型相同的指针相减得到的是它们之间的距离(以它们所指向类型的大小为单位)。

int *p1 = &arr[1];
int *p2 = &arr[4];int n = p2 - p1; // n 的值是 3

上面的代码中,p2指向数组的第五个元素,p1指向第二个元素,它们之间相差3个int大小的单位。

2.3 指针的比较

指针之间可以使用比较运算符(如 ==, !=, <, >, <=, >=)。这通常用于确定两个指针是否指向同一个元素,或者判断它们的相对位置。

if (p1 < p2) {// 当 p1 指向的地址小于 p2 指向的地址时执行
}

2.4 注意事项

  • 指针运算中的加减操作只有在同一个数组或数据结构中的元素之间进行时才是安全的。跨越不同数组或数据结构边界的指针运算可能会导致未定义的行为。
  • 指针的算术运算不适用于void类型的指针,因为void类型的大小是未知的。
  • 在进行指针运算时要特别注意指针溢出、越界等问题,这些都可能导致程序崩溃或安全漏洞。

2.5 示例代码

下面是一个使用指针算术计算的例子:

#include <stdio.h>int main() {int arr[] = {10, 20, 30, 40, 50};int *ptr = arr; // 指向数组的开始for (int i = 0; i < 5; i++) {printf("arr[%d] = %d\n", i, *(ptr + i));}return 0;
}

这段代码将遍历数组arr,使用指针ptr加上偏移量i来访问数组的每个元素。

3. 二级指针

在C语言中,二级指针是指向指针的指针。它们通常用于处理指针数组或动态分配的二维数组,以及在函数中修改指针本身。

3.1 基本概念

二级指针是一个指针变量,它存储另一个指针变量的地址。可以将其视为指针的指针。

3.2 声明二级指针

二级指针的声明类似于一级指针,但需要两个星号(**)。例如:

int **ptr;

这里,ptr是一个指向指针的指针,而该指向的指针应指向int类型的数据。

3.3 使用二级指针

要理解二级指针的使用,首先要明白每级指针解引用的含义:

  • 一级指针(*ptr):存储普通变量的地址。
  • 二级指针(**ptr):存储一级指针的地址。

示例

假设有一个整型变量var和一个指向它的指针p,我们可以创建一个二级指针pp指向p

int var = 23;
int *p = &var;
int **pp = &p;

使用二级指针访问var的值:

printf("Value of var = %d\n", **pp);

3.4 二级指针与动态内存

二级指针经常用于动态分配二维数组。例如,创建一个行数可变的二维整型数组:

int rows = 5;
int cols = 2;
int **array = (int **)malloc(rows * sizeof(int *));for(int i = 0; i < rows; i++) {array[i] = (int *)malloc(cols * sizeof(int));
}

这里,array是一个二级指针,指向一个指针数组,每个指针又指向一个整型数组。

3.5 在函数中使用二级指针

二级指针可用于函数中,以修改一级指针本身。例如,编写一个函数,修改外部一级指针所指向的地址:

void modifyPointer(int **p) {int *temp = malloc(sizeof(int));*temp = 100;*p = temp;
}

调用modifyPointer时,可以传递一级指针的地址:

int *ptr;
modifyPointer(&ptr);

函数执行后,ptr将指向新分配的内存。

注意事项

  • 使用二级指针时,务必确保指针被正确初始化。未初始化的指针可能导致程序崩溃。
  • 动态分配的内存应当在使用完毕后释放,以避免内存泄漏。
  • 在函数中修改指针时,确保理解指针解引用的层级。错误的解引用可能导致不可预料的结果。

4. 指针的高级应用

指针与数组

数组名本质上是一个常量指针,指向数组的第一个元素。因此,可以使用指针来遍历数组:

int arr[] = {1, 2, 3, 4, 5};
int *p = arr; // p指向数组的第一个元素
for (int i = 0; i < 5; i++) {printf("%d ", *(p + i)); // 逐个输出数组元素
}

指针与函数

指针可以作为函数参数传递,这样函数就能够修改调用者的数据。例如,实现一个交换两个整数的函数:

void swap(int *x, int *y) {int temp = *x;*x = *y;*y = temp;
}

动态内存分配

C语言提供了malloccallocreallocfree等函数进行动态内存分配和释放。这在处理数据大小不确定的情况下非常有用:

int *arr = (int*)malloc(10 * sizeof(int)); // 动态分配一个整型数组,包含10个元素
if (arr != NULL) {// 使用动态分配的内存free(arr); // 使用完毕后,释放内存
}

3. 注意事项

  • 避免野指针:未初始化的指针可能导致程序崩溃,始终确保指针被正确初始化。
  • 防止内存泄漏:动态分配的内存必须在适当的时候通过free函数释放。
  • 确保指针操作安全:避免指针越界访问,确保指针操作不会导致程序访问非法内存。

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

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

相关文章

单例模式有几种写法?请谈谈你的理解?

为什么有单例模式&#xff1f; 单例模式&#xff08;Singleton&#xff09;&#xff0c;也叫单子模式&#xff0c;是一种常用的软件设计模式。在应用这个模式时&#xff0c;单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个全局对象&#xff0c;这样有利…

测试用例的书写方式以及测试模板大全

一个优秀的测试用例&#xff0c;应该包含以下信息&#xff1a; 1 &#xff09; 软件或项目的名称 2 &#xff09; 软件或项目的版本&#xff08;内部版本号&#xff09; 3 &#xff09; 功能模块名 4 &#xff09; 测试用例的简单描述&#xff0c;即该用例执行的目的或方法…

SpringMVC实现对网页的访问,在请求控制器中创建处理请求的方法

目录 测试HelloWorld RequestMapping注解 RequestMapping注解的位置 RequestMapping注解的value属性 RequestMapping注解的method属性 SpringMVC支持路径中的占位符&#xff08;重点&#xff09; SpringMVC获取请求参数 1、通过ServletAPI获取 2、通过控制器方法的形参…

头歌C++之while循环性质

目录 第1关:求1到n间所有整数的和 本关必读 本关任务 测试说明 第2关:计算x的n次方 本关必读 本关任务 测试说明 第3关:求给定正整数的“亲密对数” 本关必读 本关任务 测试说明 第4关:判断正整数n的各位数字中是否包含数字3或4

Spring-boot项目+Rancher6.3部署+Nacos配置中心+Rureka注册中心+Harbor镜像仓库+NFS存储

目录 一、项目概述二、环境三、部署流程3.1 Harbor部署3.1.1 docker安装3.1.2 docker-compose安装3.1.3 安装证书3.1.4 Harbor下载配置安装 3.2 NFS存储搭建3.3 Rancher平台配置3.3.1 NFS存储相关配置3.3.2 Harbor相关配置3.3.3 Nacos部署及相关配置3.3.4 工作负载deployment配…

Vue3+vite引入Tailwind CSS

Tailwind CSS 是一个为快速创建定制化 UI 组件而设计的实用型框架。与其他 CSS 框架或库不同&#xff0c;Tailwind CSS 组件没有预先设置好样式。可以使用 Tailwind 的低级实用类来为 CSS 元素设置样式&#xff0c;如 margin、flex、color 等。 自从 2017 年发布以来&#xff…

嵌入式学习第十五天

内存管理: 1.malloc void *malloc(size_t size); 功能: 申请堆区空间 参数: size:申请堆区空间的大小 返回值: 返回获得的空间的首地址 失败返回NULL 2.free void free(void *ptr); 功能: 释放堆区空间 注…

五大架构风格之一:数据流风格

数据流风格详细介绍 系统架构数据流风格是一种软件体系结构风格&#xff0c;它强调了系统内部不同部分之间的数据流动。这种风格侧重于描述系统中的数据处理过程&#xff0c;以及数据是如何从一个组件传递到另一个组件的。以下是系统架构数据流风格的详细介绍&#xff1a; 1 基…

vue3项目下载@element-plus/icons-vue苦笑不得的乌龙

一、背景 node.js版本&#xff1a;v16.20.1 npm版本&#xff1a;8.19.4 pnpm版本&#xff1a;8.0.0 二、心路历程 pnpm install element-plus/icons-vue 用命令下载element-plus/icons-vue的时候&#xff0c;报错并提醒如图 是&#xff0c;我按照提示执行了&#xff0c;结…

【美团】1688事业部-高级开发工程师JAVA-杭州

所属部门: 淘天集团 | 学历: 本科 | 工作年限: 1 年 职位描述 参与建设基于产业带LBS物理信息实时数据的全新B类专业导购模式&#xff0c;建设1688的产业地图导购中心化场景&#xff0c;为线上下做融合提供交互技术窗口&#xff1b;主导构建商家可提报和小二可运营的榜单技术支…

基于腾讯云自然语言处理 NLP服务实现文本情感分析

文章目录 一、前言二、NLP 服务简介三、Python 调用腾讯云 NLP 服务 SDK 构建情感分析处理3.1 开通腾讯云 NLP 服务3.2 创建的腾讯云持久证书&#xff08;如果已创建请跳过&#xff09;3.2 在腾讯云服务器中安装 Git 工具以及 Python 环境3.3 安装 qcloudapi-sdk-python3.4 部署…

【C++】函数提高

1.函数默认参数 在C语法中&#xff0c;函数的形参列表中的形参是可以有默认值的。 语法&#xff1a;返回值类型 函数名 &#xff08;参数 默认值&#xff09;{ } //1. 如果某个位置参数有默认值&#xff0c;那么从这个位置往后&#xff0c;从左向右&#xff0c;必须都要有默…

JRT人大金仓测试

之前基于IRIS导出的Sql脚本用JRT的导表脚本执行Sql语句在PostGreSql数据库把IRIS导出的库还原。并且试了模板设计器的打开和保存及打印功能。本次测试IRIS导出的Sql在人大金仓上还原数据库&#xff0c;并且测试模板设计器功能和打印。 首先碰到的一个坑是人大金仓把空串存成NU…

Log4j 输出日志到 TextArea JavaFX、Swing

前言 之前在自己开发一个软件的时候想着实现一个打印日志到控制台的功能&#xff0c;网上找了一大堆资料实在晦涩&#xff0c;不是看不懂就是运行一堆报错&#xff0c;截止前两分钟刚用第二种方式实现了这个功能&#xff0c;这也是咱 "独立实现" 解决问题的第一次&am…

【js逆向】scrapy基础

目录 一, 爬虫工程化 二, scrapy简介 三, Scrapy工作流程(重点) 四, scrapy安装 4.1 pip 安装 4.2 wheel安装 五, Scrapy实例 六, 自定义数据传输结构item 七, scrapy使用小总结 一, 爬虫工程化 在之前的学习中我们已经掌握了爬虫这门技术需要的大多数的技术点, 但是我…

LabVIEW传感器通用实验平台

LabVIEW传感器通用实验平台 介绍了基于LabVIEW的传感器实验平台的开发。该平台利用LabVIEW图形化编程语言和多参量数据采集卡&#xff0c;提供了一个交互性好、可扩充性强、使用灵活方便的传感器技术实验环境。 系统由硬件和软件两部分组成。硬件部分主要包括多通道数据采集卡…

代码随想录 Leetcode538. 把二叉搜索树转换为累加树

题目&#xff1a; 代码(首刷看解析 2024年1月31日&#xff09;&#xff1a; class Solution { public:int pre 0;TreeNode* convertBST(TreeNode* root) {if (!root) return nullptr;root->right convertBST(root->right);if (pre 0) {pre root->val;}else {root…

【百度Apollo】轨迹绘制:探索路径规划和可视化技术的应用

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《linux深造日志》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! ⛳️ 推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下…

2024.1.28 GNSS 学习笔记

1.基于 地球自转改正卫地距 以及 伪距码偏差 重构定位方程&#xff1a; 先验残差计算公式如下所示&#xff1a; 2.观测值如何定权&#xff1f;权重如何确定&#xff1f; 每个卫星的轨钟精度以及电离层模型修正后的误差都有差异&#xff0c;所以我们不能简单的将各个观测值等权…

Kafka-服务端-PartitionLeaderSelector、ReplicaStateMachine

PartitionLeaderSelector 通过对前面的分析可知&#xff0c;PartitionMachine将Leader副本选举、确定ISR集合的工作委托给了PartitionLeaderSelector接口实现&#xff0c;PartitionMachine可以专注于管理分区状态。这是策略模式的一种典型的应用场景。 图展示了PartitionLead…