当前位置: 首页 > news >正文

C语言之指针初阶


目录

前言

一、内存与地址的关系

二、指针变量

三、野指针

四、const

五、传值调用与传址调用

总结


前言

        本文主要介绍C语言指针的一些基础知识,为后面深入理解指针打下基础,因此本文内容主要包括内存与地址的关系,指针的基本语法,指针运算,野指针,还有const修饰指针和assert断言的使用,最后还会讲到指针的传址调用,希望对大家有所帮助。


一、内存与地址的关系

指针作为C语言的核心知识,那么指针究竟是什么呢?

  1. 首先指针其实就是地址,而地址是内存中一个个内存单元的编号
  2. 我们知道计算机上CPU(中央处理器)在处理数据的时候,需要的数据是在内存中读取的,处理后的数据也会放回内存中,那我们买电脑的时候,电脑上内存有8GB/16GB/32GB等,这些内存就是程序运行时需要用到的内存
  3. 为了更高效的管理与使用这些内存,于是就将这些内存分为一个个内存单元,每个内存单元的大小取一个字节,也就是8个比特位,⼀个比特位可以存储一个2进制的位1或者0
  4. 每个内存单元也都有一个编号,有了这个内存单元的编号,CPU就可以快速找到一个内存空间,在计算机中我们把内存单元的编号也称为地址。C语言中给地址起了新的名字:指针
  5. 所以我们可以这样理解:内存单元的编号 == 地址 == 指针

如图:


二、指针变量

指针变量就是储存地址的变量

1. 取地址操作符  &

&操作符是一个单目操作符,用来取出一个变量的地址

比如,创建 int a,观察其地址

图中画红线的部分就为a变量在内存中的地址以及储存的数据,即0x004FF82C为地址,0a 00 00 00为以16进制储存的数据,其中 0a 表示的就是以16进制保存的十进制数字10,因为一个16进制数需要4个比特位表示,0a就是两个16进制数,占了8个比特位刚好为一个字节,而0a 00 00 00 四个这样的就刚好表示4个字节,至于为什么0a在前面,这是编译器自己的规则


2.指针变量创建

对于一般的指针变量的创建

(类型) * 变量名;

这样就将变量 a ,b 的地址存储在对应类型的指针变量里面,其余的如float、double等可以以此类推

注意:这里的*号表示其变量为指针变量


3.解引用操作符  *

* 解引用操作符为一个单目操作符,它可以通过地址找到其对应的数据

因为指针变量存储的就是地址,所以指针变量搭配*就可以找到其对应的数据进行操作

如:

我们可以通过解引用操作符修改指针对应的变量数据


4.指针变量类型的意义

指针变量的大小和类型无关,只要是指针变量,在同一个平台下,大小都是一样的,比如在32位平台上指针类型的变量大小都是4个字节,64位平台上为8个字节(以下在32位上演示)

因此,指针变量的类型有什么意义呢?

其实,这个意义非常重要:指针的类型决定了,对指针解引用的时候有多大的权限(一次能操作几个字节)。比如: char* 的指针解引用就只能访问⼀个字节,而 int* 的指针的解引用就能访问四个字节

如:我们再次创建一个变量a。(注:程序每次运行时分配的地址不一样)

除了a变量的地址不一样,其他和上面一样为 0a 00 00 00,它表示的是10,并且每两位表示一个字节,而一个字节表示一个内存单元,因此如上的0x0099FC8C其实表示的是储存0a的地址,我们可以一列一列的观察其内存

因为a为整形变量,占4个字节,因此其在内存中为4个连续的内存单元,如上标记的区域,此时如果我们创建一个整形指针变量接收a的地址,那么我们解引用该指针就可以操作这四个字节

如:

注:90 01 00 00 在读取时是以 00000190,也就是190,为16进制

十进制刚好为400

此时变量a可以被正常修改,而如果我们以字符类型的指针接收a的地址后,我们一次只能修改一个字节

如:

16进制28等于十进制40,如上我们貌似也能正常修改整形变量a的数组,但实际上只要我们修改的数值大于两位16进制能表达的最大数字,就不能正常修改a的数值

如:

我们只能改变一个字节,也就是char类型的指针一次只能修改一个字节

这就是指针类型的意义,当然不止如此,指针变量的类型还决定了指针加减整数的时候一次跳过多少字节,下面就会讲到


5.指针 + - 整数

先说结论:指针加减整数,会使指针前进或后退n个字节,而指针的类型决定了指针向前或者向后走一步有多大(距离)

也就是说,指针类型决定了指针加减1时的步长,比如char*指针,它一次只能跳过一个字节,它加减n也就是向前或向后跳过n*sizeof(char)个字节

比如:

(注:此处不能int *parr = arr不能写成&arr,下篇指针进阶文章我会讲到) 

此处我们就利用了循环来使数组首元素地址依次跳过 i个int类型大小的字节,实现了循环打印数组元素

此处我们有几处需要注意的点:

  1. 数组的元素在内存中是连续存放的,并且地址由低到高,不了解的可以参考我主页的数组文章
  2. 此处我们发现,如果把 *(arr+i) 换成 arr[ i ] 也就是我们之前的写法达到的效果是一样的,这是因为 *(arr+i) 是完全等价于 arr[ i ] ,也就是说,当编译器遇到 arr[ i ] 时会把它解读为 *(arr+i),按这样理解,因为 arr+i 等于 i+arr ,也就是可以写成 *(i+arr),进而可以写成 i[arr] ,我们可以验证一下
  3. 答案是完全可以的,但是平时不建议这样写因为可读性不如 arr[ i ]。总结就是 [ ] 操作符其实也是解引用的效果,只不过多了加法的作用


6.指针 - 指针

对于指针 - 指针这个运算来说,只有两指针指向的是同一块连续的内存区域时才有意义

我们可以通过指针 - 指针来计算数组两元素地址之间有多少个元素

如:

那么为什么是9个而不是10个呢?

我们可以画图来理解:

画图我们就可以很直观的感受到,arr[9] 的元素没有被计算到


三、野指针

1.概念

野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)。

野指针成因:

  1. 指针未初始化:主要是创建在函数中的指针变量没有进行初始化,造成指针指向的地址是随机值,此时指针指向的地址随机,不能对其进行解引用。
  2. 指针越界访问:这种主要出现在数组中,指针指向的地址超出了数组所在的内存区域,指向了一个不确定的地址
  3. 指针指向的空间被释放:这种主要发生在指针变量指向的地址是已经被释放的内存空间地址,被释放的空间不属于该程序,虽然可能引用不会导致报错,但是不安全


2.如何规避野指针

野指针的危害有:访问违规、数据损坏、内存泄露、安全风险等。

野指针的危害众多、因此我们的代码中需要规避野指针,那么如何规避野指针呢?

  1. 指针变量初始化时如果没有需要赋值的地址就先赋值为NULL
  2. 指针变量不再使用时,及时置NULL,指针使用之前检查有效性:当指针变量指向一块区域的时候,我们可以通过指针访问该区域,后期不再使用这个指针访问空间的时候,我们可以把该指针置为NULL。因为约定俗成的⼀个规则就是:只要是NULL指针就不去访问, 同时使用指针之前可以判断指针是否为NULL。
  3. 避免返回局部变量的地址

除了以上的方法,还有一个常用的方法:

assert 断言

assert.h 头文件定义了宏 assert() ,用于在运行时确保程序符合指定条件,如果不符合,就报错终止运行。这个宏常常被称为“断言”

比如:assert (p != NULL);

上面代码在程序运行到这一行语句时,验证变量 p 是否等于 NULL 。如果确实不等于NULL 继续运行,否则就会终止运行,并且给出报错信息提示。

程序 assert() 宏接受⼀个表达式作为参数。如果该表达式为真(返回值非零), assert()不会产生任何作用,程序继续运行。如果该表达式为假(返回值为零), assert() 就会报错,在标准错误流 stderr 中写入一条错误信息,显示没有通过的表达式,以及包含这个表达式的文件名和行号。

如:

assert() 的使用对程序员是非常友好的,使用 assert() 有几个好处:它不仅能自动标识文件和出问题的行号,还有一种无需更改代码就能开启或关闭 assert() 的机制。如果已经确认程序没有问 题,不需要再做断言,就在 #include <assert.h>语句的前面,定义一个宏 NDEBUG

如下assert()就会失去作用:

如果想再次启用assert()只需要注释掉第一行的宏就行

assert() 的缺点:因为引入了额外的检查,增加了程序的运行时间。 一般我们可以在 Debug 中使用,在 Release 版本中选择禁用 assert 就行,在 VS 这样的集成开发环境中,Release 版本中,assert()直接就是自动优化掉了。这样在debug版本写有利于程序员排查问题,在Release 版本不影响用户使用时程序的效率。


四、const

const的作用:被const修饰的变量不能被直接修改

如:

程序在还未运行时已经发出错误警告

虽然不能直接修改,但是还能通过指针变量间接修改:


但是如果const修饰的是指针变量,就分以下两种情况:

  • const如果放在*的左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改变。 但是指针变量本身的内容可变。
  • const如果放在*的右边,修饰的是指针变量本身,保证了指针变量的内容不能修改,但是指针指向的内容,可以通过指针改变。

如:


五、传值调用与传址调用

传值调用与传址调用,这个主要针对的是自定义函数的参数,也就是说:

  1. 函数参数为非指针类型,调用时传入非指针类型参数,即为传值调用
  2. 函数参数为指针类型,调用时传给函数地址,即为传址调用

那么这两个有什么区别呢?

其实主要是传值调用时,在函数内部修改形参不会影响实参,而在传址调用时,修改形参也同样会会修改实参

比如这个例题:编写一个函数,交换两个整形变量的内容

此前我们在主函数中只需要再创建一个变量,通过三者交换即可达成题目这样的效果,但如果我们在自定义函数里面,函数参数为两个整形变量,分别接收需要交换内容的两个实参,使用一样的方法是达不到一样的效果的,这时候我们只需要使用传址调用即可

如:

通过传给函数实参的地址,在函数中用指针变量的形参接收,就可以在函数中解引用该指针变量来修改对应的实参变量的内容

这就是传值调用与传址调用的不同

另外,如果函数的参数为数组类型,其实也是指针变量,给函数传参时,一般传入的就是数组名,因为数组名就是数组首元素的地址,至于详细原因我会在指针进阶中讲到


总结

        以上就是本文的全部内容,希望对大家有所帮助,下一篇我会继续写指针的进阶篇,感谢大家的支持

相关文章:

C语言之指针初阶

目录 前言 一、内存与地址的关系 二、指针变量 三、野指针 四、const 五、传值调用与传址调用 总结 前言 本文主要介绍C语言指针的一些基础知识&#xff0c;为后面深入理解指针打下基础&#xff0c;因此本文内容主要包括内存与地址的关系&#xff0c;指针的基本语法&…...

异常检测的学习和实战

1.应用&#xff1a; 1.在工业上的应用 当检测设备是否处于异常工作状态时&#xff0c;可以由上图分析得到&#xff1a;那些零散的点对应的数据是异常数据。因为设备大多数时候都是处于正常工作状态的&#xff0c;所以数据点应该比较密集地集中在一个范围内&#xff0c;而那些明…...

RabbitMQ 面试题(一)

1. 简述为什么要使用 RabbitMQ ? 使用 RabbitMQ 的主要原因包括以下几点&#xff1a; 解耦&#xff1a;在复杂的系统中&#xff0c;不同的服务或组件之间往往需要通信和协作。RabbitMQ 作为消息队列&#xff0c;允许这些组件或服务通过发送和接收消息来交互&#xff0c;而无…...

org.postgresql.util.PSQLException: 错误: 关系 “dual“ 不存在

springboot 项目连接 postgreps&#xff0c;启动时报错 org.postgresql.util.PSQLException: 错误: 关系 "dual" 不存在。 查阅资料后发现这是由配置文件中的配置 datasource-dynamic-druid-validationQuery 导致的 spring:datasource:druid:stat-view-servlet:ena…...

mysql权限分类

USAGE --无权限,只有登录数据库,只可以使用test或test_*数据库 ALL --所有权限 select/update/delete/super/slave/reload --指定的权限 with grant option --允许把自己的权限授予其它用户(此用户拥有建立账号的权限) 权限级别&#xff1a; 1、. &#xff0d;&#xff0d;全…...

【C++11】列表初始化、右值引用的详细讲解(上)

前言 在一开始学C之前我们就简单的了解了一下C的发展历史。 相比较而言&#xff0c;C11能更好地用于系统开发和库开发、语法更加泛华和简单化、更加稳定和安全&#xff0c;不仅功能更强大&#xff0c;而且能提升程序员的开发效率加了许多特性&#xff0c;约140个新特性。使得C…...

【JAVA进阶篇教学】第十三篇:Java中volatile关键字讲解

博主打算从0-1讲解下java进阶篇教学&#xff0c;今天教学第十三篇&#xff1a;volatile关键字讲解。 在 Java 中&#xff0c;volatile关键字是一种轻量级的同步机制&#xff0c;用于确保变量的可见性和禁止指令重排序。本文将详细解释volatile关键字的工作原理、可见性保证以及…...

蓝桥杯-地宫取宝

X 国王有一个地宫宝库&#xff0c;是 nm 个格子的矩阵&#xff0c;每个格子放一件宝贝&#xff0c;每个宝贝贴着价值标签。 地宫的入口在左上角&#xff0c;出口在右下角。 小明被带到地宫的入口&#xff0c;国王要求他只能向右或向下行走。 走过某个格子时&#xff0c;如果那个…...

带头单链表 C++实现

节点定义 带头单链表&#xff1a;我们只需要一个结点指针指向整个链表的第一个节点&#xff0c;这样我们就可以通过next指针访问整个链表内的所有节点 template<class T> struct ListNode {T _val;ListNode* _next;ListNode(const T &val):_val(val),_next(nullptr){…...

学习c#第24天 枚举类型

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace enumType { //定义枚举 public enum Week { 星期一, 星期二, 星期三, 星期四, 星期…...

TensorFlow运行bug汇总

1、ImportError: urllib3 v2.0 only supports OpenSSL 1.1.1 解决方案 pip install urllib31.26.15 -i https://pypi.tuna.tsinghua.edu.cn/simple 升级或者降级 (TF2.1) C:\Users\Administrator>pip install urllib31.26.15 -i https://pypi.tuna.tsinghua.edu.cn/sim…...

docker部署调度程序

Dockerfile(构建初始镜像) # python:3.8-slim-buster为精简版的python FROM python:3.8-slim-buster # 1059为组的id,newgroup为组名,1088为用户的id,newuser为新用户 RUN groupadd -g 1059 newgroup && \useradd -g -u 1088 -g newgroup -m newuser USER newuser RUN…...

websocket和http协议的区别

ws(websocket)协议和http协议是两种不同的协议。 http&#xff1a;http是一种用于传输超文本的应用层协议&#xff0c;通常用于web端浏览器和web端服务器之间传输数据。http也是基于tcp的&#xff0c;但是HTTP只能在同一时刻单向发送消息&#xff0c;是一种半双工通信。&#…...

CSS之定位

目录 CSS定位为什么需要定位定位组成定位的叠放顺序拓展 CSS定位 为什么需要定位 浮动可以让多个块级盒子一行没有缝隙排列显示&#xff0c;经常用于横向排列盒子定位则是可以让盒子自由的在某个盒子内移动位置或者固定屏幕中的某个位置&#xff0c;并且可以压住其他盒子 定…...

[IM002][Microsoft][ODBC 驱动程序管理器] 未发现数据源名称并且未指定默认驱动程序

解决办法&#xff1a; 安装驱动 下载 ODBC Driver for SQL Server - ODBC Driver for SQL Server | Microsoft Learn...

神经网络复习--神经网络算法模型及BP算法

文章目录 神经网络模型的构成BP神经网络 神经网络模型的构成 三种表示方式&#xff1a; 神经网络的三要素&#xff1a; 具有突触或连接&#xff0c;用权重表示神经元的连接强度具有时空整合功能的输入信号累加器激励函数用于限制神经网络的输出 感知神经网络 BP神经网络 …...

【Java】/*方法的使用-快速总结*/

目录 一、什么是方法 二、方法的定义 三、实参和形参的关系 四、方法重载 五、方法签名 一、什么是方法 Java中的方法可以理解为C语言中的函数&#xff0c;只是换了个名称而已。 二、方法的定义 1. 语法格式&#xff1a; public static 返回类型 方法名 (形参列表) { //方…...

kotlin中协程相关

协程 用同步的方式写出异步的效果协程最重要的是通过非阻塞挂起和恢复实现了异步代码的同步编写方式挂起函数(suspend)不一定就是在子线程中执行的&#xff0c;但是通常在定义挂起函数时都会为它指定其他线程&#xff0c;这样挂起才有意义解决多层嵌套回调 协程不是线程&…...

(自适应手机端)物流运输快递仓储网站模板 - 带三级栏目

(自适应手机端)物流运输快递仓储网站模板 - 带三级栏目PbootCMS内核开发的网站模板&#xff0c;该模板适用于物流运输网站、仓储货运网站等企业&#xff0c;当然其他行业也可以做&#xff0c;只需要把文字图片换成其他行业的即可&#xff1b;自适应手机端&#xff0c;同一个后台…...

Navicat导出表结构到Excel或Word

文章目录 sql语句复制到excel复制到Word sql语句 SELECTcols.COLUMN_NAME AS 字段,cols.COLUMN_TYPE AS 数据类型,IF(pks.CONSTRAINT_TYPE PRIMARY KEY, YES, NO) AS 是否为主键,IF(idxs.INDEX_NAME IS NOT NULL, YES, NO) AS 是否为索引,cols.IS_NULLABLE AS 是否为空,cols.…...

反向工程与模型迁移:打造未来商品详情API的可持续创新体系

在电商行业蓬勃发展的当下&#xff0c;商品详情API作为连接电商平台与开发者、商家及用户的关键纽带&#xff0c;其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息&#xff08;如名称、价格、库存等&#xff09;的获取与展示&#xff0c;已难以满足市场对个性化、智能…...

Nginx server_name 配置说明

Nginx 是一个高性能的反向代理和负载均衡服务器&#xff0c;其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机&#xff08;Virtual Host&#xff09;。 1. 简介 Nginx 使用 server_name 指令来确定…...

Module Federation 和 Native Federation 的比较

前言 Module Federation 是 Webpack 5 引入的微前端架构方案&#xff0c;允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...

Java入门学习详细版(一)

大家好&#xff0c;Java 学习是一个系统学习的过程&#xff0c;核心原则就是“理论 实践 坚持”&#xff0c;并且需循序渐进&#xff0c;不可过于着急&#xff0c;本篇文章推出的这份详细入门学习资料将带大家从零基础开始&#xff0c;逐步掌握 Java 的核心概念和编程技能。 …...

多种风格导航菜单 HTML 实现(附源码)

下面我将为您展示 6 种不同风格的导航菜单实现&#xff0c;每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...

如何在最短时间内提升打ctf(web)的水平?

刚刚刷完2遍 bugku 的 web 题&#xff0c;前来答题。 每个人对刷题理解是不同&#xff0c;有的人是看了writeup就等于刷了&#xff0c;有的人是收藏了writeup就等于刷了&#xff0c;有的人是跟着writeup做了一遍就等于刷了&#xff0c;还有的人是独立思考做了一遍就等于刷了。…...

华硕a豆14 Air香氛版,美学与科技的馨香融合

在快节奏的现代生活中&#xff0c;我们渴望一个能激发创想、愉悦感官的工作与生活伙伴&#xff0c;它不仅是冰冷的科技工具&#xff0c;更能触动我们内心深处的细腻情感。正是在这样的期许下&#xff0c;华硕a豆14 Air香氛版翩然而至&#xff0c;它以一种前所未有的方式&#x…...

Python ROS2【机器人中间件框架】 简介

销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...

管理学院权限管理系统开发总结

文章目录 &#x1f393; 管理学院权限管理系统开发总结 - 现代化Web应用实践之路&#x1f4dd; 项目概述&#x1f3d7;️ 技术架构设计后端技术栈前端技术栈 &#x1f4a1; 核心功能特性1. 用户管理模块2. 权限管理系统3. 统计报表功能4. 用户体验优化 &#x1f5c4;️ 数据库设…...

Java编程之桥接模式

定义 桥接模式&#xff08;Bridge Pattern&#xff09;属于结构型设计模式&#xff0c;它的核心意图是将抽象部分与实现部分分离&#xff0c;使它们可以独立地变化。这种模式通过组合关系来替代继承关系&#xff0c;从而降低了抽象和实现这两个可变维度之间的耦合度。 用例子…...