
能力说明:
具备数据库基础知识,了解数据库的分类,具备安装MySQL数据库的能力,掌握MySQL数据类型知识,基本了解常用SQL语句,对阿里云数据库产品有基本认知。
暂时未有相关云产品技术能力~
阿里云技能认证
详细说明前言 动态规划的算法题经常出现在大厂的面试中,它是非常适合考查候选人的一类题型,因为它难度适中,需要一定的技巧,而且根据习题可以有一定的变化,所以如果想去大厂,建议大家好好刷一下此类题目,接下来我会写一些动态规划的相关题解,希望能对大家理解此类习题有所帮助。今天我们来看一道腾讯面试题,题目如下:有如下 8 x 8 格子,机器人需要从入口走到出口,每次只能朝右或朝下走,粉色格子为障碍物,机器人不能穿过,问机器人从入口走到出口最少需要的步数是多少? 题解分析 首先最容易想到的当然是暴力解法,对于机器人来说,每一步都可以向下或向右走所以我们可以用暴力算法求出所有路径所需求步数,然后求其最小步数,伪代码如下 paths(start, end) = 1+min(paths(start下方格子, end), paths(start右边格子, end)) 时间复杂度是多少?对于机器人所处的每一个格子来说,下一步可以走两步(向右或向下),共有 N 个格子,所以共有 O(2^n) 步可走,指数级别!暴力解法显然有很大的问题。这道题其实考察的是用动态规划的思想来求解。动态规划主要解题思路是用自底向上的思想来求解,求入口到出口的最短路径叫自顶向上,而求出口到入口的最短路径即为自底向上。怎么求,我们先看下出口的上一个位置。对这个位置来说,它往出口走只需要一步,所以我们在它的位置上填1,同理,它的上一个位置必须经由此位置走到出口,所以它的上一个位置应该填 2,依此类推,我们可以在右边填上这些格子走到出口的步数同理可得底部的格子到出口的位置,如下:可能有人会说了,如果右边和底边有个格子有障碍物咋办,如下对于这种情况,由于障碍物上面的格子不可能通向出口,所以障碍物上面的格子应该填无穷大(另外,显而易见,所有障碍物本身所在格子应该填无穷大),如下以上最右列和最底边格子所填数字即为动态规划的 base case,求完了 base case,还要得出动态规划的「状态转移方程」,得出状态转移方程后,动态规划求解基本上就大功告成了,一起来看下怎么求解。现在我们再从右到左,从下到上依次遍历每个格子,求出每个格子到出口的最小步数,我们知道每个格子的下一步只能向右或向下,所以每个格子到出口的最小步数可按如下公式求解 当前格子到出口的最小步数 = 1 + min(右格子到出口最小步数,下方格子到出口的最小步数) 此公式即为状态转移方程举个例子,以下方的 A,B 两个格子为例对于 A 格子来说,它的右格子,下方格子到出口的最小步数是 1,所以其到出口的最小步数为 1+min(1,1) = 2。对于 B 来说,它右格子到出口的最小步数为 3,其下格子是障碍物,到出口的步数为无穷大,所以 B 到出口的最小步数为 1 + min(∞, 3) = 4。如下依此类推,我们可以得出每个格子到出口的最小步数,如下所示:填满之后到了入口位置,显然入口到出口的最少步数应该是 1 + min(13,13) = 14 步。以下是代码,注释写得很清楚了,相信大家应该能看懂。 public class Solution { /** * 初始化格子,假设为 8 x 8, -1 代表格子为障碍物 */ private static final int GRIDS[][] = { {0,0,0,0,0,0,0,0}, {0,0,-1,0,0,0,-1,0}, {0,0,0,0,-1,0,0,0}, {-1,0,-1,0,0,-1,0,0}, {0,0,-1,0,0,0,0,0}, {0,0,0,-1,-1,0,-1,0}, {0,-1,0,0,0,-1,0,0}, {0,0,0,0,0,0,0,0} }; static private int getMinStep() { /** * 格子为 8 x 8 */ int N = 8; // 如果格子为障碍物,则此格子到出口的距离标记为无究大(这里用100000表示),代表此格子到出口不通 Integer infinity = 100000; /** * 初始化最右边的格子,从最后一列的倒数第二行开始(因为最后一个格子为出口) */ for (int row = N-2; row >= 0; row--) { // 如果当前格子的下一个格子不是障碍物,则当前格子到出口的最小步数为 1 + 下个格子到出口的步数 if (GRIDS[row+1][N-1] != -1) { GRIDS[row][N-1] = 1 + GRIDS[row+1][N-1]; } else { // 如果下一个格子是障碍物,则此格子到出口的步数设置为无穷大(代表此路不通),这里用正整数的最大值表示 GRIDS[row][N-1] = infinity; } } /** * 初始化最底部的格子,从最后一行的倒数第二列开始(因为最后一个格子为出口) */ for (int column = N-2; column >= 0; column--) { // 如果当前格子右边的格子不是障碍物,则当前格子到出口的最小步数为 1 + 右格子到出口的步数 if (GRIDS[N-1][column+1] != -1) { GRIDS[N-1][column] = 1 + GRIDS[N-1][column+1]; } else { // 如果是障碍物,则到出口的步数为无穷大,这里用正整数的最大值表示 GRIDS[N-1][column] = infinity; } } /** * 从右到左,从下到上填满每个格子的值,由于最右和最底部的格子都初始化了, * 所以从倒数第二行,倒数第二列开始遍历 */ for (int i = N - 2; i >= 0; i--) { for (int j = N - 2; j >= 0; j--) { // 说明是障碍物,所在格子到出口步数显然为无穷大(代表此路不通) if (GRIDS[i][j] == -1) { GRIDS[i][j] = infinity; continue; } /** * 当前格子到出口的最小步数为1+(右边的格子到出口的步数与下格子到出口步数之和的最小值) */ GRIDS[i][j] = 1 + Math.min(GRIDS[i+1][j], GRIDS[i][j+1]); } } /** * 遍历完之后起点格子对应的值即为最终所求的解 */ return GRIDS[0][0]; } public static void main(String[] args) { int result = Solution.getMinStep(); System.out.println("result = " + result); } } 理清了思路,剩下用代码实现就相对简单了,接下来我们分析下时间复杂度和空间复杂度。时间复杂度中间有两层循环,所以显然为 O(N^2),空间复杂度呢,我们并没有分配额外的空间来存储数据,而是复用了代表迷宫的格子数组 GRIDS,所以空间复杂度为 O(1)。有人可能会问了,为啥可以直接用 GRIDS 来计算格子到出口的步数,这样不就把格子的信息(如是否是障碍物)给覆盖了吗。这里就要了解一下动态规划的无后效性了,啥叫无后效性。以上文所举例子为例,对于图中的 A,B 格子来说,由状态转移方程 当前格子到出口的最小步数 = 1 + min(右格子到出口最小步数,下方格子到出口的最小步数) 可知,计算它到出口的最短步数只与它的右格子与下方格子到出口的最小步数有关(此时右格子与下方格子的步数已经计算出来)也就是说对于 A,B 格子来说,它只关心它的右格子与下方格子中的步数,至于这两个格子的步数是如何算出来的,它们不关心,这就叫无后效性。所以我们可以从右到左,从下到上依次遍历每个格子的步数,将其填入 GRIDS 中,这样虽然 GRIDS 中的格子信息被覆盖了,但对计算被遍历到的格子到出口的步数没有影响。巧妙使用无后效性在很多场景下可以有效压缩空间,降低空间复杂度。最后给大家留个思考题,本题我们只是算了从入口到出口的最小步数,如果我要打印这个最小步数对应的最短路径(即经过了哪些格子),该怎么解呢,欢迎大家留言回答。 作者 | 码海来源 | 码海原文链接
原文链接 网络部新员工 我叫Robert,是Linux帝国一个普通的公民。今天是我第一天上班的日子,我下了好大功夫才考上了帝国的公务员,根据我的成绩,我被分到了帝国网络部。一进入帝国的办公园区,我就被眼前的景象惊呆了,一座座高楼大厦,富丽堂皇,鳞次栉比,我忍不住驻足多看了几眼。这些大楼上面都有招牌,最高的那一座是帝国的进程&线程管理部门的办公大厦,旁边还有内存管理部门、文件管理等部门办公大厦。 网卡驱动部门 我只顾东张西望,不小心跟旁边小路跑出来的一位小哥撞了一个满怀,他手里抱的一堆数据散落的满地都是。我一边道歉,一边帮他捡起数据。“这位小哥行色匆匆,不知要去哪里啊”,我好奇的问到。“哦,你好,我是网卡驱动部门的,这是从网卡那里刚刚拿到的数据包,我得赶紧交给协议栈处理”,说完整理了下数据,就匆忙离开了。寻着他出来的地方看去,不远处就是他说的网卡驱动部门,难道我办公的地方就在这里?我沿着这条小路走了过去。一进入网卡驱动部门,出现在眼前的就是一副热火朝天的景象,收包的,解包的,发包的,一群人忙的不亦乐乎。“这么早就这么忙碌了啊”,我问门口的保安大叔。“是啊,这平时这个点也没什么网络访问,不知道今天怎么回事,一大早的数据就传输个不停”,保安说到。我指着里面一个员工问保安:“大叔,那人在干嘛呢?一直在转来转去的”“你说他啊,他在从网卡轮询读取数据包呢!”“轮询?网络数据包不是网卡发中断通知吗,干嘛要去轮询呢?”,我不解的问到。“以前是这样的,不过后来CPU那边有个叫阿Q的家伙不干了,说网卡数据太频繁,老是打断他们正常的工作。不仅如此,中断响应的时候还得把中断给关了,避免出现错误,时间久了,键盘、鼠标等单位就得不到响应纷纷闹事了”,保安说完点了一支香烟。我若有所思的点了点头,“那现在就改成轮询了?不过这样好浪费时间哦”保安吐了一个烟圈,继续说到:“倒也不是全都是轮询,现在把处理过程分成了两段,最开始的第一部分还是靠中断来通知的,这个时候需要关一下中断,不过通知后不会真正处理数据包,而是开启了一个软中断,所以关不了太久时间。第二部分在软中断中去轮询处理的,这个时候就不用关中断了。把硬中断和轮询结合了一下,就不用每个数据包来都中断一次了,也不用关中断太长时间,还给这技术取了个名字叫NAPI”“保安大叔,你怎么什么都知道啊?”“我以前就在里面工作啊,现在年纪稍微大了些,比不上年轻人,就让我来当保安了,唉~”,大叔说完又猛抽了一口香烟,整理了下自己日益稀疏的头发。“唉,对了,你是谁啊,怎么没见过你?”“我是帝国网络部新来的员工,今天来报道的。我把手中的录取通知书递给了保安”保安大叔看了看说到:“你走错了,不是这里,你该去网络协议栈大厦”。 协议栈大厦 离开网卡驱动部门,我继续前行终于找到了网络协议栈大厦,这便是我今后工作的地方了。走近一看,这座网络大厦并不如前面看到的高大,只有三层高,每一层的墙上都挂着一个巨大的招牌,上面写着这一层的名字,从上向下分别是: 应用层 传输层 网络层 大厦的门口还有一个收发室,门牌上写着netif_receive_skb,收发室坐着一位大爷。正在这时,先前碰到的小哥又来了,将手里的数据放到了收发室就离开了,看来这里就是网络协议栈的入口了。大爷拆开这个数据包看了看,随即按了下按钮,数据包就顺着管道传到了背后协议栈大厦一楼的一个办公室,我抬头一看,上面写着IPv4。再向旁边看去,还有好几间办公室,分别写了IPv6、ICMP、IGMP、ARP···我来到这个IPv4的门口,里面也是忙的不亦乐乎,有分片的、组包的、计算校验和的、有条不紊。办公室正中央有一个圆柱形的管道,通向了二楼,一楼处理完毕就通过这管道把数据包送了上去。墙上还有另一个管道,上面写着netfilter hook,不知道是通向了哪里。“你找谁?”,我正看得入神,里面一个负责人发现了我,我赶紧表明来意。他看过我手中的录取通知书后说到,“你是在传输层啊,出门右拐上二楼就是了。我们这一楼都是网络层协议的办公室。”我又看了下手里的录用通知书,这才发现被分配在了传输层工作。 传输层工作 来到二楼,总算见到了我的主管。“Robert,欢迎加入网络部,工作岗位在传输层的TCP小组,大家欢迎!”“谢谢主管!谢谢大家!”“这是Cerf,你刚来,就让他先带带你,有什么不懂的就向他请教吧。”我点头感谢,和一旁这个叫Cerf的握了握手。接下来,主管向我介绍了咱们传输层的几个小组的情况:TCP、UDP、SCTP、UDP-lite······我这才知道,原来传输层不是只有TCP和UDP。Cerf带我来到了工位,不愧是国有单位,无比宽敞,桌上还有一堆奇怪的设备。“这是一堆什么东西啊”,我问Cerf。“这些都是定时器,后面你工作处理TCP连接会用到的”我点了点头,环顾四周,工位旁边的墙壁上还贴满了什么东西,我凑近了一看,才发现满满的都是RFC几千条的规定。“好好看,以后的工作可是要天天用到这些东西呢”,Cerf略开玩笑的说着。“这些我基本都背的下来了,要不然我也考不到这里来”,我笑着说,略带一些得意。Cerf也笑了笑,“别大意,之前也有人也说过这话,后来还不是走了”我有些尴尬,不知道说些什么,这时办公室中央的管道里冒出了一个数据包。“Robert,你刚来,这个新的连接数据包就交给你来处理下,熟悉下工作流程”,主管说到。我刚刚放松的心情一下紧张了起来,毕竟以前都是纸上谈兵,还从没有真正处理过数据包呢。我小心翼翼的接过这个数据包,定位到TCP的头部,瞧了一眼标志位,发现SYN位是1,看来是有新的连接到来了,接下来不就是三次握手吗,我再熟悉不过了。我准备了一个响应包,将SYN标记和ACK标记都点亮后,接下来就犯了难了。这个确认号ACK我倒是知道是对方的序列号+1,不过我回复的序列号该是多少呢?一时之间,不知道如何是好。你们知道吗?在线等,挺急的。** 彩蛋 CPU一号车间的阿Q又闹脾气了。“我们花了大量时间把网卡数据搬运到内存,重复又没有技术含量,我受够了!”预知后事如何,请关注后续精彩······ 作者 | 轩辕之风O来源 | 编程技术宇宙
大家好,今天来分享一个有意思的分钱模拟问题,为了帮助大家理解,采取了可视化的方式。这个问题描述是这样的:房间里有 100 个人,每人都有 100 元钱,他们在玩一个游戏。每轮游戏中,每个人都要拿出一元钱随机给另一个人,最后这 100 个人的财富分布是怎样的?猜一下,经过 10000 次的交换,你们认为最后的结果会是怎么样子的?54321登登登,答案是这个样子的。和你的直觉想法有出入吗?是不是一开始认为是平均分布的?事实上,很多人一开始都没想到结果会是这样子的。我们借助 Java GUI 来可视化的理解这个问题。首先初始化数据,一开始每人都有 100 元钱。 // 初始化数据 money = new int[100]; for(int i = 0 ; i < money.length ; i ++) money[i] = 100; 初始状态 然后每轮游戏中,每个人都要拿出一元钱随机给另一个人,for(int i = 0 ; i < money.length; i ++){ if(money[i] > 0){ int j = (int)(Math.random() * money.length); money[i] -= 1; money[j] += 1; } } 不够直观?那我们可以先排序再显示。 Arrays.sort(money); for(int i = 0 ; i < money.length; i ++){ if(money[i] > 0){ int j = (int)(Math.random() * money.length); money[i] -= 1; money[j] += 1; } } 排序 我们可以发现,初始时所有人的财富值相等,随着游戏的进行,财富值差距越来越大,而不是均匀分布。感兴趣继续研究的小伙伴可以下载下方的源码。完整代码:https://github.com/MisterBooo/AmazingAlgo参考阅读: 看的见的算法:https://coding.imooc.com/class/138.html 该如何面对这个残酷的世界?:https://www.sohu.com/a/162310796_99952273 来源 | 五分钟学算法作者 | 程序员吴师兄
原文链接 广告再临 “老周,有人找你”一大早,361杀毒公司的老周就被吵醒。今天的阳光很明媚,老周伸了伸懒腰,这才踱步走向工作室。“是谁一大早的就来吵吵,坏了我的瞌睡”,听得出来,老周有点不太高兴。“咚咚~”,老周微微抬头一瞥,只见一甜美女子出现在工作室的门前。老周一下从座椅上弹了起来,三步并作两步,走到女子面前,作出欢迎的手势:“美女请进”二人坐罢,老周扶了下镜框,又整理了一下格子衬衣,一副温文尔雅的作态,轻声问到:“不知美女到访,所为何事?”女子倒是一副焦急的样子,“您好,我是Chrome浏览器公司的小雪,最近我们访问千度网、淘贝网的网页中时常出现不少奇怪的广告,一直被投诉,听领导说361杀毒公司的周老师是这方面的专家,想请您帮忙诊断一下,到底这些广告是怎么来的”老周听得有些不好意思,连连挥手,“原来是小雪姑娘,哪里哪里,勇斗病毒木马,消灭流氓软件本就是我361公司的分内之事,在下也只是尽一些绵薄之力罢了”。“周老师别谦虚了,您之前揪出IE公司的木马入侵的事迹已经传遍整个Windows帝国了,大家都知道您的厉害。这一次广告的问题,就拜托了”,小雪看着老周,彷佛眼里闪着星星。“别客气,这事儿包在我身上了”小雪起身,连说了几句谢谢就离开了。 谁动了HTTPS流量 此刻,负责网络数据过滤的大白正在忙碌着,突然一只手搭在了他的肩膀上,大白回头一看,正是老周。“老周,什么风把你吹到这里来了,你不在安全实验室分析恶意代码,跑我们网络部门来干嘛?”老周拍了下大白的肩膀,说到:“大白啊,有点事想请你帮帮忙,你帮我瞅瞅,Chrome浏览器的流量中是不是被插入广告了?”“就这事啊,前段时间发现路由器老给插入广告,我就给做了特征屏蔽,原以为它们消停了,这才没几天又卷土重来了?”,大白说完调出了Chrome公司的流量,准备一看究竟。大白越看眉头锁的越紧,“应该没有吧,我看访问千度网和淘贝网都是用的HTTPS协议,按理说路由器没有可能插入广告了啊”“HTTPS协议?为什么用这个就没法插入广告?”,老周问到。“这都不知道啊,你这361公司安全实验室领导怎么当上去的啊”,大白一脸无语的表情。老周有点难为情,“唉,老弟你也别取笑我了,这个术业有专攻嘛,我擅长病毒木马代码的分析,对网络协议这块确实知之甚少,劳烦大白老弟给说道说道”大白似乎是感觉自己的话说的有些重了,也借坡下坎,“老周啊,刚才我跟你开玩笑的,你可别往心里去啊”。 “没事没事,你快给我说说这HTTPS协议,帮助我早点破案吧”“好嘞,你稍等啊”,说完,大白开始在白板上画了起来。 什么是HTTPS “HTTPS = HTTP + SSL/TLS,这门技术,说简单也简单,说复杂也复杂。简单来说,就是为了网络数据的安全性,通过加密传输的方式来对传统上网的HTTP流量进行保护”,大白一边画着图一边给老周讲述。“明白,那么问题来了,用什么加解密算法呢?对方如何知道用什么算法以及用什么密钥解密呢?”,老周一下抓到了关键点。“唉,问到点子上了。在正式传输数据之前,双方会有一个协商过程,为后面所选择的加密算法,以及要使用的密钥达成一致。” “那么问题又来了,这个协商的内容要是被别人知道了,他不就可以按图索骥,解密传输的内容了吗?”,老周的反应很快。“老周果然是老周!加密算法被知道是无所谓了,毕竟算法都是公开的,关键在于这个用于后续加密的密钥,这个才是需要保护的关键,这个不能让别人知道”,说罢,大白又继续画起来。“so?怎么保护这个密钥呢?你倒是说啊”,老周有点着急了。 “注意哦,高能来了,双方使用一个叫非对称加密的方式来传输...”"等一下",老周打断了大白,“非对称加密,这是个什么意思?”大白默默叹了一口气,“常见的加密方式叫对称加密算法,所谓对称,就是加密和解密使用同一个密钥。那与此相对的,非对称加密,就是说加密和解密使用的是不同的密钥,明白了吧”老周略微思索,点了点头,“我知道了,你继续刚才说的,怎么用这个非对称加密算法来传输后面需要的密钥呢”大白继续说到:“客户端产生一个随机数,使用公钥加密,发给服务端,服务端使用私钥解密取得这个随机数,再根据这个随机数和其他信息计算出一个key,就作为后续加密内容使用的密钥了” “等等,客户端的公钥是哪里来的?”“最开始的时候,客户端发来请求,服务端在响应中,会把公钥告诉客户端。好了,我画完了,整个过程就是这样的”,大白放下画笔,一副完整的HTTPS协议握手过程图浮现了出来:老周反复端详,许久总算开口:“过程我倒是看懂了,不过我总感觉这不多此一举吗,直接使用非对称加密算法不就得了,这么折腾” 大白连连挥手,“你想的倒是简单,非对称加解密算法执行起来麻烦的多,耗费的时间会多很多倍,如果全程使用非对称加密算法,那将会严重影响上网体验。算法是个好算法,但用的代价也很大,所以权衡之下,好钢用在刀刃上,就只用来传输密钥,后面的正式数据传输,还是用常规的对称加密算法,来的经济划算。” 老周点了点头,一会儿低头思考,一会儿又抬头看着流程图。又过了许久,老周指着流程图,再次提问:“我说大白,如果我在客户端和服务端之间插入一个角色,对客户端冒充服务端,对服务端又冒充客户端,就能从中作梗,修改数据包,插入广告了是吧?”正在喝水的大白听后呛得连连咳嗽,“你说的就是中间人攻击嘛!你当HTTPS是玩具嘛,这么容易就被劫持,笑话!注意看图,那里有个认证环节,不是谁都能冒充的” 老周又看了看图,“怎么认证法,我倒是听听”“在服务端的响应中,我前面说的公钥是在一个叫证书的东西里面,这个证书就是用来标识服务端的身份的,是由权威机构颁发的,客户端收到证书后,会检查是否是可信任的,如果不受信任就会及时中止后面的流程。” “那如何判断一个证书是可信任的呢?”“帝国早已把受信任的证书安装好了,届时只需调用API查一下即可”老周思来想去,总觉得哪里有问题,却又说不上来。 真相只有一个 一连过了几天,老周依旧毫无头绪,这事儿就这样搁置了。 福无双至,祸不单行。这案子还没弄明白,firefox公司又出事了。原来,361杀毒公司检测到firefox秘密启动了有木马特征的进程,老周再一次带队前往勘查,firefox公司的小狐负责对接此事。 老周来到了firefox磁盘存储目录,打算先排查一下木马文件是什么来头。“这是一堆什么数据?”,老周指着一堆文件问到。“周老师,这是网页缓存数据”,一旁的小狐回答。“打开看看,看看能否找到一些攻击痕迹” 老周环顾四周,指着另一堆文件问到:“这又是一堆什么数据?”“周老师,这是一堆证书信息,HTTPS握手时认证服务器所用的,跟这次攻击事件应该没关系的”,小狐继续解释到。“认证用?帝国不是存储了受信认的证书吗,你们还保存证书信息做什么?”,老周有些不解。“帝国存储的受信任证书我们可不认,谁知道那里面都是些什么证书,太不可靠了,我们firefox浏览器公司自己做认证,不用那一套”,小狐言语之间流露着些许得意。 听完小狐的回答,老周突然愣住了,短暂的几ms之后反应了过来,掏出了从Chrome公司拿来的千度网证书,打算请小狐看一下。 小狐接过证书,仔细察看,片刻之后一口咬定的说:“这证书有问题!”老周眼前闪过一道亮光,追问到:“哪里有问题?”“这证书颁发机构叫ABSafe,不在我们受信任的列表中!再说了,我这里有缓存千度网的证书,根本不是这样的,这肯定是假的,你看”老周拿着两个证书反复查看,不时点点头,之前困扰多时的问题终于有了答案。 “我明白了,真相只有一个!一定是有人把这个ABSafe颁发机构安装到了帝国受信任列表,以此骗过了Chrome公司!进行了HTTPS中间人劫持!YES!”,老周说完用力挥了挥拳头。“周老师,您在说什么啊,我怎么听不懂?”,看着老周自言自语,小狐满脸的问号。 老周叮嘱同行的老齐继续勘察,匆忙拜别小狐就离开了。这天夜里,两个黑影出现在了帝国受信任根证书仓库。 “原来是有人把我们安装的根证书给删掉了,难怪刚才Chrome浏览器访问千度网报了警告”,其中一个胖的黑影说到。瘦的黑影捂住了胖子的嘴巴,“嘘,你给我把着点风,我去重新装上”瘦的黑影蹑手蹑脚走了过去,从怀里掏出了一个东西。“别动!安全检查!”,突然一束光线射了过来,原来老周带着队伍在此潜伏多时了。 “竟然是你们,禁广大师!千度网和淘贝网的广告也是你们加的是吧?”,老周大声质问。 胖瘦黑影面面相觑,老实交代了一切。未完待续······· 彩蛋 “老齐,firefox公司的案子有什么发现吗”“老周,你还是再来一趟吧,情况有点复杂” 作者 | 轩辕之风来源 | 编程技术宇宙
原文链接 我是一个函数 我是一个函数,名叫str_upper,我可以把输入的字符串从小写变成大写。不信你看,我长这样: char* str_upper(char* str, int len) { char upper[256]; if (len >= 256 || len <= 0) return nullptr; for (int i = 0; i < len; i++) { if (str[i] >= 'a' && str[i] <= 'z') { upper[i] = str[i] - 32; } else { upper[i] = str[i]; } } return upper; } 上面是我的源代码形式,听我的好朋友str_lower说,一会儿我们就要一起被送到一个叫编译器的地方加工处理了,我心里害怕极了。 编译器之旅 没多久,我们就来到了这里,一座很庞大到高楼,里面有好多精密的机器在不停的运转着。一进入大厅,好多函数代码在这里排队等待。我抬头向上望去,不知道有多少层楼,每一层都有一个指示牌,从下往上分别写着: 预处理 词法分析 语法分析 语义分析 ··· 再往上太远就看不太清楚了。所有的函数代码按照文件为单位排好队,静静地等待着。不过没有等太久,就轮到了我们这一队。来了一个工作人员把我们带到了一个房间,让我们都好好躺着,一台机器快速的从头到尾扫描了一遍,将我们所在文件中出现的#include和#define全部给替换掉了。接着,通过房间里的电梯,将我们送上了二楼。接下来的一段时间,我们在好几层楼都做了“体检”,每个函数都被那些像CT一样的机器照了个遍。不一会儿,来到了编译层,这一层有一个特别奇怪的机器,我看到一个个函数被送了进去,出来的时候都变了样子。不仅如此,接待处的工作人员看起来很凶,我这下更加紧张了。 函数调用约定 工作人员拿到了我的资料,瞅了几眼,问到:“请问你的调用约定是什么?”我有些懵,不太懂他的意思,小声问到:“不好意思,你刚问什么?”工作人员有点不耐烦了,提高了音量,“我是问你调用约定是什么?调用约定啊!”看见我仍然一脸茫然,工作人员直接给我的资料上调用约定那一栏盖上了一个标记:cdecl。我有点摸不着头脑,同行的小伙伴str_lower拽了我一下说到:“他是在问你函数的调用约定,就是约定调用函数的方式,涉及怎么传递参数,谁来恢复调用栈等”他这一说我才反映过来,“这个调用约定都有哪些可选的呢?”“一般有三种:” cdcel,参数从右往左入栈,主调函数负责恢复栈平衡 stdcall,参数从右往左入栈,被调函数负责恢复栈平衡 fastcall,参数通过寄存器传递,寄存器不够再用栈传递 “他刚才看你没有显式声明,就默认给你cdecl的方式了”,小伙伴继续说到。我点了点头,原来调用个函数还有这么多讲究呐! Stack Canary “别闲聊了,快进去吧!”,工作人员催我了。我准备走向那台可怕的机器。“唉,等一下”,正紧张着,工作人员又叫住了我。我回头看去,工作人员正招手让我过去。“你好,是我的代码有什么问题吗?”,我紧张的问到,生怕有错误被打回去,连累我们整个文件都要被遣返。“不是,是我注意到你的函数里有一个局部数组,需要给你加一下栈溢出保护”,工作人员说到。我看了下我的代码,确实有一个局部字符数组: char upper[256]; “栈溢出保护是什么啊?”,我小声问到。工作人员没有搭理我,忙着给我的资料上加东西。旁边的小伙伴又把我拽了过去,说到:“咱们函数里面定义的局部变量、参数是存放在线程栈里面的。线程要不断游走在不同的函数中,调用函数后为了能回到原来的地方,调用之前把返回地址也放在了线程栈里。就像这样,你看会不会有什么问题:”我仔细看了下,“哦,要是越界访问我的upper数组,那就可以修改返回地址,那可就危险了!”“很聪明嘛!”“那这个怎么加保护呢?”,我问到。“你看,函数进来之前,先在局部变量和返回地址之间设置一个数值,函数返回之前再去检查一下,如果栈里的数据被破坏了,检查这个数值就能发现,提前抛出异常!”,小伙伴耐心的解释到。“这样啊,那岂不是要把我打回去加上你说的这些设置和检查代码?”,我继续提问。这时,工作人员听到了我们的闲聊,“不用,我们编译器自动添加好了,快去吧,已经处理好了”我瞥了一眼,看到我的资料上增加了一个叫Stack Canary的标记。我小心翼翼的走进了那架奇怪的机器,立刻就失去了知觉,等我醒来时,我的身体已经发生了变化,变成了一堆奇怪的代码,现在我长这样了: 链接 没过一会儿,我们这一队的所有函数代码都编译完成,大家从原来的.c文件都搬到了新家:一个.o文件,我也再次见到了小伙伴str_lower。“咱们是不是已经完成了编译,可以离开这里了吧?”“还不行,编译虽然是完成了,还差链接这一步呢!”又过了一小会儿,和我们一起过来的其他文件的函数代码也编译完成了,咱们一堆.o文件一起被送到了编译器大厦的顶楼:链接层。这一层也有一个巨大的机器,机器背后连接了一个管道,不知通向了哪里。我们这一批的所有.o文件挨个走进了这个巨大的机器,像是一条时空隧道一般,穿行于其间,我感觉到了巨大的压力把我们挤压在了一起,很快我们再一次失去了意识。醒来之后,我发现所有的函数们都被合在了一个文件中,这是一个可执行文件,而我的身体也再次发生了变化,变成了一段段的二进制指令,现在我长这样了:终于离开了编译器,真是一趟难忘的旅程,不过我再也不想来了······ 彩蛋 没想到命运跟我开了一个玩笑,我的第一次运行就出了错!我又要被打回去重新改造,再走一遍这魔鬼般的旅程。你能帮我看看,我的代码哪里有错吗? 作者 | 轩辕之风O来源 | 编程技术宇宙
2020年09月
2020年08月
2020年07月
进群反馈下哦【21746399】
https://developer.aliyun.com/adc/ 体验平台有类似教程,有什么疑问的话,进群问就好了。
建议提交下工单哦~
https://developer.aliyun.com/mirror/centos 里面有下载地址,可以自己找到对应版本进行下载哈。
登录控制台,点击【容器镜像服务】-->【镜像加速器】,即可查看。
正在准备了哈
进群反馈下哦【21746399】
https://developer.aliyun.com/adc/ 体验平台有类似教程,有什么疑问的话,进群问就好了。
建议提交下工单哦~
https://developer.aliyun.com/mirror/centos 里面有下载地址,可以自己找到对应版本进行下载哈。
登录控制台,点击【容器镜像服务】-->【镜像加速器】,即可查看。
正在准备了哈
正在准备了哈
可以的呀,不行就换成https试试
收到
收到~
这里找找看:https://mirrors.aliyun.com/pypi/simple/
是您那边的环境问题导致没法解析 mirror.aliyun.com 的域名了哈
收到,非常感谢您的建议,我们会尽快落地哒~
在这里:https://mirrors.aliyun.com/centos-vault/7.7.1908/isos/x86_64/
CentOS7.7在这里:https://mirrors.aliyun.com/centos-vault/7.7.1908/
收到。
收到~
收到~
用https://mirrors.aliyun.com
后期官方会添加的哈~