C语言 指针

C语言学习!

目录

文章目录

前言

一、指针是什么?

二、指针变量的大小

三、指针和指针类型

四、指针和函数

五、野指针

5.1野指针成因

5.2 如何规避野指针

六、指针运算

6.1 指针+- 整数

6.2 指针-指针

6.3 指针的关系运算

总结


前言

指针理解的2个要点:

  1. 指针是内存中一个最小单元的编号,也就是地址。
  2. 平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量。


一、指针是什么?

1.1 指针变量

        整个内存空间是切割成一个个1byte(字节)大小的内存单元并编号来管理的,把内存单元的编号称为地址,地址也叫指针。存放指针(地址)的变量就是指针变量,通过指针变量里存放的地址,可以找到对应的内存单元。

        就好比一个人国家划分成许多省,每个省都有邮政编码,根据邮政编码可以找到对应地址的省,而指针变量就像是记录邮政编码的小本子。

  • 内存单元有编号,而这个编号其实就是地址,地址被称为指针。
  • 存放指针(地址)的变量就是指针变量。

  • 把内存单元的编号就称为指针。
  • 指针其实就是地址,地址就是编号。
  • 指针就是内存单元的编号。

        总结:指针就是地址,口语中说的指针通常指的是指针变量。

代码示例:

#include <stdio.h>int main()
{int a = 10;//a是整型变量,占用四个字节的内存空间,存储10int* pa = &a;//pa是一个指针变量,用来存放地址的return 0;
}

调试窗口:

        a变量里存放着10,a的地址是0x008ffd80,&a的意思是取得a的地址,运算符&的功能就是取得对象的地址。pa变量中存放的是a的地址0x008ffd80,而pa前面的int表示 pa 指向的对象是int类型的,*说明 pa 是指针变量。

        这里注意:int 整形变量占4个字节的内存,&a取出的是a所占4个字节的第一个字节的地址存放在pa变量中,pa指针变量中存的就是a的首地址。

总结: 指针变量,用来存放地址的变量。(存放在指针中的值都被当成地址处理)


1.2 取地址运算符和间接寻址运算符

        C语言为指针的使用提供了一对运算符。为了找到变量的地址,可以使用 & 取地址运算符。如果 a 是变量,那么 &a 就是 a 在内存中的地址。为了获得对指针所指向对象的访问,可以使用 * 间接寻址运算符。如果 p 是指针,那么 *p 表示 p 当前指向的对象。

        只要 p 指向 a ,*p 就是 a 的别名。

代码示例:

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

运行结果:

        *为解引用操作符,*p意思就是通过p中存放的地址,找到p所指向的对象(此时*p就是a)

&a:找到a的地址;*p找回对象a。


二、指针变量的大小

指针的大小是由硬件机器决定的:

        在32位的机器上,地址是320或者1组成二进制序列,那地址就得用4个字节的空间来存储,所以 一个指针变量的大小就应该是4个字节。

        在64位的机器上,如果有64个地址线,那一个指针变量的大小是8个字节,才能存放一个地 址。

总结:

  • 指针变量是用来存放地址的,地址是唯一标示一个内存单元的。
  • 指针的大小在32位平台是4个字节,在64位平台是8个字节

代码示例:

#include <stdio.h>
int main()
{char* pc = NULL;short* ps = NULL;int* pi = NULL;double* pd = NULL;printf("%zu\n", sizeof(pc));printf("%zu\n", sizeof(ps));printf("%zu\n", sizeof(pi));printf("%zu\n", sizeof(pd));return 0;
}

运行结果:

4
4
4
4

        不管什么类型的指针都是在创建指针变量,指针变量是用来存放地址的。指针变量的大小取决于一个地址存放的时候需要多大空间。

三、指针和指针类型

        指针的定义方式是: type + *

  • char* 类型的指针是为了存放 char 类型变量的地址。
  • short* 类型的指针是为了存放 short 类型变量的地址。
  • int* 类型的指针是为了存放 int 类型变量的地址。

代码示例1:

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

调试窗口:

代码示例2:

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

调试窗口:

结论:

        指针类型决定了指针在被解引用的时候访问几个字节。

  • int*的指针,解引用访问4个字节
  • char*的指针,解引用访问1个字节

代码示例:

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

运行结果:

pa=008FF774
pa+1=008FF778
pc=008FF774
pc+1=008FF775

结论:

  • 指针类型决定了指针+1或-1操作的时候,跳过几个字节。
  • 指针的类型决定了指针的步长。

代码示例1:

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

调试窗口:

代码示例2:

#include <stdio.h>
int main()
{int a = 0;int* pi = &a;float* pf = &a;*pf = 100.0;return 0;
}

调试窗口:

int* 和 float* 不能通用

  • pi 解引用访问4个字节,pi+1 也是跳过4个字节。
  • pf 解引用访问4个字节,pf+1 也是跳过4个字节。

        但整型数字和浮点型小数在内存中的存储方式不同,所以在监视内存中的结果也不同,也论证 int* 和 float* 不能通用。

四、指针和函数

        指针的一个重要作用就是作为函数参数使用。

代码示例:

#include <stdio.h>void text(int* y)
{*y = 20;
}
int main()
{int a = 0;text(&a);printf("a=%d", a);return 0;
}

运行结果:

a=20

        通过函数调用表达式 text(&a) ,调用函数 text 时, text 函数中形参 y 被声明为指向 int 型变量的指针变量。函数被调用时,将 &a 复制到 y 中,指针 y 便指向了a。

        由于在指针前加上指针运算符 * ,就可以显示该指针指向的对象。因此 *y 时 a 的别名。对 *y 赋值,也就是对 a 赋值,所以即使从 text 函数返回 main 函数,a 中保存的依然是180。

        若要在函数中修改变量的值,就需要传入指向该变量的指针,即告诉程序:传入的是指针,请对该指针指向的对象进行处理。

        只要在被调用的函数里的指针前写上指针运算符 * ,就能间接地处理该指针指向的对象。这也是 * 运算符又被称为间接访问运算符的原因。另外,通过在指针前写上指针运算符 * 来访问该指针指向的对象,称为解引用。

        这里可以对照函数的传址调用加深理解 C语言 函数-CSDN博客


计算和与乘积

代码示例:

#include <stdio.h>void sum_mul(int x, int y, int* Sum, int* Mul)
{*Sum = x + y;*Mul = x * y;
}
int main()
{int a = 3;int b = 6;int sum = 0;int mul = 0;sum_mul(a, b, &sum, &mul);printf("sum=%d\n", sum);printf("mul=%d\n", mul);return 0;
}

运行结果:

sum=9
mul=18

        调用函数 sum_mul 时,会将 sum 和 mul 的地址复制给形参 Sum 和 Mul 。因此 *Sum 就是 sum 的别名,*Mul 就是 mul 的别名。在函数体中,将求得的和赋值给 *Sum,将求得的乘积赋值给 *Mul 这就相当于给 sum 和 mul 进行赋值,因此从 sum_mul 函数返回到 main 函数之后,和与乘积也分别存储在 sum 和 mul 中了。


五、野指针

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

5.1 野指针成因

1. 指针未初始化

代码示例1:

#include <stdio.h>int main()
{int* p;*p = 10;return 0;
}
  • p没有初始化,就意味着没有明确指向。
  • 一个局部变量不初始化时,放的是随机值:0xcccccccc
  • *p = 10; 这里非法访问内存了,这里的p就是野指针。

2. 指针越界访问

代码示例2:

#include <stdio.h>int main()
{int arr[10] = { 0 };int* p = &arr;int i = 0;for (i = 0; i <= 10; i++){*p = i;p++;}return 0;
}

        当指针指向的范围超出数组arr的范围时,p就是野指针。


3. 指针指向的空间释放

代码示例3:

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

        调用text函数,返回变量a的地址给指针p。

        a是局部变量,他所占的空间一旦出了text函数就销毁了,就是将a所占的空间还给了操作系统。所以返回地址的同时,a所占空间销毁。

        p在存a的地址的时候,地址所对应的空间已经销毁。p虽然可以通过地址找到对应空间,但是p不能访问和使用这块空间。此时p就是野指针。


5.2 如何规避野指针

1. 指针初始化

2. 小心指针越界

3. 指针指向空间释放,及时置NULL

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

5. 指针使用之前检查有效性

代码示例:

#include <stdio.h>int main()
{int* p = NULL;int a = 10;p = &a;if (p != NULL){*p = 100;}return 0;
}

六、指针运算

6.1 指针+- 整数

        如果 p 指向数组元素 arr[ i ] ,那么 p+j 指向 arr[ i+j ]。

        如果 p 指向数组元素 arr[ i ] ,那么 p-j 指向 arr[ i-j ]。

代码示例1:

#include <stdio.h>int main()
{int arr[10] = { 0 };int i = 0;int sz = sizeof(arr) / sizeof(arr[0]);int* p = arr;for (i = 0; i < sz; i++){*p = 1;p++;}return 0;
}

调试窗口:


        

代码示例2:

#include <stdio.h>int main()
{int arr[10] = { 0 };int i = 0;int sz = sizeof(arr) / sizeof(arr[0]);int* p = arr;for (i = 0; i < sz; i++){*(p + i) = 1;	}return 0;
}

调试窗口:


6.2 指针-指针

        当两个指针相减时,结果为指针之间的距离,也就是两指针之间数组元素的个数。因此,若 p 指向 arr[ i ] 且 q 指向 arr[ j ],那么 p - q 就等于 i - j 。

代码示例:

#include <stdio.h>int main()
{int arr[10] = { 0 };printf("%d\n", &arr[9]-&arr[0]);return 0;
}

运行结果:

9

  • 指针-指针的绝对值得到的是指针和指针之间元素个数。
  • 不是所有指针都能相减,指向同一块空间的2个指针才能相减。

 代码示例:

#include <stdio.h>int my_strlen(char* str)
{char* start = str;while (*str != '\0'){str++;}return (str - start);
}
int main()
{int len = my_strlen("abcdefg");printf("%d\n", len);return 0;
}

运行结果:

7

6.3 指针的关系运算

        可以用关系运算符( < 、<= 、> 、>= )和判等运算符(== 、!=)进行指针比较。

代码示例:

#include <stdio.h>#define N_VALUES 10
int main()
{int values[N_VALUES] = { 0 };int* vp = NULL;for (vp = &values[N_VALUES]; vp > &values[0];){*--vp = 1;}return 0;
}

        上面代码可以简化成下方代码吗?

#include <stdio.h>#define N_VALUES 10
int main()
{int values[N_VALUES] = { 0 };int* vp = NULL;for (vp = &values[N_VALUES - 1]; vp >= &values[0]; vp--){*vp = 1;}return 0;
}

        实际在绝大部分的编译器上是可以顺利完成任务的,然而我们还是应该避免这样写,因为标准并不保证它可行。

标准规定:

        允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。


总结

        以上就是今天要讲的内容,本文仅仅简单介绍了指针。

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

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

相关文章

前端Vue中自定义Popup弹框、按钮及内容的设计与实践

标题&#xff1a;前端Vue中自定义Popup弹框、按钮及内容的设计与实践 一、引言 在Web前端开发中&#xff0c;弹框&#xff08;Popup&#xff09;是一种常见的用户界面元素&#xff0c;用于向用户显示额外的信息或提供额外的功能。然而&#xff0c;标准的弹框往往不能满足所有…

Django学习3——靓号管理

目录 靓号管理 表结构和数据 根据表结构的需求&#xff0c;在models.py中创建类&#xff08;由类生成数据库中的表&#xff09; 在数据库生成表 自己在数据模拟创建一些数据&#xff1a; 靓号列表 新建靓号 编辑靓号 删除靓号 搜索靓号 靓号管理 表结构和数据 根…

DHCP学习记录

目录 客户端向DHCP服务端申请租用IP的4个阶段: 客户端向HDCP服务器续租IP过程: 客户端重新连接租用IP过程: 客户端释放IP 声明: (Dynamic Host Configuration Protocol)动态主机配置协议&#xff0c;客户端向DHCP服务端申请获得ip的一种约定俗成的话语(协议) 手工配置方式…

啊哈c语言——4.10、for隆重登场(一起来找茬)

下面这段代码是求12345678910的值。其中有4个错误&#xff0c; 快来改正吧&#xff01; 改正后&#xff1a; #include <stdio.h> #include <stdlib.h> int main( ) {int i, sum;sum1;for(i1; i<10;i){sumsum*i;}printf("%d", sum);system("paus…

如何在无公网IP环境使用Windows远程桌面Ubuntu

文章目录 一、 同个局域网内远程桌面Ubuntu二、使用Windows远程桌面连接三、公网环境系统远程桌面Ubuntu1. 注册cpolar账号并安装2. 创建隧道&#xff0c;映射3389端口3. Windows远程桌面Ubuntu 四、 配置固定公网地址远程Ubuntu1. 保留固定TCP地址2. 配置固定的TCP地址3. 使用…

appium安装运行报错的解决方案

appium版本2.3 java17 运行报错&#xff1a; Caused by: org.openqa.selenium.SessionNotCreatedException: Could not start a new session. Response code 500. Message: An unknown server-side error occurred while processing the command. Original error: Could not…

40道MyBatis面试题带答案(很全)

1. 什么是MyBatis &#xff08;1&#xff09;Mybatis是一个半ORM&#xff08;对象关系映射&#xff09;框架&#xff0c;它内部封装了JDBC&#xff0c;开发时只需要关注SQL语句本身&#xff0c;不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。程序员直接…

海康visionmaster-渲染结果:通过绑定流程或模块获取 渲染结果的方法

描述 环境&#xff1a;VM4.0.0 VS2015 及以上 现象&#xff1a;方案或流程运行执行之后&#xff0c;就可以获取结果&#xff0c;可以通过获取渲染结果和数据结果&#xff0c; 渲染结果通过绑定渲染控件进行显示。 解答 渲染结果的显示可以通过渲染控件绑定流程或者模块&#x…

RSA加密解密——用shell加密java解密

功能描述 使用shell opensll对明文进行RSA加密&#xff0c;将密文用java的RSA工具对密文解密。这应该是全网第一个同时用到shell和java的RSA加密解密教程。中间有很多坑&#xff0c;都踩过了&#xff0c;可以放心使用代码。 正确的实现流程 shell端 首先生成公钥私钥 &…

【华为机试】2023年真题B卷(python)-冠亚军排名-奖牌榜排名

一、题目 题目描述&#xff1a; 2012伦敦奥运会即将到来&#xff0c;大家都非常关注奖牌榜的情况&#xff0c;现在我们假设奖牌榜的排名规则如下. 1.首先gold medal数量多的排在前面 2.其次silver medal数量多的排在前面 3.然后bronze medal数量多的排在前面 4.若以上三个条…

【数据结构】栈和队列(栈的基本操作和基础知识)

&#x1f308;个人主页&#xff1a;秦jh__https://blog.csdn.net/qinjh_?spm1010.2135.3001.5343&#x1f525; 系列专栏&#xff1a;《数据结构》https://blog.csdn.net/qinjh_/category_12536791.html?spm1001.2014.3001.5482 目录 前言 栈 栈的概念和结构 栈的实现 ​…

【时钟】分布式时钟HLC|Logical Time|Vector Clock|True Time

目录 简略 详细 附录 1 分布式系统不能使用NTP的原因 简略 分布式系统中不同于单机系统不能使用NTP(网络时间协议&#xff08;Network Time Protocol&#xff09;)来获取时间&#xff0c;所以我们需要一个特别的方式来获取分布式系统中的时间&#xff0c;mvcc也是使用time保证读…

信号处理设计模式

问题 如何编写信号安全的应用程序&#xff1f; Linux 应用程序安全性讨论 场景一&#xff1a;不需要处理信号 应用程序实现单一功能&#xff0c;不需要关注信号 如&#xff1a;数据处理程序&#xff0c;文件加密程序&#xff0c;科学计算程序 场景二&#xff1a;需要处理信…

HTML---利用CSS3制作网页动画

文章目录 目录 文章目录 本章目标 一.CSS3概述 CSS函数概述 二.CSS3变形 transform属性 translate()&#xff1a;平移函数 scale()&#xff1a;缩放函数 rotate()&#xff1a;旋转函数 skew()&#xff1a;倾斜函数 三.CSS3过渡 四.CSS动画 练习 旋转按钮 本章目标 会使用…

“从零到一“基于Freeswitch二次开发: 应用架构设计(二)

一、架构分享 上一篇文章“从零到一“基于Freeswitch二次开发:Freeswitch入门与网络架构 (一) 对Freeswitch二次开发做了一个介绍&#xff0c;距离这篇文章的发布时间有点久了&#xff0c;之前一直没时间把下文补上来。正好到了年末想起来&#xff0c;就把我们的一个实现架构进…

深度神经网络结构

单层的感知机不能解决“异或”问题。 在前面分别介绍了M-P神经元模型和感知机模型。在M-P神经元模型中&#xff0c;神经元接收到若干个输入信号&#xff0c;并将计算得到的加权后的总输入&#xff0c;经过激活函数的处理&#xff0c;最终产生神经元的输出。而感知机模型则由两层…

腾讯云标准型S5服务器2核2G、2核4G和4核8G五年机来了

腾讯云五年特价服务器来了&#xff0c;标准型S5云服务器&#xff0c;可选2核2G、2核4G和4核8G配置&#xff0c;一次性购买五年低至2折&#xff0c;免去续费贵烦恼。腾讯云百科txybk.com分享腾讯云5年服务器特价优惠活动、购买条件、云服务器配置及优惠价格&#xff1a; 腾讯云五…

linux文件夹介绍

在linux内核文件夹下面存在着许多文件夹&#xff0c;那么那些文件夹是什么用处呢&#xff0c;下面将为你介绍。 (1)documentation 这个文件夹下没有内核代码&#xff0c;仅仅有一套实用的文档&#xff0c;但这些文档的质量不一。比如内核文档的文件系统&#xff0c;在该文件夹下…

计算机网络——传输层(五)

前言&#xff1a; 最重要的网络层我们已经学习完了&#xff0c;下面让我们再往上一层&#xff0c;对网络层的上一层传输层进行一个学习与了解&#xff0c;学习网络层的基本概念和网络层中的TCP协议和UDP协议 目录 ​编辑一、传输层的概述&#xff1a; 1.传输层&#xff1a; …

leetcode 315. 计算右侧小于当前元素的个数(hard)【小林优质解法】

链接&#xff1a;力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 代码&#xff1a; class Solution {int[]counts; //用来存储结果int[]index; //用来绑定数据和原下标int[]helpNums; //用于辅助排序 nums 数组int[]helpIndex; //用于辅助排序 i…