指针初阶(1)

文章目录

  • 目录
    • 1. 指针是什么
    • 2. 指针变量的类型
      • 2.1 指针变量+-整数
      • 2.2 指针变量的解引用
    • 3. 野指针
      • 3.1 野指针成因
      • 3.2 如何规避野指针
    • 4. 指针运算
      • 4.1 指针+-整数
      • 4.2 指针-指针
      • 4.3 指针的关系运算
  • 附:

目录

  • 指针是什么
  • 指针变量的类型
  • 野指针
  • 指针运算
  • 指针和数组
  • 二级指针
  • 指针数组

1. 指针是什么

  1. 指针是内存中一个最小单元(1个字节)的编号,也就是地址
  2. 平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量(存放在指针变量中的值都被当成地址处理)

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

#include <stdio.h>int main()
{int a = 100;int * pa = &a;//pa是专门用来存放地址(指针)的,这里的pa就被称为指针变量//指针变量在32位平台下是4个字节//指针变量在64位平台下是8个字节//int arr[10];//printf("%p", &a);return 0;
}

注:

  • 经过仔细的计算和权衡,我们发现一个字节一个对应的地址是比较合适的。
  • 对于32位的机器,假设有32根地址线,那么假设每根地址线在寻址的时候产生高电频(高电压)和低电频(低电压),就是(1或者0),那么32根地址线会产生2的32次方个地址,每个地址标识一个字节,那我们就可以给 (2^32Byte == 2^32/1024KB == 2^32/1024/1024MB==2^32/1024/1024/1024GB == 4GB) 4G的空间进行编址,64位机器同理。
  • 32位的机器上,地址是32个0或者1组成二进制序列,那地址就得用4个字节的空间来存储,所以一个指针变量的大小就应该是4个字节
  • 那如果在64位机器上,如果有64个地址线,那一个指针变量的大小是8个字节,才能存放一个地址。
#include <stdio.h>int main()
{printf("%d\n", sizeof(char*));printf("%d\n", sizeof(short*));printf("%d\n", sizeof(int*));printf("%d\n", sizeof(long*));printf("%d\n", sizeof(float*));printf("%d\n", sizeof(double*));return 0;
}

2. 指针变量的类型

char * pc = NULL;
short * ps = NULL;
int * pi = NULL;
long * pl = NULL;
float * pf = NULL;
double * pd = NULL;

这里可以看到,指针变量的定义方式是: type + *

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

但是,指针变量的大小又和指针变量的类型无关,那么指针变量的类型的意义是什么呢?

2.1 指针变量±整数

#include <stdio.h>int main()
{int a = 0x11223344;//0x开头的是16进制数字int * pa = &a;char * pc = &a;printf("%p\n", pa);//010FFE7Cprintf("%p\n", pc);//010FFE7Cprintf("%p\n", pa+1);//010FFE80printf("%p\n", pc+1);//010FFE7Dreturn 0;
}

总结: 指针变量的类型决定了指针向前或者向后走一步有多大(距离)。

2.2 指针变量的解引用

#include <stdio.h>int main()
{int a = 0x11223344;//0x开头的是16进制数字char * pa = &a;*pa = 0;return 0;
}

指针变量的解引用(1)

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

指针变量的解引用(2)
总结: 指针变量的类型决定了对指针变量解引用的时候有多大的权限(能操作几个字节),比如: char* 的指针变量解引用就只能访问一个字节,而 int* 的指针变量解引用就能访问四个字节

3. 野指针

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

3.1 野指针成因

  1. 指针未初始化
#include <stdio.h>int main()
{int* p;//局部变量不初始化的时候,内容是随机值*p = 20;printf("%d\n", *p);return 0;
}
  1. 指针越界访问
#include <stdio.h>int main()
{int arr[10] = { 0 };int * p = arr;int i = 0;for (i = 0; i <= 11; i++){//当指针指向的范围超出数组arr的范围时,p就是野指针*(p++) = i;}return 0;
}
  1. 指针指向的空间释放

放在动态内存开辟的时候讲解,这里可以简单提示一下。

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

3.2 如何规避野指针

  1. 指针初始化
  2. 小心指针越界
  3. 指针指向空间释放,及时置NULL
  4. 避免返回局部变量的地址
  5. 指针使用之前检查有效性
#include <stdio.h>int main()
{int a = 10;int* p = &a;int* ptr = NULL;//ptr是一个空指针,没有指向任何有效的空间,这个指针不能直接使用//int* ptr2;//野指针if (ptr != NULL){//使用}return 0;
}

4. 指针运算

4.1 指针±整数

#include <stdio.h>int main()
{int arr[10] = { 0 };//不使用下标访问数组int* p = &arr[0];int i = 0;int sz = sizeof(arr) / sizeof(arr[0]);for (i = 0; i < sz; i++){*p = i;p++;//p = p + 1}p = arr;for (i = 0; i < sz; i++){printf("%d ", *(p + i));//p + i}/*for (i = 0; i < sz; i++){printf("%d ", arr[i]);}*/return 0;
}
#define N_VALUES 5int main()
{float values[N_VALUES];float *vp;//指针+-整数;指针的关系运算for (vp = &values[0]; vp < &values[N_VALUES]; ){*vp++ = 0;}return 0;
}

注:

//int arr[10]
//int* p = arr;
//*(p+i) == arr[i]
//*(arr+i) == arr[i]//arr[i] == *(arr+i) == *(i+arr) == i[arr]int main()
{int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };int i = 0;for (i = 0; i < 10; i++){printf("%d ", i[arr]);//[] 操作符}//2+3 --> 3+2//arr[i] --> i[arr]return 0;
}

4.2 指针-指针

//地址-地址
//指针-指针#include <stdio.h>int main()
{int arr[10] = { 0 };printf("%d\n", &arr[9] - &arr[0]);//9printf("%d\n", &arr[0] - &arr[9]);//-9return 0;
}//指针-指针得到的数值的绝对值是指针和指针之间的元素个数

之前我们学习过如何通过自己的函数来实现计算字符串长度:

#include <stdio.h>int my_strlen(char* s)
{int count = 0;while (*s != '\0'){count++;s++;}return count;
}int main()
{char arr[] = "abcdef";int len = my_strlen(arr);printf("%d\n", len);return 0;
}
#include <stdio.h>int my_strlen(char* s)
{if ('\0' == *s){return 0;}else{return 1 + my_strlen(s + 1);}}int main()
{char arr[] = "abcdef";int len = my_strlen(arr);printf("%d\n", len);return 0;
}

其实,这也能通过指针-指针来实现:

#include <stdio.h>int my_strlen(char* s)
{char* start = s;while (*s != '\0'){s++;}return s - start;
}int main()
{char arr[] = "abcdef";int len = my_strlen(arr);printf("%d\n", len);return 0;
}
#include <stdio.h>int my_strlen(char* s)
{char* start = s;while (*s)//a b c d e f \0 -> 0{s++;}return s - start;
}int main()
{char arr[] = "abcdef";int len = my_strlen(arr);printf("%d\n", len);return 0;
}
#include <stdio.h>int my_strlen(char* s)
{char* start = s;while (*s++){;}return s - start - 1;
}int main()
{char arr[] = "abcdef";int len = my_strlen(arr);printf("%d\n", len);return 0;
}

注:

#include <stdio.h>int main()
{int arr[10] = { 0 };char ch[5] = { 0 };//指针和指针相减的前提是:两个指针指向了同一块空间printf("%d\n", &ch[4] - &arr[0]);//errreturn 0;
}

4.3 指针的关系运算

#define N_VALUES 5int main()
{float values[N_VALUES];float *vp;for(vp = &values[N_VALUES]; vp > &values[0];){*--vp = 0;}return 0;
}

这段代码也可以这样写:

#define N_VALUES 5int main()
{float values[N_VALUES];float *vp;for(vp = &values[N_VALUES-1]; vp >= &values[0];vp--){*vp = 0;}return 0;
}

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

标准规定:

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

附:

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

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

相关文章

vb与EXCEL的连接

一、 VB读写EXCEL表&#xff1a; VB本身提自动化功能可以读写EXCEL表&#xff0c;其方法如下&#xff1a; 1、在工程中引用Microsoft Excel类型库&#xff1a; 从"工程"菜单中选择"引用"栏&#xff1b;选择Microsoft Excel 9.0 Object Library&#xff…

Linux下使用I2C接口与AS5600角度传感器

在Linux下使用I2C接口与AS5600角度传感器进行通信的步骤如下&#xff1a; 确保I2C总线驱动程序已加载&#xff1a;在Linux中&#xff0c;使用I2C总线之前需要确保I2C驱动程序已加载。你可以通过运行以下命令来检查是否已加载i2c-dev驱动程序&#xff1a; ls /dev/i2c-*如果没…

面试总结(三)

1.进程和线程的区别 根本区别&#xff1a;进程是操作系统分配资源的最小单位&#xff1b;线程是CPU调度的最小单位所属关系&#xff1a;一个进程包含了多个线程&#xff0c;至少拥有一个主线程&#xff1b;线程所属于进程开销不同&#xff1a;进程的创建&#xff0c;销毁&…

基于Matlab实现帧间差分法的运动目标检测(附上完整源码+图像+程序运行说明)

帧间差分法是一种常用的运动目标检测方法&#xff0c;可以通过对连续帧之间的差异进行分析来确定目标的运动情况。在本文中&#xff0c;我们将介绍如何使用Matlab实现帧间差分法的运动目标检测。 文章目录 部分源码完整源码图像程序运行说明下载 部分源码 首先&#xff0c;我们…

【Vue组件eval方法的使用】

Vue页面中条件可以放在当前vue页面中而无需影响到组件 如 这是我的表格操作列按钮&#xff0c;需求是第四个按钮如果表格当前数据的is_execl字段为0则显示否则隐藏 这种条件判断很频繁 如果像之前一样给一个标识&#xff0c;页面多了就难以维护&#xff0c;而且判断条件如果不…

【硬件设计】模拟电子基础一--元器件介绍

模拟电子基础一--元器件介绍 一、半导体&#xff08;了解&#xff09;1.1 基础知识1.2 PN结 二、二级管2.1 定义与特性2.2 二极管的分类 三、三级管四、MOS管三、其他元器件管3.1 电容3.2 光耦3.3 发声器件3.4 继电器3.5 瞬态电压抑制器 前言&#xff1a;本章为知识的简单复习&…

【Spring框架】SpringBoot创建和使用

目录 什么是SpringBoot&#xff1f;SpringBoot优点创建SpringBootSpringBoot使用 什么是SpringBoot&#xff1f; Spring 的诞⽣是为了简化 Java 程序的开发的&#xff0c;⽽ Spring Boot 的诞⽣是为了简化 Spring 程序开发的。 SpringBoot优点 1.起步依赖(创建的时候就可以方…

Python爬虫遇到URL错误解决办法大全

在进行Python爬虫任务时&#xff0c;遇到URL错误是常见的问题之一。一个错误的URL链接可能导致爬虫无法访问所需的网页或资源。为了帮助您解决这个问题&#xff0c;本文将提供一些实用的解决方法&#xff0c;并给出相关代码示例&#xff0c;希望对您的爬虫任务有所帮助。 一、…

docker容器操作(第二篇)

目录 五、Docker 容器操作 1、创建容器与运行容器 2、容器的启动与停止 3、容器的运行与终止 4、容器的进入 5、容器的导出与导入 6、容器的删除 7、文件复制 8、查看容器资源使用率 9、查看容器进程状态 10、更新容器配置 五、Docker 容器操作 容器是Docker 的另一个…

玩转顺序表——【数据结构】

在C语言学习中&#xff0c;我们经常会遇见增删查改等一系列操作&#xff0c;而这些操作全都与线性表关联&#xff0c;没有线性表将会对这些操作完成的十分艰难&#xff01;那今天就让我们来了解一下顺序表如何增删查改&#xff01;&#xff01;&#xff01; 目录 1.线性表 2…

运算放大器(二):恒流源

一、实现原理 恒流源的输出电流能够在一定范围内保持稳定&#xff0c;不会随负载的变化而变化。 通过运放&#xff0c;将输入的电压信号转换成满足一定关系的电流信号&#xff0c;转换后的电流相当一个输出可调的简易恒流源。 二、电路结构 常用的恒流源电路如…

C语言每日一题:11.《数据结构》链表分割。

题目一&#xff1a; 题目链接&#xff1a; 思路一&#xff1a;使用带头链表 1.构建两个新的带头链表&#xff0c;头节点不存储数据。 2.循环遍历原来的链表。 3.小于x的尾插到第一个链表。 4.大于等于x尾插到第二个链表。 5.进行链表合并&#xff0c;注意第二个链表的尾的下一…

【【STM32学习-3】】

STM32学习-3 下面是对c语言的稍微复习 这个是我们设置好的文件 以后拖出去用就可以了 这里加入关于指针的感想 关于指针数组和数组指针的想法 常规的东西是int a10; int * p&a; &#xff08;p指向了a元素&#xff0c;意思是p等于a的地址 类型是int*&#xff09;就是 整型指…

二十三种设计模式第二十篇--备忘录模式

备忘录模式&#xff0c;备忘录模式属于行为型模式。它允许在不破坏封装的情况下捕获和恢复对象的内部状态。保存一个对象的某个状态&#xff0c;以便在适当的时候恢复对象&#xff0c;该模式通过创建一个备忘录对象来保存原始对象的状态&#xff0c;并将其存储在一个负责管理备…

基于ts的浏览器缓存工具封装(含源码)

cache.ts缓存工具 浏览器缓存工具封装实现使用方法示例代码 浏览器缓存工具封装 在前端开发中&#xff0c;经常会遇到需要缓存数据的情况&#xff0c;例如保存用户的登录状态、缓存部分页面数据等 但有时候需要缓存一些复杂的对象&#xff0c;例如用户信息对象、设置配置等。…

2、Tomcat介绍(下)

组件分类 在Apache Tomcat中&#xff0c;有几个顶级组件&#xff0c;它们是Tomcat的核心组件&#xff0c;负责整个服务器的运行和管理。这些顶级组件包括&#xff1a; Server(服务器)&#xff1a;Tomcat的server.xml配置文件中的<Server>元素代表整个Tomcat服务器实例。每…

【MySQL】模具更新方案

系列文章 C#底层库–MySQLBuilder脚本构建类&#xff08;select、insert、update、in、带条件的SQL自动生成&#xff09; 本文链接&#xff1a;https://blog.csdn.net/youcheng_ge/article/details/129179216 C#底层库–MySQL数据库操作辅助类&#xff08;推荐阅读&#xff0…

【C++】STL——vector的模拟实现、常用构造函数、迭代器、运算符重载、扩容函数、增删查改

文章目录 1.模拟实现vector1.1构造函数1.2迭代器1.3运算符重载1.4扩容函数1.5增删查改 1.模拟实现vector vector使用文章 1.1构造函数 析构函数 在C中&#xff0c;vector是一个动态数组容器&#xff0c;可以根据需要自动调整大小。vector类提供了几个不同的构造函数来创建和初…

深度学习笔记-暂退法(Drop out)

背景 在机器学习的模型中&#xff0c;如果模型的参数太多&#xff0c;而训练样本又太少&#xff0c;训练出来的模型很容易产生过拟合的现象。在训练神经网络的时候经常会遇到过拟合的问题&#xff0c;过拟合具体表现在&#xff1a;模型在训练数据上损失函数较小&#xff0c;预…

【Python目标识别】Labelimg标记深度学习(YOLO)样本

人工智能、ai、深度学习已经火了很长一段时间了&#xff0c;但是还有很多小伙伴没有接触到这个行业&#xff0c;但大家应该多多少少听过&#xff0c;网上有些兼职就是拿电脑拉拉框、数据标注啥的&#xff0c;其实这就是在标记样本&#xff0c;供计算机去学习。所以今天跟大家分…