06C语言——指针

一、指针入门

(1)、准备知识

0、图解:

1、内存地址

  • 字节:字节是内存的容量单位,英文称为 byte,一个字节有8位,即 1byte(0000 0000 --- 1111 1111) = 8bits(0 --- 1)
  • 地址:系统为了便于区分每一个字节而对它们逐一进行的编号,称为内存地址,简称地址。

32位系统:

说明:

地址+1就是加1个字节

为什么选32位系统来讲内存??

32位:4G内存:

64位:理论:1800亿亿GB,操作系统支持:16TB,电脑实际的内存条:4G-32G(所以本质上还是基于4G)

2、基地址

  • 单字节数据:对于单字节数据而言,其地址就是其字节编号。
  • 多字节数据:对于多字节数据而言,其地址是其所有字节中编号最小的那个,称为基地址。

3、取址符

  • 每个变量都是一块内存,都可以通过取址符 & 获取其地址
  • 注意:
    • 虽然不同的变量的尺寸是不同的,但是他们的地址的尺寸确实一样的。
    • 不同的地址虽然形式上看起来是一样的,但由于他们代表的内存尺寸和类型都不同,因此它们在逻辑上是严格区分的。
  • 示例代码:
// (1)、如何获取一个内存的地址??
char   ch1 = 200;
int   num1 = 100;
float  f1  = 3.14;
double f2  = 6.18;// 1、不同的变量的尺寸是不同的
printf("ch1的地址  == %p\n", &ch1);
printf("num1的地址 == %p\n", &num1);
printf("f1的地址   == %p\n", &f1);
printf("f2的地址   == %p\n", &f2);
/*// 3- 不同的地址虽然形式上看起来是一样的,但由于他们代表的内存尺寸和类型都不同,因此它们在逻辑上是严格区分的。解析:ch1的地址  == 0x7ffd1cf9c917        // char型内存占1字节num1的地址 == 0x7ffd1cf9c918        // int型内存占4字节f1的地址   == 0x7ffd1cf9c91c        // float型内存占4字节f2的地址   == 0x7ffd1cf9c920        // float型内存占8字节
*/// 2、但是他们的地址的尺寸确实一样的
printf("ch1的地址的尺寸  == %lu\n", sizeof(&ch1));
printf("num1的地址的尺寸 == %lu\n", sizeof(&num1));
printf("f1的地址的尺寸   == %lu\n", sizeof(&f1));
printf("f2的地址的尺寸   == %lu\n", sizeof(&f2));

(2)、指针的概念

0、图解:

1、指针的说明

由于翻译的问题,以及口语表达的习惯,在日常表述中,指针在不同的场合会代表以下几个含义:

  • 指 地址
    • 比如变量a的地址 &a,这是一个地址当然也是一个指针,我们可以说指针 &a 指向变量 a。
  • 指 指针变量
    • 比如 int *p; 此处变量p是指针变量,又常被简称指针。
  • 示例代码:
// (1)、指针的说明
// 1、指地址
int num2 = 200;
printf("num2的地址 == %p\n", &num2) ;   // 可以认为&num2为指针,这个指针&num指向了变量num2(或者num2的地址被&num2给存放了)// 2、指指针变量
int num3 = 300;
int *p1  = &num3;                      // 指针变量p里面存放了num3的内存的地址(指针变量p指向了num的内存)

2、指针的初始化(定义)

// 1、未初始化的类型                    // 注意:定义指针的时候,必须要有具体的合法指向,否则指针将会在内存中乱指,导致数据处理出错
char   *p1;                            // 字符型指针类型
int    *p2;                            // 整型指针类型
float  *p3;                            // 浮点型指针类型
double *p4;                            // 双精度浮点型指针类型// 2、推荐的初始化
char ch  = 0;                         
char *p5 = &ch;                        // 指向确定的我们申请的合法内存(可读可写)
char *p6 = NULL;                       // 指向内存中一个临时的安全的保留区域(不可访问区域)

3、指针的赋值

int num4 = 100;
int *p7  = NULL;
p7   = &num4;                           // 一般性的赋值操作,和初始化(定义)是不一样的,不需要加*号,p8本身就是指针变量

4、指针的索引(引用)

int num5 = 500;
num5     = 555;                         // 直接通过内存的名字,对其内存进行赋值操作int *p8  = &num5;                       // 让指针p8指向这块内存
*p8      = 888;                         // 间接通过指针p8来控制num5的内存,然后给其赋值
printf("num5 == %d\n", num5);/*int *p和*p的不同:int *p: 这个是初始化,只是表明这个p变量是个指针*p:    这个不是初始化,是后面的语句用的,这个时候表明其是指针p指向的那块内存p:      这个不是初始化,是后面的语句用的,这个时候表明其是指针变量p
*/

二、特殊指针

(1)、野指针

  • 概念:指向一块未知区域的指针,被称为野指针。野指针是危险的。

  • 危害:
  1. 引用野指针,相当于访问了非法的内存,常常会导致段错误(segmentation fault)
  2. 引用野指针,可能会破坏系统的关键数据,导致系统崩溃等严重后果
  • 产生原因:
  1. 指针定义之后,未初始化
  2. 指针所指向的内存,被系统回收
  3. 指针越界
  • 如何防止:
  1. 指针定义时,及时初始化
  2. 绝不引用已被系统回收的内存
  3. 确认所申请的内存边界,谨防越界

示例代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>// 计算数组元素的个数
#define CAL_ARR_NUM(A) (sizeof(A)/sizeof(A[0]))// 整个数组的大小 / 数组的首元素的大小 == 数组元素个数// 自己定义的NULL
#define SELF_NULL ((void *)0) // 官方定义的NULL
// #define NULL ((void *)0)   // 空指针// 主函数
int main(int argc, char const *argv[])
{// (1)、野指针// 1、指针定义之后,未初始化char *p1;/*解决方法:方法一:char *p1 = NULL;  // char *p1 =  SELF_NULL方法二:char ch  = 'a';char *p1 = &ch;*/// 2、指针所指向的内存,被系统回收char *str = malloc(100);    // 申请堆内存空间:申请一块100个字节的内存空间(堆空间),str指针指向了这块内存strcpy(str, "hello");       // 通过str指针将"hello"字符串数据,复制到str指向的内存中(堆空间)// free(str);               // 释放堆内存空间:通过str指针释放其指向的内存空间if (str != NULL)            // str指针释放堆内存空间后,其指向依然是那个空间的位置,不会是NULL(具体看系统,看编译器){strcpy(str, "world");   // 之前已经释放了str指针指向的堆内存空间了,对其已经没有访问权限了,所以会报错误或警告printf("str == %s\n", str);}// 解决方法:使用完该内存,再释放,绝不再次使用// 3、指针越界char buf[] = "shijie";char *p2   = buf;// 数组的地址for (int i = 0; i < 7; i++){printf("buf[%d]的地址 == %p\n", i, &buf[i]);}// p2逐渐加1的地址for (int i = 0; i < 7; i++){printf("p2+%d的地址 == %p\n", i, p2+i); // 将指针变量p2里面的地址打印出来}*(p2+10) = 'a';     // 此处不能赋值,因为越界了,不是我门申请的合法内存空间/*解决方法:让指针赋值操作在其限定的合法内存的区域上进行比如:使用这个函数CAL_ARR_NUM(A)   // 计算其合法内存区域的长度*/return 0;
}

(2)、空指针

很多情况下,我们不可避免地会遇到野指针,比如刚定义的指针内无法立即为其分配一块恰当的存,又或者指针所指向的内存被释放了等等。一般的做法就是将这些危险的野指针指向一块确定的内存,比如零地址内存。

  • 概念:空指针即保存了零地址的指针,亦即指向零地址的指针。
// 自写的NULL空指针
#define SELF_NULL ((void *)0)   // 官方定义的NULL空指针
#define NULL ((void *)0)

三、指针运算

(1)、计算指针的大小

  • 指针的大小(尺寸)   --- 只和系统位数相关(32位:4字节, 64字节),和数据类型无关
char    *p1 = NULL;
int     *p2 = NULL;
float   *p3 = NULL;
double  *p4 = NULL;printf("sizeof(f1) == %lu\n", sizeof(p1));
printf("sizeof(f2) == %lu\n", sizeof(p2));
printf("sizeof(f3) == %lu\n", sizeof(p3));
printf("sizeof(f4) == %lu\n", sizeof(p4));/*32位系统:sizeof(f1) == 4sizeof(f2) == 4sizeof(f3) == 4sizeof(f4) == 464位系统:sizeof(f1) == 8sizeof(f2) == 8sizeof(f3) == 8sizeof(f4) == 8
*/

(2)、指针的加减乘除(乘除没有任何意义,因此无需理会)

  • 指针加法意味着地址向上(高地址方向)移动若干个目标
  • 指针减法意味着地址向下(低地址方向)移动若干个目标

图解:

示例代码:

char ch1   = 'a';
int num1   = 100;
double f1  = 3.14;char  *p5  = &ch1;
int   *p6  = &num1;
double *p7 = &f1;// 1、乘除没有任何意义的解释
/*地址: 乘法: 0x7fffcadca377 * 5: 你很难知道这块内存到底在哪里(所以毫无意义)除法: 0x7fffcadca377 / 5
*/// 2、指针的加减法
// a、char型指针
printf("\n");
printf("指针变量p5存放的地址(ch1变量的地址)     == %p\n", p5);
printf("指针变量p5+1存放的地址(ch1变量的地址+1) == %p\n", p5+1);
/*解析:指针变量p5存放的地址(ch1变量的地址)     == 0x7ffdbddf20f7指针变量p5+1存放的地址(ch1变量的地址+1) == 0x7ffdbddf20f8地址只移动了一个字节,证明指针p5的作用范围是1个字节
*/// b、int型指针
printf("\n");
printf("指针变量p6存放的地址(num1变量的地址)     == %p\n", p6);
printf("指针变量p6+1存放的地址(num1变量的地址+1) == %p\n", p6+1);
/*解析:指针变量p6存放的地址(num1变量的地址)     == 0x7ffdc805c918指针变量p6+1存放的地址(num1变量的地址+1) == 0x7ffdc805c91c地址只移动了4个字节,证明指针p6的作用范围是4个字节
*/// c、double型指针
printf("\n");
printf("指针变量p7存放的地址(f1变量的地址)     == %p\n", p7);
printf("指针变量p7+1存放的地址(f1变量的地址+1) == %p\n", p7+1);
/*解析:指针变量p7存放的地址(f1变量的地址)     == 0x7ffcba66ad08指针变量p7+1存放的地址(f1变量的地址+1) == 0x7ffcba66ad10地址只移动了8个字节,证明指针p7的作用范围是8个字节
*/

至此,希望看完这篇文章的你有所收获,我是Bardb,译音八分贝,期待下期与你相见!

 

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

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

相关文章

Python爬虫selenium验证-中文识别点选+图片验证码案例

1.获取图片 import re import time import ddddocr import requests from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.chrome.service import Service from selenium.webdriver.support.wait import WebDriverWait from …

OpenCV(6):图像边缘检测

图像边缘检测是计算机视觉和图像处理中的一项基本任务&#xff0c;它用于识别图像中亮度变化明显的区域&#xff0c;这些区域通常对应于物体的边界。是 OpenCV 中常用的边缘检测函数及其说明: 函数算法说明适用场景cv2.Canny()Canny 边缘检测多阶段算法&#xff0c;检测效果较…

电子科技大学考研复习经验分享

电子科技大学考研复习经验分享 本人情况&#xff1a;本科就读于电科软院&#xff0c;24年2月开始了解考研&#xff0c;24年3月开始数学&#xff0c;9月决定考本院&#xff08;开始全天候图书馆学习&#xff09;并开始专业课学习&#xff0c;11月底开始政治学习&#xff0c;最后…

go基础语法

go基础语法 先下载安装go&#xff0c;然后到vscode下载go插件 1. 基础 输入输出 package main import "fmt" func main(){a:1var b2 var c int //不给初始值得标出变量类型 c3var d stringfmt.Scanf("%s",&d) //接收用户输入fmt.Printf("Hell…

硬件基础(3):三极管(1):理论基础

目录 一、背景 二、定义 三、分类 四、工作原理 NPN三极管工作原理 基本工作原理 电流放大倍数&#xff08;增益&#xff09; 输入特性 1. 输入特性的基本概念 2. 输入特性曲线的形态 3. 输入特性曲线的具体分析 输出特性 1. 输出特性图的基本概念 2. 输出特性曲…

Git最佳实践指南(Windows/Linux双系统详解)

Git最佳实践指南&#xff1a;从入门到熟练&#xff08;Windows/Linux双系统详解&#xff09; 一、环境搭建与基础配置&#xff08;适用Windows/Linux&#xff09; 1.1 Git安装与验证 # Windows系统安装&#xff08;推荐Chocolatey包管理&#xff09; # 直接下载git二进制文件…

吃一堑长一智

工作中经历&#xff0c;有感触记录下 故事一 以前在一家公司时&#xff0c;自己是一名开发人员&#xff0c;遇到问题请教领导解决方案&#xff0c;当时领导给了建议&#xff0c;后来上线后出问题了&#xff0c;背了锅。心里想的是领导说这样做的呀&#xff0c;为什么出问题还…

联想 SR590 服务器 530-8i RAID 控制器更换损坏的硬盘

坏了的硬盘会自动亮黄灯。用一个空的新盘来替换&#xff0c;新盘最好不要有东西。但是有东西可能也没啥&#xff0c;因为我看 RAID 控制器里有格式化的选项 1. 从 IPMI 把服务器关机&#xff0c;电源键进入绿色闪烁状态 2. 断电&#xff0c;推开塑料滑块拉出支架&#xff0c;…

前端浏览器开发中的浏览器兼容问题【持续更新】

目录 一、什么是浏览器兼容问题 二、JavaScript兼容问题及解决方案 2.1addEventListener与attachEvent的区别 2.2集合类对象问题 2.3自定义属性问题 2.4event.x与event.y问题 2.5window.location.href问题 2.6事件委托方法 三、CSS兼容问题及解决方案 3.1浏览器CSS样式初…

【c语言】字符函数和字符串函数(1)

一、字符分类函数 c语言中有部分函数是专门做字符分类的&#xff0c;也就是一个字符是属于什么类型的字符&#xff0c;这些函 数的使用要包含一个头文件ctype.h中。 其具体如下图所示&#xff1a; 这些函数的使用方式都类似&#xff0c;下面我们通过一个函数来看其…

LeetCodehot 力扣热题100 全排列

这段代码的目的是计算给定整数数组的所有全排列&#xff08;permutations&#xff09;&#xff0c;并返回一个包含所有排列的二维数组。 思路解析 在这段代码中&#xff0c;采用了 深度优先搜索&#xff08;DFS&#xff09; 和 回溯 的方法来生成所有的排列。 关键步骤&#xf…

【Qt源码】窥视信号槽实现机制

为了便于通过调试进源码探究下Qt信号槽实现原理&#xff0c;这里简单写一段代码如下所示。 1.自定义信号槽连接 MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow) {ui->setupUi(this);QObject::connect(ui->pushButton,&QPu…

六十天前端强化训练之第二天CSS选择器与盒模型深度解析

欢迎来到编程星辰海的博客讲解 目录 一、CSS 核心概念 1. 三种引入方式 2. CSS 注释 3. 常见单位系统 二、CSS选择器核心知识 1. 基础选择器类型 2. 组合选择器 3. 伪类选择器&#xff08;部分示例&#xff09; 4. 优先级计算规则 三、盒模型深度解析 1. 标准盒模型图…

【DeepSeek】-macOS本地终端部署后运行DeepSeek如何分析图片

【DeepSeek】-macOS本地终端部署后运行DeepSeek如何分析图片 根据您的需求&#xff0c;目前需要了解以下几个关键点及分步解决方案&#xff1a; --- 一、现状分析 1. Ollama 的限制&#xff1a; - 目前Ollama主要面向文本大模型&#xff0c;原生不支持直接上传/处理图片 …

【音视频】音视频录制、播放原理

一、音视频录制原理 通常&#xff0c;音视频录制的步骤如下图所示&#xff1a; 我们分别从音频和视频开始采样&#xff0c;通过麦克风和摄像头来接受我们的音频信息和图像信息&#xff0c;这通常是同时进行的&#xff0c;不过&#xff0c;通常视频的采集会比音频的采集慢&…

解锁养生密码,拥抱健康生活

在快节奏的现代生活中&#xff0c;养生不再是一种选择&#xff0c;而是我们保持活力、提升生活质量的关键。它不是什么高深莫测的学问&#xff0c;而是一系列融入日常的简单习惯&#xff0c;每一个习惯都在为我们的健康加分。 早晨&#xff0c;当第一缕阳光洒进窗户&#xff0c…

7种内外网数据交换方案全解析 哪种安全、高效、合规?

内外网数据交换方案主要解决了企业跨网络数据传输中的安全、效率与合规性问题。通过采用先进的加密技术、高效的数据传输协议以及严格的审批和审计机制&#xff0c;该方案确保了数据在内外网之间的安全交换&#xff0c;同时提高了传输效率&#xff0c;并满足了企业对数据合规性…

【WSL2】 Ubuntu20.04 GUI图形化界面 VcXsrv ROS noetic Vscode 主机代理 配置

【WSL2】 Ubuntu20.04 GUI图形化界面 VcXsrv ROS noetic Vscode 主机代理 配置 前言整体思路安装 WSL2Windows 环境升级为 WIN11 专业版启用window子系统及虚拟化 安装WSL2通过 Windows 命令提示符安装 WSL安装所需的 Linux 发行版&#xff08;如 Ubuntu 20.04&#xff09;查看…

监听其他音频播放时暂停正在播放的音频

要实现当有其他音频播放时暂停当前音频&#xff0c;你可以使用全局事件总线或 Vuex 来管理音频播放状态。这里我将展示如何使用一个简单的事件总线来实现这个功能。 首先&#xff0c;你需要创建一个事件总线。你可以在项目的一个公共文件中创建它&#xff0c;例如 eventBus.js…

Android数据库SQLite、Room、Realm、MMKV/DataStore、ObjectBox性能比较

Android主流数据库基础特点核心数据库特性与性能对比维度总结 在 Android 开发中&#xff0c;数据库选型直接影响应用的性能、开发效率和可维护性。不同数据库的存储限制&#xff0c;比如常用的SharedPreferences、SQLite、还有基于SQLite封装的greenDao等&#xff0c;这些似乎…