C语言 【初始指针】【指针一】

引言

        思绪很久,还是决定写一写指针,指针这块内容很多,也不是那么容易说清楚,这里尽可能写地详细,让大家理解指针。(未完序)

一、内存和地址

        在讲指针前,需要有一个对内存和地址的认识,不然后面的指针不是那么容易理解。在我们的内存中:一个字节里存储着 0 或 1 的信息。那计算机是怎么快速找到对应的信息的呢?

        回答:在内存中每个字节都有自己对应的地址,计算机通过找到地址,就能访问地址里面的信息。

        那么指针,就是一种可以存储地址的数据类型。

二、一级指针

1.存地址: 

         重点:其实指针是很简单的,只要会使用int ,float, double,long long 等数据类型,那么指针变量,你也一定会使用。

         先介绍一下指针变量是怎么创建的:

        对应数据类型的后面加上*,就可以存储该类型的内存地址

认识个东西 : & 取地址操作符,看下面的代码:

	int b = 10;int* a = &b;

        这段代码创建了一个变量b,&b,就可以取出b的地址。
int* 就是int类型的指针变量,a 里面存的就是  b 的地址

通过调试来看一下:

 

可以看到 b 的地址 

下面通过监视窗口,看一下a里面存的是什么:

发现就是b的地址。

类似地,如果要存float数据类型的地址,就要创建float类型的指针变量:

	float c = 1.2;float* d = &c;

d中存储的就是 c 的地址。

其他的数据类型一样,包括自定义类型的数据。

可以自己多试试。

2.解读地址对应的内容: 

要用到一个东西:解引用操作符:*

使用起来也特别简单:

比如上面代码中,a中存储的是b的地址,*a,就可以找到b对应位置的内容了

来看代码: 

#include<stdio.h>int main()
{int b = 10;int* a = &b;int c = *a;  //*a 等价于 bprintf("%d\n", c);*a = 20;   //改变a地址里面的内容,就是把b的内容给改了printf("%d\n", b);return 0;
}

运行结果:

        是不是特别简单,认为指针难,是因为你不理解每个符号的内容,这里给拆开来讲,相信你一定明白了

3.指针变量的大小

int在内存中占4个字节,float在内存中占4个字节,double在内存中占8个字节,和int,float,double等类型一样,指针类型在内存中也是占有字节的。

        那指针类型在内存中占多少个字节呢?

先给出结论,下面来看代码证明。

• 32位平台下地址是32个bit位,指针变量大小是4个字节

• 64位平台下地址是64个bit位,指针变量大小是8个字节

• 注意指针变量的大小和类型是无关的,只要指针类型的变量,在相同的平台下,大小都是相同的。(和CPU里面的线路有一定的关系)

sizeof操作符同样可以返回指针类型在内存中占多少个字节。 

#include<stdio.h>
int main()
{printf("%zd\n", sizeof(char*));printf("%zd\n", sizeof(short*));printf("%zd\n", sizeof(int*));printf("%zd\n", sizeof(double*));return 0;
}

 在不同平台下运行这段代码:

在32位平台下:

运行结果: 

在64位平台下: 

 运行结果:

 三、指针变量类型的意义

你是不是会有这么个疑问:

        指针变量的大小和类型无关,只要是指针变量,在同一个平台下,大小都是一样的,为什么还要有各种各样的指针类型呢?

其实指针类型是有特殊意义的,通过两中方法来理解一下。

1.指针的解引用

下面看两段代码: 

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

代码二中给int*类型,强制转换成了char*类型。最后都解引用后赋值0

通过调试,来看一下两段代码在内存中的存储。

代码一:

代码二:
        相信聪明的你一定发现了不同,代码1会将n的4个字节全部改为0,但是代码2只是将n的第一个字节改为0。

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

 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个字节。 这就是指针变量的类型差异带来的变化。指针+1,其实跳过1个指针指向的元素。指针可以+1,也可以-1。 

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

四、void* 指针 

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

 来看代码:

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

这段代码在编译的时候肯定是会报警告的。(因为类型不兼容)

#include <stdio.h>
int main()
{int a = 10;void* pa = &a;void* pc = &a;//*pa = 10; 这样写是错误的//*pc = 0;return 0;
}

void* 类型的指针可以接收不同类型的地址,但是无法直接进行指针运算。

        一般 void* 类型的指针是使用在函数参数的部分,用来接收不同类型数据的地址,这样的设计可以实现泛型编程的效果。

五、指针运算 

1.指针+-整数

        上面的一个代码已经可以看出这个功能了,这里再通过一个案例来理解一下(也算是一个小的练习)

        

通过地址来访问一个数组。

#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 (int i = 0; i < sz; i++){printf("%d ", *(p + i));}return 0;
}

运行结果:

 从这个案例中可以看出:*(p + i) 等价于 p[i]。(其实就是这样)

这也可以说明数组名是就是数组首元素的地址。

不过有来个特例需要记一下:

1. &arr,对数组名取地址,得到的是整个数组的地址,而不是首元素的地址。

2. sizeof(arr),这里面的arr也是整个数组的地址,而不是首元素的地址。

把上面代码改一下来证明一下:

#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 (int i = 0; i < sz; i++){//printf("%d ", *(p + i));printf("%d ", p[i]);}return 0;
}

只改了一个地方,就是输出位置。

运行结果:

是不是又增加了新知识,嘿嘿(●ˇ∀ˇ●) 

 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;
}

运行结果: 

3.指针的关系运算

指针之间也是可以比较大小的

来看代码:

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

运行结果: 

是不是又被震惊到了,哇呜,竟然还可以这么写。

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

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

相关文章

深入理解pthread多线程编程:从基础到生产者-消费者模型

前言 在多核处理器普及的今天&#xff0c;多线程编程已成为提高程序性能的重要手段。POSIX线程&#xff08;pthread&#xff09;是Unix/Linux系统下广泛使用的多线程API。本文将系统介绍pthread的关键概念&#xff0c;包括线程初始化、死锁预防、递归锁使用&#xff0c;并通过…

springboot 对接马来西亚数据源API等多个国家的数据源

使用Spring Boot对接StockTV全球金融数据API指南 StockTV提供了覆盖股票、外汇、期货和加密货币的全球化金融数据接口。本文将通过Spring Boot实现对这些API的快速对接&#xff0c;并提供完整的代码示例。 一、前期准备 1. 获取API Key 访问StockTV官网联系客服获取API Key…

软件测试常用设计模式

设计模式的重要原则就是&#xff1a;高内聚、低耦合&#xff1b;通常程序结构中各模块的内聚程度越高&#xff0c;模块间的耦合程度就越低。 数据驱动测试&#xff1a;Data Driven Testing&#xff0c;简称DDT&#xff1b; 数据驱动指的是从数据文件&#xff08;如数据库、Ex…

基于 Fluent-Bit 和 Fluentd 的分布式日志采集与处理方案

#作者&#xff1a;任少近 文章目录 需求描述系统目标系统组件Fluent BitFluentdKafka 数据流与处理流程日志采集日志转发到 Fluentd日志处理与转发到 KafkaKafka 作为消息队列 具体配置Fluent-Bit的CM配置Fluent-Bit的DS配置Fluentd的CM配置Fluentd的DS配置Kafka查询结果 需求…

正则表达式(Regular Expression,简称 Regex)

一、5w2h&#xff08;七问法&#xff09;分析正则表达式 是的&#xff0c;5W2H 完全可以应用于研究 正则表达式&#xff08;Regular Expressions&#xff09;。通过回答 5W2H 的七个问题&#xff0c;我们可以全面理解正则表达式的定义、用途、使用方法、适用场景等&#xff0c…

爬虫获取1688关键字搜索接口的实战指南

在当今电商行业竞争激烈的环境下&#xff0c;数据的重要性不言而喻。1688作为国内领先的B2B电商平台&#xff0c;拥有海量的商品信息&#xff0c;这些数据对于商家的市场分析、选品决策、价格策略制定等都有着重要的价值。本文将详细介绍如何通过爬虫技术获取1688关键字搜索接口…

如何快速解决django存储session变量时出现的django.db.utils.DatabaseError错误

我们在学习django进行web编程的时候&#xff0c;有时需要将一些全局变量信息存储在session中&#xff0c;但使用过程中&#xff0c;却发现会引起数据库的报错。通过查看django源码信息&#xff0c;发现其对session信息进行了ORM映射&#xff0c;如果数据库中不存在对应的表信息…

C语言复习--assert断言

assert.h 头⽂件定义了宏 assert() &#xff0c;⽤于在运⾏时确保程序符合指定条件&#xff0c;如果不符合&#xff0c;就报错终止运行。这个宏常常被称为“断⾔”。 assert(p ! NULL); 代码在程序运⾏到这⼀⾏语句时&#xff0c;验证变量 p 是否等于 NULL 。如果确实不等于 NU…

STL新增内容

文章目录 C11 中的 STL 新增内容容器算法 C14 中的 STL 新增内容容器算法 C17 中的 STL 新增内容容器算法 C20 中的 STL 新增内容容器算法 C11 中的 STL 新增内容 容器 std::array&#xff1a;这是一个固定大小的数组容器&#xff0c;和原生数组类似&#xff0c;但具备更好的…

C#测试Excel开源组件ExcelDataReader

使用微软的com组件Microsoft.office.Interop.Excel读写Excel文件虽然可用&#xff0c;但是列多、行多的时候速度很慢&#xff0c;之前测试过Sylvan.Data.Excel包的用法&#xff0c;如果只是读取Excel文件内容的话&#xff0c;还可以使用ExcelDataReader包&#xff0c;后者是C#开…

位置编码汇总 # 持续更新

看了那么多还没有讲特别好的&#xff0c;GPT老师讲的不错关于三角函数编码。 一、 手撕transformer常用三角位置编码 GPT说&#xff1a;“低维度的编码&#xff08;例如&#xff0c;第一个维度&#xff09;可以捕捉到大的位置差异&#xff0c;而高维度的编码则可以捕捉到小的细…

Java 模块系统深度解析

Java 模块系统深度解析 Java 模块系统&#xff08;Java Platform Module System, JPMS&#xff09;是 Java 9 引入的一项重要特性&#xff0c;它从根本上改变了 Java 应用程序的打包和依赖管理方式。本文将全面介绍 Java 模块系统的核心概念、优势及实际应用。 一、为什么需要…

蓝桥杯杯赛-日期模拟

知识点 处理日期 1. 按天枚举日期&#xff1a;逐天遍历起始日期到结束日期范围内的每个日期。 2. 处理闰年&#xff1a;正确判断闰年条件。闰年定义为&#xff1a;年份 满足以下任意一个条件&#xff1a;(闰年的2月只有29天) 满足下面一个条件就是闰年 1> 是 400 的倍数…

.Net中对称加密的实现

常见对称加密算法及优缺点 1. DES&#xff08;Data Encryption Standard&#xff09; 优点&#xff1a;是最早被广泛应用的加密算法&#xff0c;算法公开&#xff0c;实现简单&#xff0c;效率较高。缺点&#xff1a;密钥长度较短&#xff08;56 位&#xff09;&#xff0c;在…

SQLMesh调度系统深度解析:内置调度与Airflow集成实践

本文系统解析SQLMesh的两种核心调度方案&#xff1a;内置调度器与Apache Airflow集成。通过对比两者的适用场景、架构设计和操作流程&#xff0c;为企业构建可靠的数据分析流水线提供技术参考。重点内容包括&#xff1a; 内置调度器的轻量级部署与性能优化策略Airflow集成的端到…

centos线程数查看

查看当前最大支持的线程数 cat /proc/sys/kernel/threads-max当前用户进程可以创建的最大线程数&#xff08;包括子进程&#xff09; [rootlocalhost tmp]# ulimit -u得到当前实际的线程数 [rootlocalhost tmp]# ps -eLf | wc -l统计每个进程的总线程数前20的数据 [rootloc…

【大模型】视觉语言模型:Qwen2.5-VL的使用

官方github地址&#xff1a;https://github.com/QwenLM/Qwen2.5-VL 目录 Qwen家族的最新成员&#xff1a;Qwen2.5-VL 主要增强功能 模型架构更新 快速开始 使用Transformers聊天 Docker Qwen家族的最新成员&#xff1a;Qwen2.5-VL 主要增强功能 强大的文档解析功能&am…

HDMI接口设计

1. HDMI简介 HDMI(High Definition Multimedia Interface)高清多媒体接口,是首个支持在单线缆上传输,不经过压缩的全数字高清晰度、多声道音频和智能格式与控制命令数据的数字接口。这个接口可以同时传输视频信号、音频信号和控制信号。 从上图里面可以看到HDMI有3组数据信号…

C/C++ JSON 库综合对比及应用案例(六)

第六部分&#xff1a;C/C JSON 库综合对比及应用案例 &#x1f4e2; 快速掌握 JSON&#xff01;文章 视频双管齐下 &#x1f680; 如果你觉得阅读文章太慢&#xff0c;或者更喜欢 边看边学 的方式&#xff0c;不妨直接观看我录制的 JSON 课程视频&#xff01;&#x1f3ac; …

LXC 导入(Rockylinux,almalinux,oraclelunx,debian,ubuntu,openEuler,kail,opensuse)

前提要求 ubuntu下安装lxd 参考Rockylinux下安装lxd 参考LXC 源替换参考LXC 容器端口发布参考LXC webui 管理<