【C初阶——指针1】鹏哥C语言系列文章,基本语法知识全面讲解——指针(1)

news2025/5/24 15:48:51

长城之上是千亿的星空,星空之上是不畏的守望。
本文由@睡觉待开机原创,转载请注明出处。
本内容在csdn网站首发
欢迎各位点赞—评论—收藏
如果存在不足之处请评论留言,共同进步!


文章目录

  • 前言
  • 1.内存和地址
  • 2.指针变量和地址
    • 2.1指针变量的介绍
    • 2.2指针变量的大小
  • 3.指针变量不同类型的意义
    • 3.1不同指针类型的解引用不同
    • 3.2不同指针类型的加减整数效果不同
    • 3.3void*指针
  • 4.const修饰指针
  • 5.指针运算
    • 5.1指针+-整数运算
    • 5.2指针-指针运算
    • 5.3指针的关系运算
  • 6.野指针
    • 6.1指针未初始化
    • 6.2指针越界访问
    • 6.3指针指向的空间被释放
  • 7.如何预防野指针?
    • 7.1指针初始化
    • 7.2小心指针越界
    • 7.3指针不再使用时,及时置为NULL
    • 7.4避免返回局部变量的地址
  • 8.assert断言
    • 指针的意义体会——传值调用和传址调用的区分
    • 8.2传址调用与传值调用的选择?


前言

指针是C语言的灵魂所在,指针重要性可见一斑。


1.内存和地址

这是在了解指针之前的必备知识,不了解内存和地址就开始介绍指针,类似于不打地基就开始建造房子的感觉。
内存:是我们电脑中的硬件之一,用于存储数据的硬件。
地址:为了便于管理内存,我们把庞大的内存空间(8GB/16GB/32GB)划分为每个为1字节的空间单元,并为每个空间单元赋一个编号,这个编号我们称之为地址。
至于如何实现为每个内存空间进行编址,主要是硬件层面进行实现的,我们不予详细说明。

2.指针变量和地址

2.1指针变量的介绍

在说明指针变量之前,首先要认识几个操作符
1.&取地址操作符,用来取出操作变量的地址。
2.*解引用操作符,用来解引用指针变量找到指针指向的空间。
什么是指针变量?
第一,需要明白,指针变量也是一种变量类型,这种变量类型是与整型类型,浮点型类型并列存在的。
第二,这种变量的特点是专门用来存放地址的。

int a = 10;
//我想取出变量a的地址,那么我需要存储到指针变量当中
int * p = &a;

下面来简单解析一下如何理解**intp = &a;**这句语句。
==从右先左看,&a的意思是取出变量a在内存中的地址编号,=是把该地址编号赋给变量p,首先p与
结合,表示p需要解引用是一个指针,然后解引用指向的内存空间的变量类型为int类型,所以*p的类型为int。==

那么我想说,如果指针指向的内容是char类型呢,指针的类型写什么?char*
如果指针指向的内容是longlong类型呢?指针的类型应该写作longlong*

那我想通过地址改变变量a中的内容可以吗?可以!

int a  = 10;
int* p = &a;
*p = 20;
printf("%d\n",a);

那有同学就想要问了,为啥不直接赋值20给a啊,而是通过这种类似于绕个弯的形式进行改变a的值?这里可以简单理解为多了一种方法,并且随着后面的学习就会发现指针的真正意义。

2.2指针变量的大小

前面说过,指针也是一种变量,这种变量也需要申请空间去存放内容啊,只不过指针变量把他里面的内容视为地址而已。
这里说一个关键字sizeof,是用来计算变量大小的关键字。

int a = sizeof(char*);
int b = sizeof(int*);
int c = sizeof(long long*);
int d = sizeof(float*);

上面代码打印结果出来试一下就行了,结果发现都是一样的,4/8字节,明明指向的内容的类型有int有char还有longlong类型大小不一样,为什么指针反而大小一样呢?
其实是因为,指针存放的是地址啊,地址的编写都是依赖于x86或者x64环境的,x86环境下,有32个二进制位来编写地址,那么自然就需要32个bit来存放地址,也就是4个字节啊,x64环境下同理。

在这里插入图片描述

3.指针变量不同类型的意义

有人可能就奇怪了,既然每个指针变量类型在同一环境下大小一样,咱们只用一个指针变量不就可以了吗?干嘛用这么多乱七八糟的。
下面来简单解答一下大家的这个疑惑。

3.1不同指针类型的解引用不同

char类型的指针解引用访问一个字节,int类型的指针解引用访问4个字节。(下面是代码验证)

int a1 = 0x11223344;
int a2 = 0x11223344;
int*p1=&a1;
char*p2=&a2;
*p1=20;
*p2=20;
printf("a1=%d;a2=%d\n",a1,a2);

3.2不同指针类型的加减整数效果不同

char类型的指针加整数1就是跳过1个字节,int类型的指针+1就是跳过4个字节

int a = 20;
int*p = &a;
char ch = 'a';
char*cp = &ch;
printf("%p\n",p);
printf("%p\n",cp);
p++;
cp++;
printf("%p\n",p);
printf("%p\n",cp);

3.3void*指针

void*指针叫做空指针,写作NULL,包含在include<stdio.h>头文件中
这个指针有些特殊,该指针可以接收所有类型的地址,但不可以被解引用。
那有啥用?基本用于函数参数部分用来接收所有类型的数据传入。

4.const修饰指针

const是C语言中的一个关键字,是用来限制某个变量的。

const int a = 10;//这样在变量前面限制非指针变量之后,该变量内容不可被修改
const int* const p=&a;//在指针变量型号前面+const进行限制,该指针变量不可被解引用操作;在型号后面j+const修饰,该指针变量不可以更改内容。

5.指针运算

指针有三大运算规则

5.1指针±整数运算

该指针的运算规则是:看指针指向内容的类型大小,如果是char指针,指向的是char类型变量,+1就跳过1个char类型的大小的内存空间,int同理(如下图)
在这里插入图片描述

5.2指针-指针运算

该指针的运算规则是:两指针指向的地址相减,得到的是两个地址之间该指针类型指向的内容类型的元素个数的绝对值。
在这里插入图片描述

5.3指针的关系运算

怎么用呢?看你自己的意志哈。
简单说一下我们一般定义在函数内部(包括主函数)的变量都是存储在栈空间的,栈空间的使用是由低地址到高地址进行存储并且使用
比如我可以这样打印一个一维数组的内容(如下代码):

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

在这里插入图片描述

6.野指针

啥是野指针?说白了野指针就是指针使用不当的情况而已。
下面来简单说几个比较容易入坑的野指针情况:

6.1指针未初始化

int a = 10;
int*p;
*p=20;

6.2指针越界访问

int arr[]={1,2,3,4,5,6};
int*p=arr;
int i = 0;
for(i=0;i<6;i++)
{
printf("%d ",*p);
p++;
}
//此时p已经指向了6之后的空间
*p = 20;//指针越界访问

6.3指针指向的空间被释放

void test(void)
{
int n = 20;
return &n;
}

int main()
{
int *p = test();
*p=100;//此时p指向的空间已经被回收
return 0;
}

7.如何预防野指针?

这个野指针呢只能减少出错,不能完全避免哈,下面是一些针对减少野指针发生的建议

7.1指针初始化

7.2小心指针越界

7.3指针不再使用时,及时置为NULL

7.4避免返回局部变量的地址

8.assert断言

assert是C语言中的一个关键字,包含在<assert.h>头文件中,用来检查C语言程序中的是否按照指定条件来运行的。
在DEBUG版本下,如果assert中的条件为真,那么返回非0的数值,并且什么也不会发生;如果条件为假,那么返回0并且报错,提示报错信息。在RELEASE环境下,assert断言会被直接优化掉。
assert的开关:在其头文件之前定义**#define NDEBUG**

其实有同学会感觉这个assert跟if语句差不多啊,其实是有一些差别的,if elseif是一个逻辑程序,不会报错提示,之后还不能被优化掉,还有没开关,相比之下,assert我感觉更有利于程序员检查自己的代码。。。

指针的意义体会——传值调用和传址调用的区分

前面说了一大堆介绍指针的基本用法,但是指针啥用啊?是不是多此一举?
不是!
下面来通过一个题目来简单体会一下:写一个自定义函数,用来调换定义在main函数中两个变量的值。
然后,有些同学讲就想了,这简单:

#include <stdio.h>
void Swap1(int x, int y)
{
 int tmp = x;
 x = y;
 y = tmp;
}
int main()
{
 int a = 0;
 int b = 0;
 scanf("%d %d", &a, &b);
 printf("交换前:a=%d b=%d\n", a, b);
 Swap1(a, b);
 printf("交换后:a=%d b=%d\n", a, b);
 return 0;
}

嗯…乍一看好像没啥问题,试一下就发现不对。。。数值没有交换啊!
而是应该这样写:

void Swap2(int*px, int*py)
{
 int tmp = 0;
 tmp = *px;
 *px = *py;
 *py = tmp;
}
int main()
{
 int a = 0;
 int b = 0;
 scanf("%d %d", &a, &b);
 printf("交换前:a=%d b=%d\n", a, b);
 Swap1(&a, &b);
 printf("交换后:a=%d b=%d\n", a, b);
 return 0;
}

为啥啊?这两种写法有什么区别啊,非得用指针吗?
其实第一种写法,写的那个形参是实参的临时拷贝,说白了就是你又拷贝了一份新的变量把他俩交换了,然后又回去main函数中去看原先那份变量的值是否交换~
第二种写法,就是创建了两个指针变量去直接在自定义函数中追根溯源找到原本的那份变量进行交换。
可能同学现在比较懵哈,我画个图应该比较好理解了(里面的地址是我瞎编的哈):
在这里插入图片描述
第一种基本就是传值调用了,第二种是传址调用,现在体会到一点指针啥用处了吗?

8.2传址调用与传值调用的选择?

两种调用方式怎么选啊?
传址调⽤,可以让函数和主调函数之间建⽴真正的联系,在函数内部可以修改主调函数中的变量;所以未来函数中只是需要主调函数中的变量值来实现计算,就可以采⽤传值调⽤。如果函数内部要修改主调函数中的变量的值,就需要传址调⽤。

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

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

相关文章

Databend 的算力可扩展性

作者&#xff1a;尚卓燃&#xff08;PsiACE&#xff09; 澳门科技大学在读硕士&#xff0c;Databend 研发工程师实习生 Apache OpenDAL(Incubating) Committer PsiACE (Chojan Shang) GitHub 对于大规模分布式数据处理系统&#xff0c;为了更好应对数据、流量、和复杂性的增长…

[足式机器人]Part2 Dr. CAN学习笔记-Ch01自动控制原理

本文仅供学习使用 本文参考&#xff1a; B站&#xff1a;DR_CAN Dr. CAN学习笔记-Ch01自动控制原理 1. 开环系统与闭环系统Open/Closed Loop System1.1 EG1: 烧水与控温水壶1.2 EG2: 蓄水与最终水位1.3 闭环控制系统 2. 稳定性分析Stability2.1 序言2.2 稳定的分类2.3 稳定的对…

SwiftUI 打造一款“可收缩”的 HStack(一):“原汤化原食”

概览 拥有雄心壮志(亦或是自我感觉良好)的秃头码农们都喜欢接受编程上各种颇有难度的挑战,比如刷刷力扣(LeetCode)或 codeforces 上难题啥的。 为了满足小伙伴们的“冒险精神”(亦或是“自虐癖”),加上这篇博文我们会用连续 4 篇系列文章通过不同的方式来解决同一道与…

【IDEA——连接MySQL数据库,创建库和表】

IDEA——连接MySQL数据库&#xff0c;创建库和表 1、打开idea数据库操作页面&#xff08;两种打开方法都可以&#xff09; 2、点击加号&#xff0c;选择Driver,方便导入连接数据库的驱动jar包 然后选择MySQL进行添加驱动 3、点击上一步页面的左上方的Data Sources连接本地数据…

保姆级教程:从0到1搭建web自动化测试环境

之前都是在linux上安装&#xff0c;第一次在windows上配置环境&#xff0c;加上距离上次配置环境有点久了&#xff0c;竟也花了点时间。特此记录下保姆级教程&#xff0c;给初学者一个有效的参考&#xff01; 一. 环境搭建 工具清单 工具工具名版本Java开发工具包JDK1.8浏览…

揭秘Linux软链接:如何轻松创建、删除和修改

揭秘Linux软链接&#xff1a;如何轻松创建、删除和修改 一、简介二、创建软链接三、删除软链接四、修改软链接五、Linux软链接的高级用法六、总结 一、简介 在Linux中&#xff0c;软链接&#xff08;Symbolic Link&#xff09;是一种特殊的文件类型&#xff0c;它是一个指向另…

【DevOps-03】Build阶段-Maven安装配置

一、简要说明 下载安装JDK8下载安装Maven二、复制准备一台虚拟机 1、VM虚拟复制克隆一台机器 2、启动刚克隆的虚拟机,修改IP地址 刚刚克隆的虚拟机 ,IP地址和原虚拟的IP地址是一样的,需要修改克隆后的虚拟机IP地址,以免IP地址冲突。 # 编辑修改IP地址 $ vi /etc/sysconfig…

申请域名SSL证书并自动推送至阿里云 CDN

近期国外SSL证书厂商调整了免费证书的续签规则&#xff0c;一年期的证书全部取消&#xff0c;现在只能申请90天有效期的免费证书。普通web站点可以通过宝塔面板或部署acme.sh等证书自动管理工具来实现自动化申请和部署&#xff0c;但是阿里云之类的CDN服务就只能通过手动或Open…

Python split()方法详解:分割字符串

Python 中&#xff0c;除了可以使用一些内建函数获取字符串的相关信息外&#xff08;例如 len() 函数获取字符串长度&#xff09;&#xff0c;字符串类型本身也拥有一些方法供我们使用。 注意&#xff0c;这里所说的方法&#xff0c;指的是字符串类型 str 本身所提供的&#x…

linux性能优化

文章目录 性能优化图CPU进程和cpu原理性能指标 性能优化图 CPU 进程和cpu原理 进程与线程&#xff1a; 进程是程序的执行实例&#xff0c;有自己的地址空间和系统资源。线程是进程内的执行单元&#xff0c;共享进程的资源。在多核系统中&#xff0c;使用多线程可以更好地利用多…

【四】CocosCreator-修改引擎源码

看源码的过程中&#xff0c;少不了得修修改改源码&#xff0c;做点实验。果断去找找源码在哪里&#xff0c;然后就可以快乐动手改源码了。 CocosCreator引擎源码主要就是js和原生C两类&#xff0c;分别在引擎目录的resources/engine和resources/cocos2d-x下&#xff0c;如图&am…

金色麦芒的2023

2023年即将过去&#xff0c;回首这一年&#xff0c;我深感自己在技术和职业生涯中取得了巨大的进步。这一年里&#xff0c;我不仅在技术层面有了更深入的掌握&#xff0c;也在个人成长和职业规划上有了更明确的方向。 首先&#xff0c;在技术层面&#xff0c;我今年最大的收获是…

Innosetup 调用c# dll 和 c# dll的函数导出

目标需求&#xff0c;基于现在安装包脚本。需要在用户安装和卸载成功时。进行数据记录,所以需要调用c#dll 主要涉及到的知识点 需要理解脚本的文件使用机制脚本的文件dll加载&#xff0c;和dll的调用c# dll的制作&#xff0c;和工具的使用 下面具体介绍 脚本的文件dll加载&…

【程序】USART串口通信接收数据(标准库带printf)

&#x1f31f;博主领域&#xff1a;嵌入式领域&人工智能&软件开发 前言&#xff1a;本程序使用stm32f429作为主控&#xff0c;使用串口1&#xff0c;使用的是标准库程序版本。&#xff08;其它主控/串口x&#xff0c;实现过程类似&#xff09;。本程序亲测无误。 目录…

如何制作可预约的上门维修服务小程序?

上门维修服务已经成为人们日常生活中不可或缺的一部分。为了满足这一需求&#xff0c;我们学习如何无经验自己制作上门维修服务小程序。 首先&#xff0c;打开乔拓云-门店系统的后台&#xff0c;可以看到有很多各行各业的模版。这些模版涵盖了各种行业&#xff0c;包括家电维修…

9.java——(杂例)组合,代理,向上转型static,fianl,关键字(有道云笔记复制粘贴,大家整体性的把握)

组合——内部有类&#xff08;心中有对象&#xff01;&#xff01;&#xff01;&#xff09;&#xff08;足球 和足球运动员梅西和脚下的足球一样&#xff09; has和is的区别&#xff0c;has是组合&#xff0c;是有&#xff0c;持有的意思&#xff1b;is是继承&#xff0c;是…

MT9201 1.2MHz,3V~24V输入高效增压白色LED驱动器 丝印B9HB

描述 MT9201是一个升压转换器&#xff0c;设计用于从单电池锂离子电池驱动多达7系列白色led。MT9201使用电流模式&#xff0c;固定频率结构来调节LED电流&#xff0c;它通过外部电流感测电阻器来测量。其低200mV反馈电压降低了功率损耗&#xff0c;提高了效率。MT9201包括欠电压…

FlinkSQL中【FULL OUTER JOIN】使用实例分析(坑)

Flink版本&#xff1a;flink1.14 最近有【FULL OUTER JOIN】场景的实时数据开发需求&#xff0c;想要的结果是&#xff0c;左右表来了数据都下发数据&#xff1b;左表存在的数据&#xff0c;右表进来可以关联下发&#xff08;同样&#xff0c;右表存在的数据&#xff0c;左表进…

spring见解2基于注解的IOC配置

3.基于注解的IOC配置 学习基于注解的IOC配置&#xff0c;大家脑海里首先得有一个认知&#xff0c;即注解配置和xml配置要实现的功能都是一样的&#xff0c;都是要降低程序间的耦合。只是配置的形式不一样。 3.1.创建工程 3.1.1.pom.xml <?xml version"1.0" en…

C++八股学习心得.4

1.C 类 & 对象 C 在 C 语言的基础上增加了面向对象编程&#xff0c;C 支持面向对象程序设计。类是 C 的核心特性&#xff0c;通常被称为用户定义的类型。 类用于指定对象的形式&#xff0c;它包含了数据表示法和用于处理数据的方法。类中的数据和方法称为类的成员。函数在…