C语言指针·入门用法超详解

目录

1.  什么是指针

2.  指针变量的定义格式

3.  指针的作用

3.1  查询数据

3.2  存储数据(修改数据)

3.3  操作其他函数中的变量

 3.4  函数返回多个值

3.5  函数的结果和计算状态分开


1.  什么是指针

        通过内存地址,指向的空间,我们可以对空间的数据进行修改,而这个内存地址就被称为指针

        在代码当中,我们会拿一个变量将指针进行存起来,那么这个变量就叫做指针变量

        通常情况下,我们会将指针变量,称为指针,但我们需要了解,真正的指针,实际上是指针变量存起来的内存地址

2.  指针变量的定义格式

        指针变量起始就是存着指针的变量,本身也是变量,而我们变量的定义格式是:

数据类型  变量名;

        但如果我们要是这样声明指针变量,那么就无法和普通变量做一个区分,因此我们将指针的数据类型和变量名之间加一个“*”进行区分:

数据类型  *  变量名;

        对于指针变量的数据类型要跟指向的变量类型保持一致,例如:

int a =10;
int* p1=&a;double b = 10;
double* p2 =&b;

        对于指针变量的*可以理解为这是一个标记,见到*我们可以理解为此时声明的变量为指针变量,他右面声明的变量名,所存储的是内存地址,例如以上代码的p1,p2。

        对于指针变量的变量名,就是字节起的名字,可随意取,但是需要避开关键字。

3.  指针的作用

3.1  查询数据

格式:*指针名

其中*在这里是解引用运算符

        首先我们创建如下代码:

int a =10;         //①
int* p=&a;         //②printf("%d\n",*p); //③*p=200;           //④printf("%d\n",*p); //⑤

        对于①我们可以理解为,在一个内存当中存储一个变量,这个变量的数据为10,下面我们假设这个数据存储的内存地址位0x0011:

        对于②我们可以理解为,我获取了变量a的地址,在存储到指针p当中,指针p可以通过内存地址指向了变量a:

        对于③中的*p,我们直到此时的p代表地址0x0011,而*p代表通过内存地址获取改地址下变量数据的意思,因此此时输出的数据为10;

注意:这里会有人将指针变量格式的*和解引用运算符的*混淆:

指针变量格式的定义指针的*,仅仅作为标记使用,告诉你*右边的变量记录的是内存地址;

而查询数据里面的是解引用运算符,他表示通过后面的内存地址去获取到对应的数据。

3.2  存储数据(修改数据)

格式:*指针名 = 数据值;

        对于④,我们可以理解为修改改地址下的数据,及此时0x0011下的数据会从10变为200:

        完整代码:

#include <stdio.h>int main()
{int a = 10;//定义一个指针变量aint* p = &a;//利用指针获取变量中的数据printf("%d\n", *p);//利用指针去存储数据/修改数据*p = 200;//输出打印printf("%d\n", a);printf("%d\n", *p);}

指针使用细节:

(1)指针变量的名字,例如int*  p需要分开;

(2)指针变量的数据类型要跟指向的变量的类型保持一致;

(3)指针变量占用的大小,跟数据类型无关,跟编译器有关,32位的是4字节,64位的是8字节。对于这里我们可以看一下:C++学习之指针-CSDN博客的1.3中的介绍;

(4)给指针变量赋值的时候,不能把一个数值赋值给指针变量。例如下图右侧,因为对于500编译器并未分配该空间的内存地址,要是将其赋值给p编译器会报错。

3.3  操作其他函数中的变量

        在使用,指针变量前,我们先来了解一下普通变量的值传递,编写代码:

#include <stdio.h>void swap(int num1, int num2);int main()
{//定义两个变量,要求交换变量中记录的值//注意:交换的代码写在一个新的函数swap中//定义两个变量int a = 10;int b = 20;//调用swap函数printf("调用前:%d,%d\n", a, b);swap(a, b);printf("调用后:%d,%d\n", a, b);return 0;}void swap(int num1, int num2)
{int temp = num1;num1 = num2;num2 = temp;
}

        该段代码主要想要实现的功能是:定义两个变量,要求交换变量中记录的值,但是我们会发现调用了交换变量的函数,但是a和b的值并未发生转换,那是因为上面这段函数,仅仅是将a和b的值赋值给了num1和num2:

        变量之间值的交换也仅仅是num1和num2值的交换,根本就没有修改a和b的值:

        因此最终输出的结果会是:

        那么我们如何实现不同函数之间,值的修改呢?

        这里我们就可以使用指针变量的功能,修改代码:

#include <stdio.h>void swap(int* p1, int* p2);int main()
{//定义两个变量,要求交换变量中记录的值//注意:交换的代码写在一个新的函数swap中//定义两个变量int a = 10;int b = 20;//调用swap函数printf("调用前:%d,%d\n", a, b);swap(&a, &b);printf("调用后:%d,%d\n", a, b);return 0;
}void swap(int* p1, int* p2)
{int temp = *p1;*p1 = *p2;*p2 = temp;
}

        这里我们可以理解为swap(&a,&b)是将a和b的内存地址,也就是指针指向的变量作为参数p1和p2,通过指针操作,将p1指向的值和p2指向的值进行交换,实现了变量值的交换:


注意:

        函数中的变量的生命周期跟函数相关,函数结束消失,变量也会消失,此时在其他函数中,就无法通过指针使用了:

#include <stdio.h>int* method();int main()
{//调用method函数,并使用该函数的变量aint* p = method();printf("%d\n",*p);return 0;
}int* method()
{int a = 10;return &a;
}

        此时运行我们会发现:

        为什么,不是说函数结束消失,变量也会消失吗?这是一个偶然发生的概念,那是因为我们执行完 int* p = method(); 并没有别的代码执行,此时这一块内存还没来得及被回收,所以此时还能使用到变量a,那我们就在该段指令后拖点时间多执行几条指令:

#include <stdio.h>int* method();int main()
{//调用method函数,并使用该函数的变量aint* p = method();printf("拖点时间\n");printf("拖点时间\n");printf("拖点时间\n");printf("拖点时间\n");printf("拖点时间\n");printf("拖点时间\n");printf("拖点时间\n");printf("%d\n",*p);return 0;
}int* method()
{int a = 10;return &a;
}

        会发现:

        如果不想函数中的变量被回收,可以在变量前加static关键字:

#include <stdio.h>int* method();int main()
{//调用method函数,并使用该函数的变量aint* p = method();printf("拖点时间\n");printf("拖点时间\n");printf("拖点时间\n");printf("拖点时间\n");printf("拖点时间\n");printf("拖点时间\n");printf("拖点时间\n");printf("%d\n",*p);return 0;
}int* method()
{static int a = 10;return &a;
}


 3.4  函数返回多个值

        老样子,我们下不使用指针看看如何实现:

#include <stdio.h>//函数返回多个值
int main()
{//定义一个函数,求数组的最大值和最小值,并进行返回return 0;
}void getMaxAndMin(int arr[], int len)
{//求数组的最大值int max = arr[0];for (int i = 0; i < len; i++){if (arr[i] > max){max = arr[i];}}//求数组的最小值int min = arr[0];for (int i = 0; i < len; i++){if (arr[i] < min){min = arr[i];}}int res[] = { max,min };return res;
}

        我们正常情况下,一个函数只能返回一个值,而若是我们想要返回多个值,我们可以如上代码,创建一个数组,将想要返回的值全部保存到数组内,不过这样会出现,若是别人想要调用你这个函数,还要读懂你这个函数,了解你这个函数的返回值,每一位都代表什么,比较麻烦,那么如何更简便一些呢?

        我们可以直接使用指针操控该内存地址的值,进行修改变量,达到返回值的目的:

#include <stdio.h>void getMaxAndMin(int arr[], int len, int* max, int* min);//函数返回多个值
int main()
{//定义一个函数,求数组的最大值和最小值,并进行返回//定义数组int arr[] = { 1,2,3,4,5,6,7,8,9,10 };int len = sizeof(arr) / sizeof(arr[0]);//调用getMaxAndMin求最大值和最小值int max = arr[0];int min = arr[0];getMaxAndMin(arr, len, &max, &min);printf("数组最大值为:%d\n", max);printf("数组最小值为:%d\n", min);return 0;
}void getMaxAndMin(int arr[], int len, int* max, int* min)
{//求数组的最大值*max = arr[0];for (int i = 0; i < len; i++){if (arr[i] > *max){*max = arr[i];}}//求数组的最小值*min = arr[0];for (int i = 0; i < len; i++){if (arr[i] < *min){*min = arr[i];}}
}

        其中,如何求len可以参考2.2.1:C++学习之数组-CSDN博客

3.5  函数的结果和计算状态分开

        继续我们先编写一个小例子:

#include <stdio.h>//函数的结果和计算状态分开
int main()
{//定义一个函数,将两个数相除,获取他们的余数return 0;
}int getRemainder(int num1, int num2)
{if (num2 == 0){return ???;}int res = num1 % num2;return res;
}

        首先,定义一个函数,将两个数相除,获取他们的余数,然后return 返回值res,但是这就会出现一个问题,若是num2==0,那么等式将会不成立,那么我们就需要加一个判断条件if(num2==0)进行返回别的值,但是此时返回什么呢?当然我们代码也可以这样写:

#include <stdio.h>//函数的结果和计算状态分开
int main()
{//定义一个函数,将两个数相除,获取他们的余数return 0;
}int getRemainder(int num1, int num2)
{if (num2 != 0){int res = num1 % num2;}return res;
}

        或者也可以写为:

#include <stdio.h>//函数的结果和计算状态分开
int main()
{//定义一个函数,将两个数相除,获取他们的余数return 0;
}int getRemainder(int num1, int num2)
{if (num2 == 0){return -1;}int res = num1 % num2;return res;
}

        这两种方法都可以完成,除此之外,我们也可以使用指针来进行操作:

#include <stdio.h>//函数的结果和计算状态分开
int main()
{//定义一个函数,将两个数相除,获取他们的余数//定义两个变量int a = 10;int b = 3;int res = 0;//调用函数获取余数int flag = getRemainder(a, b, &res);//获取getRemainder(a, b, &res);的返回值赋值给flag//对状态进行判断if (!flag){printf("获取到的余数为:%d\n", res);}return 0;
}//此时返回值表示计算的状态,0表示正常,1表示不正常
int getRemainder(int num1, int num2, int* res)
{if (num2 == 0){return 1;}*res = num1 % num2;return 0;
}

指针_时光の尘的博客-CSDN博客

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

终端pip安装包后,Pycharm却导入失败?新手别慌,3招搞定!

很多小伙伴在学习Python的过程中,都会遇到这种情况:明明在终端用pip安装好了需要的包,但在Pycharm中导入时却报错。难道是安装姿势不对? 例如在cmd中已经有了pandas,但是去pycharm中导入pandas显示没有 先别急着怀疑人生,这很可能是因为pip安装包的路径和Pycharm项目使用…

数据结构之八大排序(上)

找往期文章包括但不限于本期文章中不懂的知识点&#xff1a; 个人主页&#xff1a;我要学编程(ಥ_ಥ)-CSDN博客 所属专栏&#xff1a;数据结构&#xff08;Java版&#xff09; 目录 排序的相关介绍 直接插入排序 希尔排序&#xff08;缩小增量排序&#xff09; 选择排序 …

SQL-自连接和分组

一.介绍 这是一道面试题&#xff0c;看似简单&#xff0c;其实还是有一定技巧的&#xff0c;分析一下可以复习一下SQL查询的一些重要概念。 二.问题 给定一个包含四列的员工表 IDNameSalaryManagerId 要求 获取经理姓名、每个经理的员工数量以及每个团队的总工资。 三.设…

Vscode ssh Could not establish connection to

错误表现 上午还能正常用vs code连接服务器看代码&#xff0c;中午吃个饭关闭vscode再重新打开输入密码后就提示 Could not establish connection to 然后我用终端敲ssh的命令连接&#xff0c;结果是能正常连接。 解决方法 踩坑1 网上直接搜Could not establish connectio…

前端表格控件:打造自动化报表的高效工具

摘要 在现代Web应用中&#xff0c;自动化报表的生成对于数据分析和业务决策至关重要。前端表格控件提供了一种直观且强大的方式&#xff0c;使得报表的创建、展示和交互变得更加容易。本文将探讨如何利用前端表格控件实现自动化报表的设计、生成和优化。 引言 自动化报表可以…

《Milvus Cloud向量数据库指南》——ChatGLM:从GLM-130B到GLM-4

ChatGLM:从GLM-130B到GLM-4的跨越:智谱AI在通用人工智能领域的深度探索与实践 在人工智能的浩瀚星空中,智谱AI如同一颗璀璨的新星,以其独特的技术视角和坚定的创新步伐,在通用人工智能(AGI)的征途上留下了深刻的足迹。技术生态总监贾伟在近期的一次分享中,不仅为我们描…

分布式日志分析系统--ELK

文章目录 ELK概述ELK主要特点ELK应用架构 Elasticsearch原理JSON格式倒排索引 ES与关系型数据库ES相关概念ES安装说明1.环境初始化2.优化系统资源限制配置3.编辑ES服务文件elasticsearch. yml 优化ELK集群安装脚本scp的使用集群安装成功 Shell命令API使用创建索引创建Type创建分…

Spring Cache常用注解

依赖代码如下&#xff1a; <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId></dependency> 常用注解详解 1. Cacheable 作用&#xff1a;主要用于配置方法&#xff0c;使其…

真实体验!猫咪长肉选这些主食罐!猫生、希喂、黑夜传说详细测评

我的猫咖店铺开在高校附近&#xff0c;顾客以学生为主&#xff0c;也有很多养猫人士会到店里来&#xff0c;和我交流选粮经验。不少铲屎官都羡慕我店里的猫咪体格健壮&#xff0c;希望能介绍一些能够帮助猫咪长肉的主食罐头。那么今天我就选择了三款高肉含量的猫罐头进行测评&a…

【JLINK】J-link Commander

官方参考文档&#xff1a;J-Link Commander - SEGGER Wiki 一、运行 打开windows命令行窗口&#xff0c;找到有jlink.exe文件的地方&#xff0c;直接输入jlink.exe即可运行 二、常用命令 输入命令时候&#xff0c;大小写不影响 Command (long)Command (short)ExplanationExa…

【面向就业的Linux基础】从入门到熟练,探索Linux的秘密(十四)-租云服务器及配环境、docker基本命令

主要介绍了租云服务器和docker配置、基本命令&#xff01;&#xff01;&#xff01; 文章目录 前言 一、云平台 二、租云服务器及安装docker 1.阿里云 2.安装docker 三、docker命令 将当前用户添加到docker用户组 镜像&#xff08;images&#xff09; 容器(container) 四、实战…

在linux运维中为什么第一道防线是云防火墙,而不是waf

在Linux运维和云计算环境中&#xff0c;第一道防线通常是云防火墙&#xff08;Cloud Firewall&#xff09;&#xff0c;而不是Web应用防火墙&#xff08;WAF&#xff09;&#xff0c;主要是因为云防火墙提供了更基础和广泛的网络层安全控制。以下是一些关键原因&#xff1a; 1…

vue elementui 上传视频 以及上传视频失败重新上传没反应的处理方法

<template><el-drawertitle"上传视频"size"50%":visible.sync"drawer":direction"direction"><div class"content"><div class"upload-box" v-if"!secondStep"><!--on-exce…

艾体宝干货 | 如何分析关键网络性能指标?持续接收样品试用申请!

网络性能是企业顺利运营的重要基础&#xff0c;而Allegro流量分析仪作为一款强大的网络性能分析工具&#xff0c;为企业提供了深入了解网络运行状况的途径。在本文中&#xff0c;我们将探讨如何利用Allegro 流量分析仪分析关键网络性能指标&#xff0c;以优化网络性能、提高安全…

AI来了,这4个方面,是我们普通人的赚钱机会

在2024年&#xff0c;AI不仅改变了我们的生活方式,更为我们带来了前所未有的赚钱机会。今天&#xff0c;让我们一起探索如何利用AI赚钱的几种方法。 通过AI做自由职业 还记得小时候大人们常说"一技在身,走遍天下"吗?在AI时代,这句话变得更加真实。自由职业意味着你…

Jeecgboot仪表盘设计器使用https时访问报错

问题 仪表盘设计器设计好后&#xff0c;Nginx配置域名发送https请求时&#xff0c;/drag/page/queryById、/drag/page/addVisitsNumber仍发送http请求。导致发送下面错误&#xff1a; 原因 仪表盘设计器里设计的页面是由后端生成返回给前端的&#xff0c;后端是根据后端服…

金融行业缓存建设历程

本文转载与中原银行分布式缓存平台建设历程及实践经验中原银行分布式缓存平台历经三代建设&#xff0c;实现了高效稳定智能的缓存服务&#xff0c;提升了系统性能与资源利用率&#xff0c;降低了运维难度&#xff0c;强有力的支撑金融业务。https://mp.weixin.qq.com/s/3NgLvAb…

不得不安利的程序员开发神器,太赞了!!

作为一名程序员&#xff0c;你是否常常为繁琐的后端服务而感到头疼&#xff1f;是否希望有一种工具可以帮你简化开发流程&#xff0c;让你专注于创意和功能开发&#xff1f;今天&#xff0c;我要向大家隆重推荐一款绝佳的开发神器——MemFire Cloud。它专为懒人开发者准备&…

Hive3:库操作常用语句

1、创建库 create database if not exists myhive;2、选择库 use myhive;3、查看当前选择的库 SELECT current_database();4、查看库详细信息 desc database myhive;可以查看数据文件在hdfs集群中的存储位置 5、创建库时制定hdfs的存储位置 create database myhive2 …

什么是 HTTP/3?HTTP/3 为何席卷全球?HTTP/3 中有什么新内容?为什么需要它?

超文本传输​​协议 ( HTTP ) 是互联网的基石&#xff0c;有助于加载网页、流式传输视频以及为您最喜爱的应用程序获取数据。 去年 &#xff0c;负责定义互联网技术的组织 互联网工程任务组 ( IETF )对该协议的新版本 HTTP/3 进行了标准化。自那时起&#xff0c;HTTP/3 和相关…