C语言指针(1)
目录
一、内存和地址
1、生活中的例子
2、内存的关系
二、指针变量和地址
1、&符号,%p占位符
2、一个简单的指针代码。
3、理解指针
4、解引用操作符
5、指针变量的大小。
三、指针变量类型的意义
1、指针解引用的作用
2、指针+指针
3、指针-指针
4、void*指针
四、const修饰指针
1、const修饰变量
2、const修饰指针变量
1、没有const
2、const在*左边
3、const在*右边
4、const在*两边
五、指针运算
1、指针+-整数
2、指针-指针
3、指针的关系运算
六、野指针
1、野指针的成因
1、未初始化
2、越界访问
3、指针指向空间的释放
2、如何避免野指针
七、assert断⾔
八、指针的使⽤和传址调⽤
1、strlen的模拟实现
2、传值调用传值调用
1、传值调用
2、传址调用
hello大家好,今天我们来讲一下C语言中指针的知识点,指针这部分内容很多,容易记混而且有时候写代码也想不到,这就需要有一个良好的指针基础,我尽全力把指针用我们已经学习过的知识来讲述,让学习者都可以很快的理解,并且可以使用我所讲指针给出的例子来练习。
那么我们就开始学习吧!!!
一、内存和地址
1、生活中的例子
学习指针我们就要知道内存和地址的关系。
下面图中,我们可以把这一栋楼看做内存,每个房间可以看做地址。
如果我们想去到楼里的特定房间,我们就要挨个房间寻找我们想要去的特定房间里面,然后我们再去到另外一个特定的房间里面,我们还需要挨个寻找,这样效率会非常慢。
聪明的同学已经开始想到了,我们可以把每个房间都设置门牌号,这样我们就可以快速去到我们想去的房间了。
结论:门牌号=地址=指针
2、内存的关系
我们可以把每把内存给划分为内存单元,一个内存单元大小等于一个字节,所以每个房间就可以看着是字节。
常见的存储单位:bit(比特)-> Byte(字节) -> KB -> MB -> GB -> TB -> PB
1字节 = 8bit位
二、指针变量和地址
1、&符号,%p占位符
取地址符号(&):可以让我们获取地址。
%p:想打印出地址就需要使用%p这个占位符。
int main( )
{int a = 10;printf("%p\n", &a);return 0;
}
输出:
2、一个简单的指针代码。
int main()
{int a = 10;int* p = &a;return 0;
}
通过调试vs我们看见p=&a。
在这段代码中,我们设置了a=10,我们通过*p指向了a的地址,此时p就等于a的地址。我们就可以通过*p来修改a中的地址。
3、理解指针
int a = 10;
int * pa = &a;
pa左边写的是int*,*说明是pa的指针变量,而int在说明pa指向的是一个整型类型的对象。
4、解引用操作符
解引用操作符就是“ * ”。
int main()
{int a = 10;int* p = &a;*p = 20;printf("%d ", *p); //输出20return 0;
}
这段代码中,我们通过*p=20;修改a的数值。尽管int* p = &a;这一行代码我们已经获取了a的地址,但是我们还是需要通过操作符“ * ”来访问或修改这样地址指向的值。p中存放的内容是a的地址,如果我们直接写p=20,程序修会报错,因为p是一个指针,不能直接存放一个整数。只有我们使用*p,我们才能直接访问p指向的内存空间,即a的内存空间,从而修改变量中的a,此时a的数值也会跟着改变。
5、指针变量的大小。
指针变量的大小是取决于我们使用的是32位平台还是64位平台。
int main()
{printf("%d ", sizeof(char*));printf("%d ", sizeof(int*));printf("%d ", sizeof(double*));printf("%d ", sizeof(float*));return 0;
}
32位平台输出:
64位平台输出:
在32位下,指针的大小位4个字节。
在64位下,指针的大小位8个字节。
三、指针变量类型的意义
1、指针解引用的作用
int main()
{int a = 0x11223344;char* p = (char*)&a;*p = 0;return 0;
}
*p运行前&a地址:
*p运行后&a地址:
为什么只有一个字节等于0,因为*p是一个char类型,char类型只占一个字节。
只有*p跟a是一个类型的时候,才可以把a=10。
2、指针+指针
int main()
{int a = 10;char* p1 = (char*)&a;int* p2 = &a;printf("%p\n", &a);printf("%p\n", p1);printf("%p\n", p1+1);printf("%p\n", p2);printf("%p\n", p2+1);return 0;
}
输出:
我们可以看到,p1指针是char类型,p2是int类型,当把指针+1的时候,p1和p2所得到的地址是不一样的,p1加1字节,p2加4字节,这是因为指针的类型是不一样的。
结论:指针的类型决定的指针向前的步子是多大。
3、指针-指针
int main()
{int a = 10;char* p1 = (char*)&a;int* p2 = &a;printf("%p\n", &a);printf("%p\n", p1);printf("%p\n", p1 - 1);printf("%p\n", p2);printf("%p\n", p2 - 1);return 0;
}
输出:
4、void*指针
int main()
{int a = 10;char* p = &a;*p = 20;return 0;
}
char* p = &a;由于我们使用char类型接收int类型,导致编译器提示错误。
void*可以接收任意指针,char,int,long,double等等
int main()
{int a = 10;void* p = &a;return 0;
}
但是void*是无法修改指针变量的
int main()
{int a = 10;int b = 20;void* p1 = &a;void* p2 = &b;*p1 = 30;*p2 = 40;return 0;
}
那么void*类型的指针有什么用呢?
void*是使用函数参数部分,用来接收任意不同类型的数据,可以实现泛编程效果,使得一个函数可以出来多个数据类型。
四、const修饰指针
1、const修饰变量
const这个函数可以让变量中避免被修改。
int main()
{const int a = 10;a = 20;return 0;
}
但是我们仍然可以使用指针来修改a变量
int main()
{const int a = 10;int* p = &a;*p = 20;printf("%d", *p);return 0;
}
输出:
那么就有人问了,这有什么意思,这不就是防君子不防小人吗,那么我们如果才可以防止a被修改呢?
2、const修饰指针变量
这些有什么区别呢?
int* p; //没有const
const int* p; //const在*左边
int const* p; //const在*左边
int* const p; //const在*右边
int const * const p; //const在*两边
1、没有const
void test1()
{int n = 10;int m = 20;int* p = &n;*p = 20;p = &m;
}
在没有const限制的条件下我们可以修改指针*p的变量,p也可以获得m的地址
2、const在*左边
const在*左边有两种写法,这两种写法都是正确的:
const int* p =&a;
int const* p =&a;
void test1()
{int n = 10;int m = 20;const int* p = &n;*p = 20;p = &m;
}
在const限制的条件下我们不可以修改指针*p的变量,但是p可以获得m的地址.
3、const在*右边
void test1()
{int n = 10;int m = 20;int* const p = &n;*p = 20;p = &m;
}
const限制的条件下我们可以修改指针p的数值,但是p不可以获得m的地址。
4、const在*两边
void test1()
{int n = 10;int m = 20;int const* const p = &n;*p = 20;p = &m;
}
const限制的条件下我们不可以修改指针p的数值,p也不可以获得m的地址。
总结:
- const在左边的时候,修饰的是指针指向的内容,保证指向内容不能被指针修改,但是指针变量本身的内容是可变的。
- const在右边的时候,修改是是指针变量本身,保证指针指向的内容是可以被修改的,但是指针变量本身是无法被修改的
五、指针运算
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
下图是数组中每一个元素的下标
1、指针+-整数
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));//指针+整数}return 0;
}
指针*p的类型是int,地址是&arr[0],下标是0,通过p+i,我们就可以打印出指针指向每一个元素下标地址,通过解引用我们从而获得打印数组。
我们之前学习的是printf("%d ", arr[i]);这样打印出数组,但是在编译器底层是通过指针来打印的,*(p + i)= arr[i]。
2、指针-指针
#include <stdio.h>
int my_strlen(char* s)
{char* p = s;int count = 0;while (*p != '\0'){count++;p++;}return count;
}int main()
{printf("%d\n", my_strlen("abcdef"));return 0;
}
输出字符串长度:
---------------------------------------------------------------------------------------------------------------------------------
int my_strlen(char* s)
{char* p = s;while (*p != '\0')p++;return p - s; //当p结束是指向字符串的末尾,所以我们用末尾-初始值就是字符串长度
}int main()
{printf("%d\n", my_strlen("abc"));return 0;
}
3、指针的关系运算
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;
}
while (p < &arr[sz])这一行代码,我们是利用地址来进行比较的,当p这个地址小于&arr[sz]地址的时候,那么就不允许。
六、野指针
指针指向实际方向的内容是否有效,超出指针指向变量部分的内容,或者是栈帧销毁的内容会成为野指针,野指针的数值是一个随机值。
1、野指针的成因
1、未初始化
int main()
{int* p1;*p = 10;return 0;
}
2、越界访问
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };int* p = &arr[0];for (int i = 0; i < 11; i++){printf("%d ", *(p+i));}return 0;
}
输出:
在这个一维数组打印中,指针超出了指向数组的值,造成了随机值,这就是野指针。
3、指针指向空间的释放
int test()
{int n = 100;return &n;
}int main()
{int* p = test();printf("%d", *p);return 0;
}
在这一段代码中*p也是一个野指针,这是为什么,因为我们创建的test函数在运行完以后就会销毁,所以*p指向的是&n,test函数已经被销毁了,*p就没有地址了,导致*p变成了野指针。
2、如何避免野指针
int main()
{int a = 10;int* p1 = &a;int* p2 = NULL;return 0;
}
我们可以在创建的int* p里放入NULL,就可以避免野指针出现。
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };int* p = &arr[0];for (int i = 0; i < 11; i++){*(p++) = i;}
//p已经越界,把NULL赋值给pp = NULL; //把野指针的数值等于NULLp = &arr[0]; //重新赋值pif (p!=NULL){printf("haha\n");}return 0;
}
七、assert断⾔
assert头文件:
assert (p!=NULL);
assert是一个对程序员很友好的功能,如果assert这个程序符合调价,那么就继续运行,如果不符合那就会提示错误在哪些地方。
如果代码没有任何问题以后,可以在头文件上面写:
#define NDEBUG#include <assert.h>
int main()
{int age = 11;// 使用assert检查年龄是否大于18岁assert(age >= 18);printf("年龄大于18岁。\n");return 0;
}
八、指针的使⽤和传址调⽤
1、strlen的模拟实现
方法一:
int my_strlen(const char* str)
{int count = 0;assert(str);while (*str){count++;str++;}return count;
}int main()
{int len= my_strlen("abcdef");printf("%d\n", len);return 0;
}
方法二:
int my_strlen(const char* str)
{char* p = str;assert(str);while (*p){p++;}return p-str;
}int main()
{int len= my_strlen("abcdef");printf("%d\n", len);return 0;
}
输出:
2、传值调用传值调用
1、传值调用
void Swap1(int x, int y)
{int tmp = x;x = y;y = tmp;
}int main()
{int a = 10;int b = 20;printf("交换前:a=%d b=%d\n", a, b);Swap1(a, b);printf("交换后:a=%d b=%d\n", a, b);return 0;
}
为什么明明在函数中交换了a和b的数值,为什么没有交换成功?
2、传址调用
void Swap2(int* px, int* py)
{int tmp = 0;tmp = *px;*px = *py;*py = tmp;
}
int main()
{int a = 10;int b = 20;printf("交换前:a=%d b=%d\n", a, b);Swap2(&a, &b);printf("交换后:a=%d b=%d\n", a, b);return 0;
}
输出:
指针并不是跟形参一样是拷贝,指针指向的数据就是a,b这两个地址,当函数Swap2中px,py中数据进行交换的时候,交换的就是main函数中a,b中真实的数据。
相关文章:
C语言指针(1)
目录 一、内存和地址 1、生活中的例子 2、内存的关系 二、指针变量和地址 1、&符号,%p占位符 2、一个简单的指针代码。 3、理解指针 4、解引用操作符 5、指针变量的大小。 三、指针变量类型的意义 1、指针解引用的作用 2、指针指针 3、指针-指针 4…...
C语言中的指针与数组
C语言中的指针与数组是编程中非常基础且强大的概念,它们之间有着紧密的联系和相互转换的可能性。深入理解这两个概念对于编写高效、可维护的C程序至关重要。以下将详细探讨C语言中的指针与数组,包括它们的基本概念、关系、应用以及一些高级话题。 一、指…...
CentOS7.9升级OpenSSL1.1.1w
下载 https://www.openssl.org/source/old/1.1.1/index.html 安装依赖 yum install gcc libffi-devel zlib* openssl-devel libffi-devel zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gcc perl make 解压 tar -zxvf openss…...
环境搭建:如何安装和使用 MySQL Connector/J——与 MySQL Community Server 的关系
环境搭建:如何安装和使用 MySQL Connector/J—— MySQL Community Server 的关系 在 Java 项目中,与 MySQL 数据库的交互需要使用 MySQL Connector/J 驱动。本文将介绍 MySQL Connector/J 的作用、安装方法以及与 MySQL Community Server 的关系…...
SAP 财务管理系统 —— 企业财务智能化的领航者
在当今数字化时代,企业财务管理的智能化已成为推动企业持续增长的关键因素。SAP 财务管理系统通过智能化技术,帮助财务部门提高收入、控制成本并降低财务风险,释放财务数字化转型的价值。财务 ERP 作为 SAP 的核心组成部分,将帮助…...
python通过pyautogui自动给微信聊天窗口发消息
使用py脚本自动给聊天窗口发消息 1.突然的自我2.编写脚本玩一把i.先获取窗口位置ii.模拟聊天iii.疗效不错呢 1.突然的自我 突然想到pyautogui可以做那么事情, 那么是不是可以模拟聊天呢,如果结合现在的大模型chatGPT一边问然后得到结果一边自动和别人聊…...
QML中的Date将时间戳和指定格式时间互转
在QML中,可以通过使用JavaScript来处理日期和时间的转换,其中包括将时间戳转换为指定格式的时间字符串,以及将时间字符串解析为时间戳的操作。 将时间戳转换为指定格式的时间字符串 在QML中,可以通过JavaScript的Date对象来处理…...
C++ new/delete 重载
operator new/delete 重载 语法格式 void *operator new(size_t); void operator delete(void *); void *operator new[](size_t); void operator delete[](void *);#include <iostream> using namespace std;class A { public:// 构造函数A(){// _x1;// _y2;// 在n…...
读取连接中文件流和页面展示base64编码的文件
读取连接中文件流和页面展示base64编码的文件 背景需求从接口处获取base64编码的字节流依赖java 代码 前端展示pdf图片 背景需求 我需要展示一个pdf 文件在页面上,但是我一直没办法将 pdf的下载链接用预览方式展示出来,于是打算讨个巧,直接给…...
【大模型从入门到精通4】openAI API 分类
这里写目录标题 分类理解 SYSTEM 和 USER 在 AI 对话中的角色System MessageUser Message工作原理示例分类示例更多分类示例理论问题理论 分类 理解 SYSTEM 和 USER 在 AI 对话中的角色 在分类任务中,通常需要向模型提供一个需要将其分类到预定义类别中的文本场景…...
仓颉 -- 标识符 , 变量以及数据类型详解
仓颉 – 标识符 , 变量以及数据类型 一. 标识符 1. 普通标识符 由数字 , 字母 , 下划线构成 – cangjie , cangjie_2024由英文字母开头,后接零至多个英文字母、数字或下划线。由一至多个下划线开头,后接一个英文字母,最后可接零至多个英文…...
CC++:贪吃蛇小游戏教程
❀创作不易,关注作者不迷路❀😀😀 目录 😀贪吃蛇简介 😃贪吃蛇的实现 🐍生成地图 🐍生成蛇模块 ❀定义蛇的结构体 ❀初始化蛇的相关信息 ❀初始化食物的相关信息 🐍光标定位和…...
C#中投影运算的深入解析与实例应用
文章目录 1、投影运算的基本语法2、投影运算的高级用法3、投影运算在向量空间中的运用4、投影运算在数据库和XML中的实际应用5、投影运算能用于哪些实际场景?6、结论 在C#编程中,投影运算是一种常用的数据操作技术,它可以将一个数据集合转换成…...
HTML+CSS練習---空隙產生記錄
1.第一層和第二層之間的間隙:以為導航欄超過高度朝下擠下來了 2.第2層兩個div中的空隙 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Document</title><style>font-face {f…...
【leetcode】相同的树、另一棵树的子树、翻转二叉树(利用深度优先遍历)
Hi~!这里是奋斗的明志,很荣幸您能阅读我的文章,诚请评论指点,欢迎欢迎 ~~ 🌱🌱个人主页:奋斗的明志 🌱🌱所属专栏:数据结构、LeetCode专栏 📚本系…...
Linux系统窗口水印难点分析
给应用程序加水印是保护数据的一种方式,window上可以通过给进程通过注入的方法给进程的窗口创建一个同大小的副窗口,在副窗口上绘制水印内容,同时设置副窗口透明同时透传事件,这样就可以达到在源窗口上显示水印的效果且不影响程序…...
LabVIEW与CANopen实现自动化生产线的设备控制与数据采集
在某工厂的自动化生产线上,多个设备通过CANopen网络进行通信和控制。这些设备包括传感器、执行器和PLC,它们共同负责监测和控制生产过程中的关键参数,如温度、压力、速度等。为了实现对整个生产线的集中监控和管理,工厂决定使用La…...
吃惊!这个Windows双系统方法逆天了|UEFI篇
前言 最近小白在折腾别的系统教程,偶然间发现居然有一个很nice的Windows双系统教程。于是于是,果断尝试了一下,发现真的很可行! 这个双系统的办法并不需要使用到WinPE系统,因此并不需要使用到U盘,只需要在…...
【C语言基础】C语言试题复习
1. 执行下面的程序段后,k 的值是_______。 int k1,n325; do { k*n%10;n/10;}while(n); 解析: 给定 n 325 和初始 k 1,代码中的循环将会进行如下操作: 第一次循环:n % 10 得到 5,因此 k * 5,即 k 1 * 5 …...
一拖三无线充底座-带给你极致的便利生活
随着科技的不断进步,无线充电技术已经逐渐渗透到我们日常生活的方方面面,一拖三无线充底座作为其中的佼佼者,以其高效、便捷的特点受到广大用户的青睐。本文将从电磁感应原理、多线圈设计、频率匹配、电能传输、功率分配以及充电管理六个方面…...
探索 Electron:打造深度书籍挖掘机的搜索体验
Electron是一个开源的桌面应用程序开发框架,它允许开发者使用Web技术(如 HTML、CSS 和 JavaScript)构建跨平台的桌面应用程序,它的出现极大地简化了桌面应用程序的开发流程,让更多的开发者能够利用已有的 Web 开发技能…...
tomato靶场
扫描网址端口 访问一下8888 我们用kali扫描一下目录 访问这个目录 产看iofo.php源码,发现里面有文件包含漏洞 访问/etc/passwd/发现确实有文件包含漏洞 远程连接2211端口 利用报错,向日志文件注入木马,利用文件包含漏洞访问日志文件 http:/…...
【Vue】computed计算对象不生效问题?
问题描述 最近使用vuex来管理全局状态,遇到了computed计算state中数据却不生效的问题。 原因分析: 先看vue官网示例: computed接收的是一个getter函数,但是这个getter函数是懒加载并且有缓存的,当计算属性最终计算…...
算法小白的进阶之路(力扣9~12)
💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 非常期待和您一起在这个小…...
DOCKER容器中安装JDK1. 8 详细步骤
1.通过查找JDK8的远程镜像 docker search jdk 2.选择一个远程镜像下载到本地仓库 #拉取镜像 docker pull kdvolder/jdk8#查看镜像 docker images 可以看到REPOSITORY列下面出现了kdvolder/jdk8 3.在docker容器中运行jdk8的镜像 docker run -di --namejdk1.8 kdvolder/jdk…...
计算机毕业设计Python+Tensorflow股票推荐系统 股票预测系统 股票可视化 股票数据分析 量化交易系统 股票爬虫 股票K线图 大数据毕业设计 AI
1、用pycharm打开项目,一定要打开包含manage.py文件所在文件夹 2、配置解释器:建议使用Anaconda(Python 3.8(base)),低于3.8版本的,页面会不兼容 3、安装依赖库:打开pycharm的终端,输入: pip in…...
深度学习常见的卷积和注意力机制文章集锦(持续更新)
卷积 友好链接1 卷积原理:几种常用的卷积(标准卷积、深度卷积、组卷积、扩展卷积、反卷积) 友好链接2 一文看尽深度学习中的20种卷积(附源码整理和论文解读) 友好链接3 深度学习中组卷积(Group convolution)、深度卷积…...
如何在立创EDA的PCB电路板导入logo图案
1、首先制作好logo图案,一般为公司logo图标,如下图 2、打开立创EDA的PCB文件,如下图 3、将PCB的图层切换到丝印层: 4、然后选择EDA菜单栏的放置---图片: 5、进入后点击选择图片,将logo图片导入,…...
springboot集成canal
目录 一、打开mysql的binlog1.1 打开 MySQL 配置文件 my.cnf(通常位于 /etc/mysql/my.cnf 或 /etc/my.cnf)并添加或修改以下设置:1.2 重启mysql服务1.3 验证是否生效 二、 部署canal 服务端(docker)2.1 下载启动脚本(可…...
leetcode数论(2447. 最大公因数等于 K 的子数组数目)
前言 经过前期的数据结构和算法学习,开始以OD机考题作为练习题,继续加强下熟练程度。 描述 给你一个整数数组 nums 和一个整数 k ,请你统计并返回 nums 的子数组中元素的最大公因数等于 k 的子数组数目。 子数组 是数组中一个连续的非空序列…...
网站登录页面盗号怎么做/互联网搜索引擎有哪些
由于在ADO.NET 1.1中的固定的事务处理方式,让我直到今天才了解到ADO.NET 2.0对事务新的理解与重新编写的事务机制,不但包含了数据库的事务支持,而且还被应用于其它如:文件IO处理、消息机制等可以应用事务的代码块内。 新的事务处理架构不再与数据库绑在一起&…...
南京专业网站制作/网站查询平台
描述 给出 2 * n 1个数字,除其中一个数字之外其他每个数字均出现两次,找到这个数字。 说明 n≤100 样例 - 样例 1:输入:[1,1,2,2,3,4,4] 输出:3 解释:仅3出现一次- 样例 2:输入:[0,0,1] 输出…...
惠州附近公司做网站建设多少钱/广东seo推广哪里好
一、概念 定义: 图由点集合和边集合组成,记做G(V,E),其中点集合不能为空且应该有穷,边集合可以为空。 ①有向图:边有方向,用<Vi,Vj>表示 ②无向图:边没有方向,用(Vi,Vj)表示…...
秦皇岛网站建设服务/游戏代理
为什么80%的码农都做不了架构师?>>> 背景 构造1条记录,插入到数据库;当数据库已经存在同一主键的数据,则执行update操作,如果不存在,则执行insert操作。 用update、和insert语句可以满足要求&a…...
网站建设 阿里云/优化营商环境个人心得
1. 定义两个类:// 基类publicclassBaseBusiness { } // 子类 publicclassSampleBusiness : BaseBusiness { }2. 动态创建类的实例//程序集名称stringassemblyName "abc.Business"; //类名称stringclassName "SampleBusiness"; …...
网站制作的设计思路/活动策划方案
近年来,随着我国高速公路建设迅猛发展,高速公路的规模越来越庞大,收费站的机房数量和机房设备也越来越多。为了确保高速公路的正常运营,收费站的机房都会设有能提供不间断、稳定电源供应的UPS电源。但由于缺乏对UPS电源的有效管理…...