0%

网络编程(1)

一. 网络基础知识

1. 网络分层

  1. 四层
    1. 应用层
      1. ping
      2. telent
      3. OSPF
      4. DNS
    2. 传输层
      1. TCP
      2. UDP
    3. 网络层
      1. ICMP
      2. IP
    4. 数据链路层
      1. ARP
      2. Data Link
      3. RARP
  2. 七层
    1. 应用层
    2. 表示层
    3. 会话层
    4. 传输层
    5. 网络层
    6. 数据链路层
    7. 物理层

      2. IP地址

  3. ipv4
    1. 点分十进制,每段一字节
    2. 四字节
  4. ipv6
    1. 冒号分隔16进制,每段二字节
    2. 十六字节

      3. 端口

      16位整数(0-65535)

      4. 字节序

  5. 大端(网络字节序)
    1. 高位地址存低位数据
  6. 小端(主机字节序)
    1. 高位地址存高位数据

      4.1 转序函数

      1. 整数转序
      1
      2
      3
      4
      5
      #include <arpa/inet.h>
      htons() //16位主机到网络(序)转换
      htonl() //32位主机到网络(序)转换
      ntohs() //16位网络到主机(序)转换
      ntohl() //32位网络到主机(序)转换
      2. IP地址转序
      1
      2
      3
      4
      inet_pton(地址类型,地址字符串,用于输出的内存指针) \\IP地址转网络序
      inet_ntop(地址类型,网络序字符串,用于输出的字符串指针,字符串指针指向内存的大小) \\网络序转IP地址,调用成功返回字符串指针,失败返回null
      inet_addr(字符串) \\IP转网络序(仅ipv4)
      inet_ntoa(结构体) \\网络序转ip(仅ipv4)
  • 地址类型:AF_INET(ipv4),AF_INET6(ipv6)

Git简明教程

image.png

1. 什么是git

Git是目前世界上最先进的分布式版本控制系统,可以有效、高速的处理从很小到非常大的项目版本管理。分布式相比于集中式的最大区别在于开发者可以提交到本地,每个开发者通过克隆(git clone),在本地机器上拷贝一个完整的Git仓库。

概括:git是一个项目版本管理工具,用来合作开发和做版本迭代以及必要的时候进行方便的版本回滚。

阅读全文 »

更改开机引导LOGO(无需下载BIOS文件)

作者:BILIBILI:万能的小奇喵

1. 前期准备

  • 原理:
    从电脑的 FLASH ROM 中提取 bios 后进行修改再刷写回 FLASH ROM

    警告:BIOS操作有一定风险,请谨慎决定

  • 需要准备的工具

工具 作用
FPTW64 用于从 FLASH 提取 BIOS 文件,以及向 FLASH 写入修改后的 BIOS 文件
Change Logo 用于替换 BIOS 里的开机引导 LOGO

2. 提取BIOS

  1. (可选)新建一个空的工作文件夹【本文中为D盘根目录下的 BIOSWORKSPACE文件夹】
  2. (可选)在BIOSWORKSPACE 中新建一个文件夹用于存放 FPTW工具【本文中为FPTW64】
  3. 将 FPTW64 解压到FPTW64
  4. 按下Win+x,选择 Windows终端(管理员)【如果你的系统是 Windows10 则选择 Windows PowerShell(管理员)】
  5. 使用cd指令切换到 FPTW 目录
    1
    cd D:\BIOSWORKSPACE\FPTW64
  6. 使用 FPTW64 提取 BIOS 并保存为文件【本文中为BIOS.bin】
    1
    .\fptw64.exe -d ..\BIOS.bin -bios

3. 替换图片

  1. 点击Load Image
  2. 文件类型选择为All Files (*.*)
  3. 选择上一步骤保存的BIOS.bin
  4. 正常情况下 Available Images 栏应当会识别出 BIOS 文件中的启动 Logo 图片

    警告:如果此步骤失败请勿进行下一步骤!!!!否则一旦刷炸 BIOS 就只能网购编程器和夹子线拆修了

  5. (可选)点击 Save Logo将BIOS内的原 LOGO 备份,以备将来哪天想换回去。
  6. 点击Browse选择一张jpg或bmp格式的图片,大小在 800x600 以内的 jpg 格式图片
  7. 点击Replace Logo
  8. 点击Save Image As,保存修改后的文件到工作目录【本文中保存为 BIOSRL】

4. 刷入修改后的BIOS

  1. 按下Win+x,选择 Windows终端(管理员)【如果你的系统是 Windows10 则选择 Windows PowerShell(管理员)】
  2. 使用cd指令切换到 FPTW 目录
    1
    cd D:\BIOSWORKSPACE\FPTW64
  3. 使用 FPTW64 刷入修改后的BIOS【本文中为BIOSRL.bin】
    1
    .\fptw64.exe -f ..\BIOSRL.bin -bios

5.重启

重启查看替换后的显示效果,重复以上步骤调整至满意

可能是最专业的面向萌新机械键盘选购指导

对于对外设缺乏了解的初入者而言,想要从琳琅满目的各种键盘中选购出适合自己需求的一款似乎是一件十分复杂的事。

本篇文章致力于以较为“专业”的角度为初入者对比剖析,让初入者能够初步了解一些基础并简单的“按需定制”出最适合自己的第一板键盘。

阅读全文 »

PC下安卓逆向拆包编译打包与重新签名

1.安装jdk开发环境并设置环境变量

  1. ORCLE官网下载JDK开发环境orcle官网.png
  2. 运行安装(基本一直下一步,没啥好说的,记住自己安装目录选在哪了就行)
  3. 配置环境变量搜索高级系统设置系统属性.pngpath.png添加变量.png

然后找到你的JDK安装目录下的bin文件夹

目录.png

最后一路确定回来。

2.apk拆包打包工具——APLTOOL

  1. APLTOOL官网往下翻下载最新版APLTOOLapktool官网.png
  2. 找一个你喜欢的地方新建一个文件夹作为安卓拆包打包的地方,然后把下载的.jar文件放进去(可以把名字改短点以防打指令时候不好打)
  3. 把你要拆包的.apk或者要打包成apk的文件夹也放进来
  4. 按住shift+右键,选择在此处打开powershell(按住shift再右键才有)shiftlright.png
  5. 执行拆包/打包命令
1
2
java -jar jarfilename.jar d filename.apk//拆包
java -jar jarfilename.jar b flodername//打包

注意!打包前最好删除 META-INF 文件夹!这里面是旧的安装包签名信息

3.使用dex2jar将dex文件转换为jar格式

在将apk逆向拆包后,apktool会将里面的dex文件逆向为可编辑的smail文件,但是smail的语法比较奇怪,并不是很适合大多数人的阅读习惯,因此我们可以

首先把apk文件拷贝一份解压缩,获取里面的dex文件,然后输入

1
d2j-dex2jar.bat filename.dex

将dex文件转换为jar,再经过逆向,便可通过阅读java源代码的方式来寻找需要逆向后修改的内容,找到后到smail文件内修改对应内容即可。

4.使用JD-GUI或CFR将jar文件逆向为java源码

JD-GUI和CFR都是知名的java逆向工具,通过使用JD-GUI和CFR我们可以将已经编译为字节码或jar包的java文件逆向回java源码,JD-GUI是一个图形界面的工具,而CFR则是命令行工具。

JD-GUI的使用比较简单,只要在终端内输入

1
java -jar jarfilename.jar 

即可打开一个带图形界面的软件,在里面选中逆向出的jar打开即可查看逆向出的文件

CFR没有图形界面,但实际使用中往往比JD-GUI更稳定,一些JD-GUI无法正确逆向的文件能够在CFR下逆向成功

1
java -jar jarfilename.jar inputjarfilename.jar  --outputdir /savepath

之后可以通过其它ide或文本编辑器查看逆向出的文件

5.生成我们自己的.keystore文件

平常我们用的一些重新签名工具里面大都是别人做好的签名文件,这里我们可以生成我们自己独有的签名文件。

  • 在powershell输入
1
keytool -genkey -alias 别名 -keyalg RSA -validity 有效天数 -keystore filename.keystore
参数名 含义
-genkey 产生证书文件
-alias 产生别名
-keystore 密钥库名称
-keyalg 密钥的算法
-validity 证书有效天数

然后按提示分别输入
姓名、组织单位、、城市、省份、国家代码
Y确认正确
然后设置一个密码(输入的时候不会有反应但是实际上输进去了)
回车后会再让你确认一次密码

之后你就得到了自定义的.keystore密匙文件

6.为打包好的.apk签名

在powershell输入指令

1
jarsigner -verbose -keystore filename.keystore -signedjar outputfile.apk inputfile.apk alise
参数名 含义
-verbose 输出签名过程的详细信息
alias 密匙证书文件的别名
-keystore 密匙证书文件位置
-signedjar 指定输入输出文件名
inputfile.apk 未签名文件
outputfile.apk 签名后文件

如此我们便成功的完成了对apk文件的签名

如何快速而优雅的解决问题(提问的智慧缩减版)

注:本文大部分内容引用自《提问的智慧》,结尾附原文档链接

不要随便提问!!

不要随便提问!!

不要随便提问!!

在各种解决基本技术问题的方式里,在qq群或者私聊里提问,是你所能操作的方式里最慢,效率最低,成本最高,而且你能学到东西最少的解决方式!!!

请不要过高的估算自己的问题难度,截止2019年,这个世界上有38亿人在互联网上活跃,通常情况下,你们所遇到的问题中的绝大部分都已经有无数的前车之鉴将解决方案糊在了网上!打开浏览器,然后打开谷歌或者百度或者搜狗或者雅虎或者360搜索或者任何其它的搜索引擎(你们有多么多的选择啊),然后搜索你所遇到的问题,(请务必精准简洁的概括你的问题,如果你搜索“救命啊,我的电脑坏了,有人能帮我吗?xxxx坏了!”纯属自己作死,请不要说自己是计算机协会的人,谢谢。),通常情况下使用搜索引擎是大部分问题最快捷的解决方式。

而且通常情况下,软件手册,技术文档,论坛文章作者会更加深切透彻的描述一个问题产生的原理,从而使你学到更多的东西,好比装GCC编译环境,如果你搜索 “GCC 编译环境配置 ”,你会得到很多篇教程并在自己动手的过程中学会GCC环境的配置,但如果你问我“喵,我不会配置GCC环境”,我会从你手里接过电脑然后顺利的配置好环境再递给你。

下次你还是不会装GCC。

在你准备要通过电子邮件、新闻群组或者聊天室提出技术问题前,请你务必先做到以下事情:

  1. 尝试在你准备论坛的文章中搜索答案。
  2. 尝试上网搜索以找到答案。
  3. 尝试阅读手册以找到答案。
  4. 尝试阅读常见问题文件(FAQ)以找到答案。
  5. 尝试自己检查或试验以找到答案。
  6. 向你身边的同学老师打听以找到答案。
  7. 如果你是程序开发者,请尝试阅读源代码以找到答案。

草率的发问只能得到草率的回答,或者根本得不到任何答案。如果你什么都不做就去技术圈提问的话有很大的概率会收到以下两种答复

  1. RTFW
    • 有一个古老而神圣的传统:如果你收到RTFM (Read The Fucking Manual)的回应,回答者认为你应该去读他妈的手册。当然,基本上他是对的,你应该去读一读。
  2. STFW
    • RTFM 有一个年轻的亲戚。如果你收到STFW(Search The Fucking Web)的回应,回答者认为你应该到他妈的网上搜索。那人多半也是对的,去搜索一下吧。

通常,用这两句之一回答你的人会给你一份包含你需要内容的手册或者一个网址,而且他们打这些字的时候也正在读着。这些答复意味着回答者认为

  • 你需要的信息非常容易获得
  • 你自己去搜索这些信息比灌给你,能让你学到更多

你不应该因此不爽;依照技术圈的标准,他已经表示了对你一定程度的关注,而没有对你的要求视而不见。你应该对他祖母般的慈祥表示感谢。

而且即使他没有附文档给你,甚至完全没有回复你,你也不应感到气愤。

绝不要自以为理应得到答案,你没有为这种服务支付任何报酬。

—————————————

解释:关于一些看起来很无礼的发言:

很多技术圈子中看似无礼的行为并不是存心冒犯。相反,它是直接了当,一针见血式的交流风格,这种风格更注重解决问题,而不是使人感觉舒服而却模模糊糊。

如果你觉得被冒犯了,试着平静地反应。如果有人真的做了出格的事,邮件列表、新闻群组或论坛中的前辈多半会招呼他。如果这没有发生而你却发火了,那么你发火对象的言语可能在社区中看起来是正常的,而将被视为有错的一方,这将伤害到你获取信息或帮助的机会。

夸张的讲法是:你要的是“友善”(以上述方式)还是有用?两个里面挑一个。

当技术圈的某人说你搞砸了,并且(无论多么刺耳)告诉你别再这样做时,他正在为关心他的社区而行动。对他而言,不理你并将你从他的生活中滤掉更简单。如果你无法做到感谢,至少要表现得有点尊严,别大声哀嚎,也别因为自己是个有戏剧性超级敏感的灵魂和自以为有资格的新来者,就指望别人像对待脆弱的洋娃娃那样对你。

当然,喵在协会和学校里通常不会,喵怎么说也是个学长,而且还要尊重自己喵的设定,大部分时候喵会卖个萌然后让你自己去搜索,但是在喵使用另一个身份的技术圈,我不确定你们是否看见过转发到群里这段聊天记录。

2020-11-10_203835.png

———————————————–

在你确认你尝试了以上各种方法,并花费了至少20分钟以上仍没能解决问题之后,是时候提一个正确的问题来解决自己的疑惑了。

一个正确的问题是指什么呢?请看以下问题

救命啊!!!急急急!!!!快帮我看看!!!有人能帮帮我吗?我电脑坏了!!请把解决方法私发到我邮箱!好人一生平安!!

请问这个问题表达了什么?

针对这种问题,我的第一反应是:首先你死不了,然后我一点也不着急,你这么问问题没人能帮你,我知道你电脑坏了但是你啥信息都不说我懒得一句句问,发到你私人邮箱别的遇到这问题的人不就看不到解决方法了吗?所以我还是不理你了吧。

我对要我问你二十个问题才找得出你真正问题的问题没兴趣 —— 我有更重要的事要做呢。在看到这类问题的时候,我的反应通常不外如下三种

  • 你还有什么要补充的吗?
  • 真糟糕,希望你能搞定。
  • 这关我屁事?

一个正确的问题应该怎样表述呢?

以我和zed最近遇到的一个问题为例

首先,问题的最开头或标题处应该以简略但精准的语言表述你遇到的问题

QV2ray连接成功但无法上网

然后在后面加上你所使用的环境,能够找到的报错,以及其它你能提供的信息

Archinux下QV2ray 连接成功但无法上网,vmess无法使用,日志报错read/write on closed pipe;但ssr链接使用正常

然后表示你已经做了哪些努力但仍然无法解决

Archinux下QV2ray 连接成功但无法上网,vmess无法使用,日志报错read/write on closed pipe;但ssr链接使用正常,我通过搜索引擎没有找到相关的资料,试着重装Qv2ray并删除配置文件无效,验证对时显示ntp服务器时间误差为0秒

最后客气的表示希望能得到帮助

Archinux下QV2ray 连接成功但无法上网,vmess无法使用,日志报错read/write on closed pipe;但ssr链接使用正常,我通过搜索引擎没有找到相关的资料,试着重装Qv2ray并删除配置文件无效,验证对时显示ntp服务器时间误差为0秒,请问这个问题大家有解决方案吗?

这样一来大家第一眼就能看到Archlinux,Qv2ray,有所涉猎的人会看一下这个问题自己是否知道,比如看到read/write on closed pipe知道这个人遇到了系统对时错误,再往下看这个人做了很多努力没能解决问题,而且态度很好,我刚好遇到过这个问题,帮他解决一下吧

而如果你的问题是这样的:

“救命啊我翻墙翻不出去!我什么都做对了但是它就是不好使,dalao们快出来救救我”

你会得到怎样的回答就不言而喻了

提问的智慧原文链接

C语言快速入门

第一部分:一门基础的编程语言

走计算机行业不管哪个方向,会一两门编程语言都是必须的,第一部分讲一下如何快速的掌握一门基础的编程语言。

这里以c语言作为讲解对象

首先我们要明晰一个基本的概念,就是模块化
c语言是一个模块化了的语言,这个模块化体现在很多方面,比如函数,比如结构体,比如多文件。

首先说一下函数

函数是c语言的基本单位

一个c语言程序,就是由一个个变量拼成结构体,一个个结构体与函数拼成文件,最后再由一个个文件拼成最后的整个程序

那么函数是怎么样的呢?

1
2
3
4
void main()
{
//代码内容
}

这就是一个最简化的函数(当然我们通常不用void)

这个函数的结构是这样的

1
2
3
4
void main()    //函数声明      简单理解为表示创建一个函数
{ //代码块开始
//代码块可以简单理解为我们要运行的一段代码
} //代码块结束

这里的void是指返回值的类型,void表示没有返回值,为什么函数需要返回值呢?因为我们在执行一个函数的时候通常是需要它来实现某个功能的,如果没有返回值我们就不知道它有没有成功执行,或者不知道它的执行结果了(譬如执行开平方以后我们没有收到返回值,平方是开完了,但是结果捏?????)

而且有些时候我们的系统在执行函数的时候也强制要求返回值(一些系统,不是所有系统)

综上,我们的程序最好提供一个返回值

因此在事实上我们的一个函数表达出来其实是这样的

1
2
3
4
typename main(){
//函数内容
return volue;
}

这里的typename常用的有 int char double long short float

分别对应为整数,字符,双精度浮点数,长数,短数,浮点数,浮点数就是带小数点的数,计算机处理浮点数有误差,double比float精准一些

这里我们所写的函数名字叫main

main函数也就是程序的主函数,主函数就是程序开始执行的地方,也就说不管在主函数前面后面写多少函数都没有用,计算机只有找到主函数才能开始执行

由于是主函数,所以我们的返回值自己是没有办法用到了,但是有的计算机系统可能会要求,因此通常的要求是返回一个0,告诉系统程序正确执行完成,也就是

1
2
3
4
5
int main()
{
//函数内容
return 0
}

这里我们运行一下这个程序给大家看看结果

(展示)因为我们什么都没有执行,所以也什么都没有显示

那么怎么证明我们的程序真的执行成功了呢?这里我们再增加一行用于显示的指令

1
printf("hello world")

printf是c语言里的打印(显示到屏幕)语句,这句话的意思是显示hello world

加上之后我们再来运行一下

如左下角,加上以后我们的命令行就显示出了我们要显示的内容,这证明本喵刚才认真讲了没有胡说八道(骄傲)

可能刚才有银还意识到一个问题,就是刚才的程序比上面说的多了一行

1
#include <stdio.h>

这一句的意思又是虾米捏?

#后面加的字符是c语言里的预处理指令,就是编译器程序执行前预先处理的指令,用来补足程序运行中需要的一些东西

这个#include就是包含头文件的意思

#include <stdio,h>

的意思就是这个程序要包含头文件stdio.h里面的内容,stdio.h是c语言编译器自带的一个头文件,包含了输入输出的函数之类的一些常用的功能函数,printf()其实就是一个函数,它在stdio文件里面,我们使用的printf就是调用了程序外部头文件stdio.h里面的print函数。

刚才咱们已经说了,c语言由很多函数构成,下面咱们再定义一下别的函数来个多函数的程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include 

int dnlm()
{
printf(" pass your mother" );
return 0;
}


int main()
{
dnlm();
return 0;
}


由于这个不是主函数了所以函数名就可以自己起了,不过这里要注意,函数名不能和关键字(c语言里面已经被占用的词)同名

这里我们起名(丢乃老母)

呐,这里左下角就正确显示了

这里是先定义了丢乃老母函数然后在主函数里面用=调用了它

1
dnlm();

就酱,我们写了一个多个函数的c语言程序,(在学习编程的初期可以先不用考虑多文件,先从一个文件写起)

然后下面我们讲一坨选择分支结构

这个选择分支结构它是这个样子的

1
2
3
4
5
6
7
8
if()
{
//如果条件成立执行
}
else
{
//如果条件不成立执行
}

这里我们添加一个丢乃老父并且声明母亲父亲两个整数来测试一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include 

int dnlm()
{
int mother=30;//声明整数mother等于30
int father=20; //声明整数father等于20
if (mother>father) //如果mother大于father
{
printf(" pass your mother" ); //显示
}
else //否则
{
printf(" pass your father" ); //显示
}
return 0;
}

int main()
{
dnlm();
return 0;
}


运行结果正确的显示在了左下角。

这样一来证明我们的选择结构发挥了它应有的效果

刚才讲完了选择分支结构,这次我们从循环结构开始讲,一课时之内把c语言入门讲完,然后看看时间能不能够讲一些其他的东西。

循环结构

首先是while循环,这是平常会经常用到的一个循环

1
while(循环条件){    //代码内容}

while循环的结构是这样的,首先判断循环条件是不是成立,例如循环条件是变量a>=2,那么当a大于等于2的时候,就会执行代码内容一次,然后再次判断a是不是还大于等于2,如果是的话再执行一次。直到有一次发现a不再大于等于2了,才结束循环开始执行循环外面的内容。

这样一来的话,实际上也就是说,我们在写这种循环的时候,里面一定要写改变循环条件的代码,否则循环条件一直不变就变成死循环了。

while循环是先判断然后才决定执不执行的,所以while循环最少会执行0次,也就是说如果一开始条件就不成立的话就一次都不执行。

与之相对的是do。。。while循环

1
do{    //代码内容}while()

do。。。while和while相反,dowhile是先做了再说,做一遍到做完以后再判断,是不是条件成立 ,条件成立的话继续循环,条件不成立的话到此为止,继续往下执行,do while循环是至少会执行一次的循环。

与while系列循环用的同样多的就是for循环了

1
for(i=100;i>=0;i--){    //代码内容}

for循环的条件分为三部分,第一部分是声明控制循环的变量,比如声明个i=100,第二个部分和while的控制条件一样是用来判断循环执不执行,最后一个部分是用来声明控制变量的变化,就和while一样,for也需要改变控制条件来防止死循环,不过for直接在控制条件里就可以写好改变控制变量的语句。

以上是循环结构的三种语句。

跳转语句

下面进行今天的第二部分,跳转语句

c语言里的跳转语句其实就是goto

语法也很简单,在任意一个地方立下flag,然后就可以随时goto到这个地方了

1
2
3
:lable1
//此处省略两百万亿行
goto lable1

就可以直接飞回flag

goto用的好的话不仅可以跳转还可以实现各种循环,不过goto很容易出现不知道飞到了哪里去但是编译器不报错导致查错人员头比地球还大的现象,因此一般的来说,不建议使用goto语句。

数组

数组部分也很简单,

我们平常声明一个变量a,可以储存一个数据。

我们现在声明一个数组a[100],就可以储存100个数据

1
数据类型 数组名[数组长度]

可以通过a[0-99]来分别使用这100个数据,就不用写100遍声明变量了,而且如果我们要查找的数有好几个特点,还可以使用二维或者多维数组。

比如a[1][2][3][4][5][6][7]就是七维数组里第二组的第三小组的第四小组的第五小组的第六小组的第七小组的第八个数

这里要注意数组里面排序序号是从零开始的,第一个内容的编号是0,所以数组名加数字实际访问到位置是数字加一的位置存的数据。

从本质上而言,数组其实就是一种指针的应用方式。

指针

语言里面指针是核心精髓所在,所谓指针,就是画一个箭头指向数据储存的地方

我们平常声明一个变量,a=100,大概就相当于在白纸上划了一部分叫做a区,然后往里面记录了一个数字100,100写在a这个地方。

这块a区所在的位置就叫做a的地址

c语言里面和地址有关的有两个符号,一个是&,取地址符,一个是*,是指针的标记

我们平常创建变量的时候是这样的

1
int a;

我们创建指针时候是这样的

1
int* p;

标上一个*

表示这个变量p存储的是int变量的地址。

我们平常给变量赋值的方法是这样的

1
a=100;

我们给指针赋值的时候是这样的

1
p=&a;

这里的&a的意思就是取得a的地址

看到这里大家可能会有所疑惑,这个指针和变量到底有什么区别呢?

这里举一个简单的例子来说明指针和直接调用变量的区别

坐在隔壁的小明想要抄你的试卷,他看了一下a区,把数字抄走了,这是b=a,

坐在隔壁的小明想要抄你的试卷,他拿走了你的试卷,这是b=&a

前者只是拿走了数据,后者拿走的数据的地址,那么这会导致什么呢?

当小明想要修改卷子的时候——

前者,b修改了卷子上的数值,a的卷子没事,因为b只是抄走了一个数据

后者,b修改了卷子上的数值,a惊叫一声:卧槽你把我卷子改了干啥!!!

这就是使用变量的值和使用变量的指针的区别

BUT!!!为什么我们需要使用指针呢????

当然是因为**有些时候我们必须使用指针!**

举个糖炒栗子!比如我们想要在某个函数里修改函数外的值的时候,我们就必须得使用指针。

这里我们就需要一点关于函数调用、形参与实参的知识。

有些时候我们要用的函数会需要传入一些数值,比如我们写一个求和的函数

1
2
3
4
int sum(int a,int b)  //声明一个返回值是整数的函数,使用时需要传入整数a和整数b
{
return a+b; //返回a+b的值
}

然后我们在使用的时候就需要传入两个值

1
2
sum(x,y) //x和y是之前已经弄好的存了值的变量
sum(10,20)//或者这样直接给两个数值

这里面的a和b就叫形参,也可以简单粗略地理解为参数所需的形式,x和y就是实参,可以简单粗略的理解为实际传入的参数。

在程序运行到调用sum函数的时候就会创建两个临时的变量a和b,然后把x和y的值传给a和b。

这里就出现了不使用指针无法解决的问题——如果我们想要把存储的结果还存在x里呢?

当然,这样其实还能解决,x=sum(x,y)就行了,但是如果我们要存到的地方不确定呢?比如根据sum计算结果的不同存到不同的地方,是不是没有办法啦!

而使用指针就可以很方便(才怪)的解决这个问题,我们不传入值,而是把地址传过去,小明不久可以直接修改你的试卷了吗?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
int main(int argc, char const *argv[])
{
int x=1;
int y=2;
sum (&x,&y);
return 0;
}

int sum(int* a,int* b)
{
*a=*a+*b;
return 0;
}

这样就完美的解决了这个问题。

那么这里出一道小题,如果想要修改指针变量int* a的值应该怎么办呢?

1
2
3
4
5
int sum(int** a,int** b)
{
*a=*a+*b;
return 0;
}

当然是传入指针的指针!

指针是可以一层套一层的,int************************************************ a都可以!

回到刚才,讲指针的时候我们有说数组是指针的一种应用,是什么意思呢?现在我们就可以解答这个问题了,数组就是根据你的需求创建了好多挨在一起的空间,然后数组的名字就是一个指针,指向第一块地方,然后数组[]里跟不同的数字就是把指针往后移不同的长度,指到不同的地方。

比如a[3],其实也就是*(a+3)的意思,你把a[3]写成*(a+3),*(3+a),3[a]都可以的,都是一样的东西。

这里也解答了为什么数组前面要加int double之类的类型的问题,因为声明数组的时候需要创建出一些地方来存数据,数组前面的数据类型是用来标识每一块地方多大的。

很高兴大家能听我说这么多,今天我要讲的东西就这么多。

大家有兴趣的话也可以自己写点东西练习一下,比如写个学生信息管理系统什么的,这节课提供的知识是足够的。这里也宣传一下我们的协会,

追求技术是一场漫长的旅程,很高兴在这里与你们相逢。