【C语言】深入理解指针(1)

目录

前言

(一)内存与地址

从实际生活出发

地址

 内存

 内存与地址关系密切

(二)指针变量

指针变量与取地址操作符

指针变量与解引用操作符 

指针的大小 

指针的运算

指针 +- 整数

 指针-指针

 指针的关系运算

指针的类型的意义

 void* 指针

 const修饰指针

野指针

野指针成因

 如何规避野指针


 

前言

         C语言是一种直接操作内存的编程语言,我们可以直接访问和操作计算机内存中的地址空间。

 

        而C语言中存在的指针类型,指针指向的就是内存中的地址。我们可以通过指针来访问和修改内存中存储的数据。

 

         因此,深入理解指针,并且理解内存,对于编写高质量的程序和调试程序故障都非常有帮助。


 在理解指针之前,我们先引入内存地址的概念:

(一)内存与地址

从实际生活出发

地址

        从实际生活中的问题出发——假如你要去上课,但是学校里教学楼那么多,如果想要上课,就得挨个教室去找,这样当你找到上课的教室,估计也下课了。

        在实际生活中,我们是如何解决找东西效率低这一问题呢?

        ——我们可以将东西排好序,提前熟悉它们的位置,甚至编好号。

        我们将每栋教学楼的每一个房间一一编号,这样,你要去上课,只要有了楼号和房间号,就可以快速找到房间,找到教室。

于是,C语言借鉴生活中的实例,从而有了地址的概念。 

 内存

        我们知道计算上CPU(中央处理器)在处理数据的时候,需要的数据是在内存中读取的,处理后的数 据也会放回内存中。

        我们买电脑的时候,电脑上内存是8GB/16GB/32GB等,那这些内存空间如何高效的管理呢?

        其实也是把内存划分为⼀个个的内存单元,每个内存单元的⼤⼩取1个字节。

         一个字节有多大呢?

常见内存单位

1byte(字节) = 8bit

1KB = 1024byte

1MB = 1024KB

1GB = 1024MB

1TB = 1024GB

1PB = 1024TB

 (一个内存单元的大小取为1字节是比较合适的,如果取得过大,那么计算机的内存就会小很多,如果过大,那么内存无法被高效利用)

 内存与地址关系密切

        每个内存单元也都有⼀个编号(这个编号就相当于教学楼的门牌号),有了这个内存单元的编号,CPU就可以快速找到⼀个内存空间。

        生活中我们把门牌号也叫地址,在计算机中我们 把内存单元的编号也称为地址。

        C语⾔中给地址起了新的名字————指针

所以我们可以理解为:

        内存单元的编号 == 地址 == 指针


(二)指针变量

指针变量与取地址操作符

        我们对内存和地址有了初步的了解,我们再回到C语言,在C语言中创建变量其实就是向内存申请空间:

#include <stdio.h>
int main()
{
 int a = 10;
 return 0;
}

 

⽐如,上述的代码就是创建了整型变量a,内存中申请4个字节,用于存放整数10,其中每个字节都有地址,上图中4个字节的地址分别是:

0x007DF784
0x007DF785
0x007DF786
0x007DF787

        上图中,出现了一个陌生的操作符—— &(取地址操作符)

        作用:取出变量的地址

 比如,我们可以将a的地址放在pa中,并用%p  打印出来:

但是,我们发现打印出的只有一个地址, 并且与第一次看到的a的地址不同!

原因在于:

        1.每一次运行代码,编译器都会重新给变量分配内存空间,这也就解释了为什么第二次打印的地址与第一次不同。

        2.同时虽然整型变量占用4个字节,我们只要知道了第⼀个字节地址,顺藤摸瓜访问到4个字节的数据也是可行的。

         在上图中,又出现了一个陌生的操作符

指针变量与解引用操作符 

        我们通过取地址操作符(&)拿到的地址是⼀个数值,如:0x006FFD70,这个数值有时候也是需要 存储起来,⽅便后期再使用的,那我们把这样的地址值存放在哪里呢?

        答案是:指针变量中。

        我们可以通过创建一个指针变量,来存储变量的地址:

#include<stdio.h>
int main()
{
    int a = 100;
    int* pa = &a;
    *pa = 0;
}

        *pa 的意思就是通过pa中存放的地址,找到指向的空间, *pa其实就是a.

        于是,解引用操作符的作用就是通过地址找到对应的变量。


指针的大小 

        我们知道,32位机器假设有32根地址总线,每根地址线出来的电信号转换成数字信号后 是1或者0,那我们把32根地址线产⽣的2进制序列当做⼀个地址,那么⼀个地址就是32个bit位,需要4 个字节才能存储。

        如果是32位机器,那么指针的大小是4个字节。

        如果是64位机器,指针的大小是8个字节。

int main()
{
 printf("%zd\n", sizeof(char *));
 printf("%zd\n", sizeof(short *));
 printf("%zd\n", sizeof(int *));
 printf("%zd\n", sizeof(double *));
 return 0;
}

 如图,演示:

         注意指针变量的大小和类型是无关的,只要指针类型的变量,在相同的平台下,大小都是相同的。


既然指针的大小与类型无关,那么指针类型存在的意义是什么?

在讨论这个问题之前,我们先引入指针的运算

指针的运算

 指针的基本运算有三种,分别是:

• 指针+- 整数

• 指针-指针

• 指针的关系运算

指针 +- 整数

#include <stdio.h>
//指针+- 整数
int main()
{
 int arr[10] = {1,2,3,4,5,6,7,8,9,10};
 int *p = &arr[0];
 int i = 0;
 int sz = sizeof(arr)/sizeof(arr[0]);
 for(i=0; i<sz; i++)
 {
 printf("%d ", *(p+i));//p+i 这⾥就是指针+整数
 }
 return 0;
}

 1.数组在内存中是连续存放的,只要知道第⼀个元素的地址,顺藤摸瓜就能找到后面的所有元素。

2.指针加减整数,表示跳过当前指针类型的元素的个数。

 指针-指针

//指针-指针
#include <stdio.h>
int my_strlen(char *s)
{
 char *p = s;
 while(*p != '\0' )
 p++;
 return p-s;
}
int main()
{
 printf("%d\n", my_strlen("abc"));
 return 0;
}

 1.图中实现的是strlen函数的模拟实现

2.指针-指针  表示两个指针变量之间的元素的个数

 指针的关系运算

//指针的关系运算
#include <stdio.h>
int main()
{
 int arr[10] = {1,2,3,4,5,6,7,8,9,10};
 int *p = &arr[0];
 int i = 0;
 int sz = sizeof(arr)/sizeof(arr[0]);
 while(p<arr+sz) //指针的⼤⼩⽐较
 {
 printf("%d ", *p);
 p++;
 }
 return 0;
}

1.指针的关系的比较,实际上就是地址大小的比较。


指针的类型的意义

         1.指针的类型决定了,对指针解引用的时候有多大的权限(⼀次能操作几个字节)。

//代码2
#include <stdio.h>
int main()
{
	int n = 0x11223344;
	char* pc = (char*)&n;
	*pc = 0;
	return 0;

 对于n的地址:

 *pc = 0;之前

 之后:

         2.指针的类型决定了指针向前或者向后一步有多大(距离)。

#include <stdio.h>
int main()
{
 int n = 10;
 char *pc = (char*)&n;
 int *pi = &n;
 
 printf("%p\n", &n);
 printf("%p\n", pc);
 printf("%p\n", pc+1);
 printf("%p\n", pi);
 printf("%p\n", pi+1);
 return 0;
}

 

我们可以看出, char* 类型的指针变量+1跳过1个字节, int* 类型的指针变量+1跳过了4个字节。 这就是指针变量的类型差异带来的变化。 

 void* 指针

        在指针类型中有⼀种特殊的类型是 void* 类型的,可以理解为无具体类型的指针(或者叫泛型指 针),这种类型的指针可以用来接受任意类型地址。但是也有局限性, void* 类型的指针不能直接进行指针的+-整数和解引用的运算。

 const修饰指针

见详解

const修饰指针icon-default.png?t=N7T8https://mp.csdn.net/mp_blog/creation/editor/134341320

野指针

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

野指针成因

1. 指针未初始化

2. 指针越界访问

3. 指针指向的空间释放

3的实例

#include <stdio.h>
int* test()
{
 int n = 100;
 return &n;
}
int main()
{
 int*p = test();
 printf("%d\n", *p);
 return 0;
}

 如何规避野指针

1.指针初始

2.小心指针越界

3.指针变量不再使用时,及时置NULL,指针使用之前检查有效性

4.避免返回局部变量的地址

5.assert断言


完~

未经作者同意禁止转载

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2022 China Collegiate Programming Contest (CCPC) Guilin Site

A.Lily Problem - A - Codeforces 题意 思路 数所有周围没L的格子 #include <bits/stdc.h>using i64 long long;constexpr int N 2e5 10; constexpr int mod 1e9 7; constexpr int Inf 0x3f3f3f3f; constexpr double eps 1e-10;std::string s;int n;void solv…

vue3 + TS 项目中使用pinia-plugin-persistedstate持久化缓存

Vue 3和Pinia是一对非常好的组合&#xff0c;可以帮助你构建现代化的Vue应用程序。而pinia-plugin-persistedstate是一个用于在Pinia存储中实现状态持久化的插件。下面我将详细介绍如何在Vue 3应用程序中使用Pinia和pinia-plugin-persistedstate模块。 首先&#xff0c;确保你…

Redis高可用之Sentinel哨兵模式

一、背景与简介 Redis关于高可用与分布式有三个与之相关的运维部署模式。分别是主从复制master-slave模式、哨兵Sentinel模式以及集群Cluster模式。 这三者都有各自的优缺点以及所应对的场景、对应的业务使用量与公司体量。 1、主从master-slave模式 【介绍】 这种模式可以采用…

Vue学习计划--Vue2(二)Vue代理方式

Vue data中的两种方式 对象式 data:{}函数式 data(){return {} }示例&#xff1a; <body><div id"app">{{ name }} {{ age}} {{$options}}<input type"text" v-model"value"></div><script>let vm new Vue({el: …

【C++】STL简介(了解)【STL的概念,STL的历史缘由,STL六大组件、STL的重要性、以及如何学习STL、STL的缺陷的讲解】

这里写自定义目录标题 一、什么是STL二、STL的版本1. 原始版本2. P. J. 版本3. RW版本★ 4. SGI版本 三、STL的六大组件四、STL的重要性五、如何学习STL六、STL的缺陷 一、什么是STL STL ( standard template libaray - 标准模板库 )&#xff1a;是C标准库 的重要组成部分&…

nodejs+vue+微信小程序+python+PHP就业求职招聘信息平台的设计与实现-计算机毕业设计推荐

主要有前端和后端&#xff0c;前端显示整个网站的信息&#xff0c;后端主要对前端和网站的基本信息进行管理。用户端模块主要是系统中普通用户在注册、登录系统可以看到自己的基本信息&#xff0c;维护自己的信息&#xff1b;管理员端模块主要是管理员登录后对整个系统相关操作…

【算法】算法题-20231205

这里写目录标题 一、LCS 01. 下载插件二、已知一个由数字组成的列表&#xff0c;请将列表中的所有0移到右侧三、实现一个trim()函数&#xff0c;去除字符串首尾的空格&#xff08;不能使用strip()方法&#xff09; 一、LCS 01. 下载插件 简单 小扣打算给自己的 VS code 安装使…

自动化巡检实现方法 (一)------- 思路概述

一、自动化巡检需要会的技能 1、因为巡检要求一天24小时全天在线&#xff0c;因此巡检程序程序一定会放在服务器上跑&#xff0c;所以要对linux操作熟悉哦 2、巡检的代码要在git上管理&#xff0c;所以git的基本操作要熟悉 3、为了更方便不会代码的同学操作&#xff0c;所以整个…

LaTex入门简明教程

文章目录 写在前面安装Texlive的安装TeXstudio 的安装 LaTex 的使用节指令图指令表指令公式指令参考文献指令引用指令TeXstudio 编译 LaTex 的 \label{} 写法建议最后 写在前面 这篇文章面向没有任何 LaTex 基础的小白&#xff0c;主要讲解了 LaTex 的安装和使用。读完文章之后…

父类的@Autowired字段被继承后能否被注入

可以 示例 父类&#xff1a;Animal.class public class Animal {Autowiredprivate PrometheusAlertService prometheusAlertService;public void eat(){System.out.println("eat food");}} 子类&#xff1a;Dog.class Service public class Dog extends Animal …

Vue3 组合式实现 带连接线的Tree型 架构图(一级树形图)

创建组件名称 TreeNodeView.vue <template><div class"tree-node"><div class"node">{{ rootNodeName }}</div><div class"children" :style"childrenLineStyle"><div class"child-node"…

Affinity VS PS 2024最新功能详细对比?Affinity Photo与Photoshop比哪家强?

多年来&#xff0c;ps已经有了大量竞争对手。然而每次Photoshop都足以保持其领先地位。开源GIMP和Pixelmator都试图取代Photoshop&#xff0c;不过Photoshop对此不屑一顾。英国Serif公司研发了一款名为Affinity Photo的软件&#xff0c;声称可以叫板ps。今天我们看看有最有可能…

12.4作业

1. #include <iostream>using namespace std;class Sofa { private:string sitting;string *lying; public:Sofa(){cout << "Sofa::无参构造函数" << endl;}Sofa(string sitting,string lying):sitting(sitting),lying(new string(lying)){cout &…

ffmpeg格式转换 免费使用视频格式转换教程

下载安装 首先去官网下载ffmpeg的软件包https://ffmpeg.org/ 如果是windows可以在直接下载编译好的软件包 https://www.gyan.dev/ffmpeg/builds/ 进入解压后的目录&#xff0c;子目录bin中的ffmpeg.exe就是我们要使用的转换器 视频信息查看 打开cmd控制台&#xff0c;从…

【开源】基于JAVA语言的天沐瑜伽馆管理系统

项目编号&#xff1a; S 039 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S039&#xff0c;文末获取源码。} 项目编号&#xff1a;S039&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 瑜伽课程模块2.3 课…

Latex去掉参考文献后面的参考文献所在页(去掉参考文献的反向超链接)

如下&#xff1a; 在使用latex插入参考文献的时候&#xff0c;最后面总是会出现这种代号。这是表明的是这条参考文献所在的页码&#xff0c;并且点击之后可以跳转到该页。正式来讲&#xff0c;这个叫超链接的BACKREF。若要去掉&#xff0c;只需要在引用hyperref的时候去掉page…

C++学习之路(十八)C++ 用Qt5实现一个工具箱(点击按钮以新窗口打开功能面板)- 示例代码拆分讲解

上篇文章&#xff0c;我们用 Qt5 实现了在小工具箱中添加了《增加托盘图标并且增加显示和退出菜单》功能。今天我们把按钮打开功能的方式改一改&#xff0c;让点击按钮以新窗口打开功能面板。下面我们就来看看如何来规划开发这样的小功能并且添加到我们的工具箱中吧。 老规矩&…

Ubuntu之Sim2Real环境配置(坑居多)

不要一上来就复制哦&#xff0c;因为很多下面的步骤让我走了很多弯路&#xff0c;如果可能的话&#xff0c;我会重新整理再发出来 前提&#xff1a; 参考教程 Docs 创建工作空间(不用跟着操作&#xff0c;无用&#xff09; 1.创建sim2real server container 1.尝试创建sim2r…

导入JDBC元数据到Apache Atlas

前言 前期实现了导入MySQL元数据到Apache Atlas, 由于是初步版本&#xff0c;且功能参照Atlas Hive Hook&#xff0c;实现的不够完美 本期对功能进行改进&#xff0c;实现了导入多种关系型数据库元数据到Apache Atlas 数据库schema与catalog 按照SQL标准的解释&#xff0c;…

直观清晰的带你了解KMP算法(超详细)

KMP算法用来找某个字符串是否存在某个连续的真子串的 下面举一个例子让抽象的KMP算法更加直观&#xff0c;有助于理解 首先我们要了解KMP算法首先要找到一个next数组来表示主串中每一个字符的回退的下标&#xff08;这个下标是对于真子串而言的&#xff0c;主串不需要回退&…