<p><strong><span style="text-wrap: nowrap;">确认虚拟机网络是否正常:</span></strong><br/></p><ul class=" list-paddingleft-2" style="list-style-type: disc;"><li><p><span style="text-wrap: nowrap;">虚拟机是否可以访问外网</span></p></li><li><p><span style="text-wrap: nowrap;">虚拟机是否与windows网络互通。</span></p></li></ul><p><strong><span style="text-wrap: nowrap;">查看ip:</span></strong></p><pre class="brush:ps;toolbar:false">ifconfig</pre><p><span style="text-wrap: nowrap;">提示命令未找到,安装net-tools:</span></p><pre class="brush:ps;toolbar:false">sudo apt install net-tools</pre><p><strong><span style="text-wrap: nowrap;">windows下查看IP:</span></strong></p><pre class="brush:ps;toolbar:false">ipconfig</pre><p><strong><span style="text-wrap: nowrap;">测试虚拟机是否可以访问外网:</span></strong></p><pre class="brush:ps;toolbar:false">ping www.baidu.com</pre><p><strong><span style="text-wrap: nowrap;">测试虚拟机与windows主机是否互通:</span></strong></p><p><span style="text-wrap: nowrap;">互相ping对方ip</span></p><p><span style="text-wrap: nowrap;">Windows power shell: </span><span style="text-wrap: nowrap;">ping 虚拟机ip;</span></p><p><span style="text-wrap: nowrap;">虚拟机:</span><span style="text-wrap: nowrap;">ping windows ip</span></p><p><strong><span style="text-wrap: nowrap;">将虚拟机的ip改为固定ip</span></strong><br/></p><p><span style="text-wrap: nowrap;">如果你的虚拟机每次重启的时候,ip地址可能会发生变化。当我们访问一个项目的时候,由于虚拟机的ip发生了变化,会导致项目访问不到的情况。</span></p><p><span style="text-wrap: nowrap;">这样操作会很麻烦,如果能把虚拟机的ip改成固定的就好了。</span><br/></p><p><span style="text-wrap: nowrap;">下面是修改的步骤:</span><br/></p><p><span style="text-wrap: nowrap;">第一步:在windows中打开cmd,使用ipconfig /all命令查看windows机器ip,如:10.1.53.218</span></p><p><span style="text-wrap: nowrap;">第二步:确认网卡设置为桥接网卡</span></p><p><span style="text-wrap: nowrap;">第三步:进入ubuntu桌面,点击右上角网络图标,点有线设置,如下图:</span></p><p><br/></p><p><img src="/uploads/images/20230212/409e5c3849fcfd30100c2f1754d43079.png" title="1.png" alt="" width="859" height="520"/></p><p><span style="text-wrap: nowrap;">第四步.设置ip,子网掩码,网关:</span></p><p><span style="text-wrap: nowrap;">1.切换到ipv4选项卡,ipv4,由自动改为手动。</span></p><p><span style="text-wrap: nowrap;">2.填写固定ip,如第一步查看到的windows的ip为**10.1.53.218**</span></p><p><span style="text-wrap: nowrap;">我们虚拟机这里的ip要保持和windows在一个网段,如设置为:10.1.53.132</span></p><p><span style="text-wrap: nowrap;">(与windows机器ip保持在同一网段,即前三位相同,最后一位填写1-255中间的一个值),</span></p><p><span style="text-wrap: nowrap;">3.子网掩码、网关与windows保持一致</span></p><p><span style="text-wrap: nowrap;">4.DNS填写114.114.114.114即可</span></p><p><span style="text-wrap: nowrap;">如图:</span></p><p><img src="/uploads/images/20240616/91601151da34a520b2cab5d5a1d9c2a7.png" title="image.png" alt=""/></p><p><span style="text-wrap: nowrap;">最后,重启虚拟机,再次输入ifconfig命令查看虚拟机ip,ip被设置为10.1.53.132,并使用'ping www.baidu.com'测试是否可以连网,如果有返回结果,即设置成功。以后再重启虚拟后,ip将</span></p><p><span style="text-wrap: nowrap;">保持固定不变。</span></p><p><br/></p>
文章列表
<h5 style="color:red;">系统学习shopify开发,推荐小册:<a style="color:blue;" href="https://www.maxiaoke.com/manual/shopify_dev.html" target="_blank">《Shopify应用实战开发》</a></h5> <div class="image-container"> <p> <a style="color:blue;" href="https://www.maxiaoke.com/manual/shopify_dev.html" target="_blank"> <img src="https://www.maxiaoke.com/uploads/images/20230612/03b58362c0718203696abbd7c748a136.jpg" alt="Shopify应用实战开发"> </a> </p> </div> <div class="text-container" style="font-size:14px; color:#888"> <p>这本小册将领您进入 Shopify 平台,学习开发出Shopify应用程序。作为全球最受欢迎的电子商务平台之一,Shopify 提供了一个强大的基础架构,让开发者可以创建个性化、功能丰富的在线商店。本课程将专注于 Shopify 应用开发,为您提供全面的指导和实践机会,打造功能齐全的app,帮助商家实现收益增长,作为个人开发者从中赚取收益。</p> </div> <hr><h5 style="color:red;">系统学习shopify开发,推荐小册:<a style="color:blue;" href="https://www.maxiaoke.com/manual/shopify_dev.html" target="_blank">《Shopify应用实战开发》</a></h5> <div class="image-container"> <p> <a style="color:blue;" href="https://www.maxiaoke.com/manual/shopify_dev.html" target="_blank"> <img src="https://www.maxiaoke.com/uploads/images/20230612/03b58362c0718203696abbd7c748a136.jpg" alt="Shopify应用实战开发"> </a> </p> </div> <div class="text-container" style="font-size:14px; color:#888"> <p>这本小册将领您进入 Shopify 平台,学习开发出Shopify应用程序。作为全球最受欢迎的电子商务平台之一,Shopify 提供了一个强大的基础架构,让开发者可以创建个性化、功能丰富的在线商店。本课程将专注于 Shopify 应用开发,为您提供全面的指导和实践机会,打造功能齐全的app,帮助商家实现收益增长,作为个人开发者从中赚取收益。</p> </div> <hr><h5 style="color:red;">系统学习shopify开发,推荐小册:<a style="color:blue;" href="https://www.maxiaoke.com/manual/shopify_dev.html" target="_blank">《Shopify应用实战开发》</a></h5> <div class="image-container"> <p> <a style="color:blue;" href="https://www.maxiaoke.com/manual/shopify_dev.html" target="_blank"> <img src="https://www.maxiaoke.com/uploads/images/20230612/03b58362c0718203696abbd7c748a136.jpg" alt="Shopify应用实战开发"> </a> </p> </div> <div class="text-container" style="font-size:14px; color:#888"> <p>这本小册将领您进入 Shopify 平台,学习开发出Shopify应用程序。作为全球最受欢迎的电子商务平台之一,Shopify 提供了一个强大的基础架构,让开发者可以创建个性化、功能丰富的在线商店。本课程将专注于 Shopify 应用开发,为您提供全面的指导和实践机会,打造功能齐全的app,帮助商家实现收益增长,作为个人开发者从中赚取收益。</p> </div> <hr><p><span style="color: rgb(77, 82, 89); font-family: "Microsoft YaHei", sans-serif; text-wrap: wrap; background-color: rgb(255, 255, 255);">这门课程将带领您进入 Shopify 平台,学习开发出Shopify应用程序。作为全球最受欢迎的电子商务平台之一,Shopify 提供了一个强大的基础架构,让开发者可以创建个性化、功能丰富的在线商店。本课程将专注于 Shopify 应用开发,为您提供全面的指导和实践机会,打造功能齐全的app,帮助商家实现收益增长,作为个人开发者从中赚取收益。</span></p><p><span style="color: rgb(77, 82, 89); font-family: "Microsoft YaHei", sans-serif; text-wrap: wrap; background-color: rgb(255, 255, 255);">shopify是一个独立站平台,类似于国内的淘宝,京东。商家可以在shopify平台开启自己的小店,打造自己的电子商务外贸平台。<br/></span></p><p><span style="color: rgb(77, 82, 89); font-family: "Microsoft YaHei", sans-serif; text-wrap: wrap; background-color: rgb(255, 255, 255);">同时,为了方便商家打造自己独特的个性平台,shopify平台提供了二次开发功能。商家可以通过自己编程的方式,为自己的店铺添加功能,帮助平台增加销售。</span></p><p><span style="color: rgb(77, 82, 89); font-family: "Microsoft YaHei", sans-serif; text-wrap: wrap; background-color: rgb(255, 255, 255);">shopify二次开发包含两个方向:shopify的app二次开发和shopify的店铺模板美化二次开发。</span></p><p><span style="color:#4d5259;font-family:Microsoft YaHei, sans-serif"><span style="background-color: rgb(255, 255, 255);">shopify是一款国外的平台,目前在国内关于技术方面的二次开发教程较少。因此,码小课平台为大家提供了专门的shopify二次开发教程。本教程以一本小册的形式,为大家介绍shopify二次开发一个app并将该app添加到自己的店铺的完整项目开发过程。同时,也可以将自己开发出来的app上传到shopify官方平台上上架销售,当其他商家安装了我们开发的shopify功能的app后,会给我们支付收益。我们可以在shopify平台上赠取收益。</span></span></p><p><span style="color:#4d5259;font-family:Microsoft YaHei, sans-serif"><span style="background-color: rgb(255, 255, 255);">本小册是国内唯一一部真正深入到shopify功能二次开发的教程。包含了shopify二次开发的完整流程,填补了国内shopify二次开发中文教程资料稀少的问题。</span></span></p><p><span style="color:#4d5259;font-family:Microsoft YaHei, sans-serif"><span style="background-color: rgb(255, 255, 255);">大家可以在码小课中学习该教程,本小册一共分为二十几章内容,从shopify创建店铺,到创建shopify项目代码,进行二次开发编写代码,最终将自己开发的app上线等完整功能。</span></span></p><p><span style="color:#4d5259;font-family:Microsoft YaHei, sans-serif"><span style="background-color: rgb(255, 255, 255);">通过这个shopify中文教程学会shopify的二次开发后,无论是想自己在shopify官方开店,给自己的店铺增加功能,还是单纯的以个人开发者名义开发shopify的app上架到shopify应用市场进行销售,赚取收益,都可以从这本shopify应用实战开发小册中掌握到全部技能。</span></span></p><p><span style="color:#4d5259;font-family:Microsoft YaHei, sans-serif"><span style="background-color: rgb(255, 255, 255);">最后,祝愿大家早日掌握shopify二次开发技能。</span></span></p><p><span style="color:#4d5259;font-family:Microsoft YaHei, sans-serif"><span style="background-color: rgb(255, 255, 255);">本小册链接地址如下,可以直接点击跳转学习:</span></span></p><p><a href="http://www.maxiaoke.com/manual/shopify_dev.html" target="_blank"><strong>shopify应用实战开发</strong></a><br/></p><p><br/></p>
<h2 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 1em; font-weight: 300; line-height: 1.225; font-size: 1.75em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; padding-bottom: 0.5em; border-bottom: 1px solid rgb(238, 238, 238);">一、数据压缩介绍</h2><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px;">数据从服务器传输到客户端,需要传输时间,文件越大传输时间就越长,为了减少传输时间,我们一般把数据压缩后在传给客户端。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px;">apache支持两种压缩:deflate、gzip</p><h4 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 16px; font-weight: 300; line-height: 1.4; font-size: 1.25em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background: transparent; transition: all 0.3s linear 0s; outline: none !important;"></a>mod_gzip 和mod_deflate比较</h4><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px;">首先一个区别是安装它们的Apache Web服务器版本的差异。Apache 1.x系列没有内建网页压缩技术,所以才去用额外的第三方mod_gzip 模块来执行压缩。而Apache 2.x官方在开发的时候,就把网页压缩考虑进去,内建了mod_deflate 这个模块,用以取代mod_gzip。虽然两者都是使用的Gzip压缩算法,它们的运作原理是类似的。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px;">第二个区别是压缩质量。mod_deflate 压缩速度略快而mod_gzip 的压缩比略高。一般默认情况下,mod_gzip 会比mod_deflate 多出4%~6%的压缩量。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px;">那么,为什么使用mod_deflate?第三个区别是对服务器资源的占用。 一般来说mod_gzip 对服务器CPU的占用要高一些。mod_deflate 是专门为确保服务器的性能而使用的一个压缩模块,mod_deflate 需要较少的资源来压缩文件。这意味着在高流量的服务器,使用mod_deflate 可能会比mod_gzip 加载速度更快。</p><h4 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 16px; font-weight: 300; line-height: 1.4; font-size: 1.25em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background: transparent; transition: all 0.3s linear 0s; outline: none !important;"></a><strong style="box-sizing: border-box;">应用场景:数据压缩传输</strong></h4><h4 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 16px; font-weight: 300; line-height: 1.4; font-size: 1.25em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background: transparent; transition: all 0.3s linear 0s; outline: none !important;"></a><strong style="box-sizing: border-box;">优化目的:提升用户访问页面加载速度,节约带宽</strong></h4><h2 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 1em; font-weight: 300; line-height: 1.225; font-size: 1.75em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; padding-bottom: 0.5em; border-bottom: 1px solid rgb(238, 238, 238);"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background: transparent; transition: all 0.3s linear 0s; outline: none !important;"></a>二、数据压缩实现</h2><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px;">1)开启模块</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px;">LoadModule deflate_module modules/mod_deflate.so</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px;">2)调用模块</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px;">DeflateCompressionLevel 4</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px;">AddOutputFilterByType DEFLATE text/html text/plain text/xml application/x-javascript application/x-httpd-php</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px;">AddOutputFilter DEFLATE js css</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px;">BrowserMatch \bMSIE\s[1-6] dont-vary</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px;">SetEnvIfNoCase Request_URI .(?:gif|jpe?g|png)$ no-gzip dont-vary</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px;">SetEnvIfNoCase Request_URI .(?:exe|t?gz|zip|bz2|sit|rar)$ no-gzip dont-vary</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px;">SetEnvIfNoCase Request_URI .(?:pdf|doc)$ no-gzip dont-vary</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;"><IfModule deflate_module> # 压缩等级 4 1-9,数字越大压缩的越好,也越占用CPU时间 DeflateCompressionLevel 4 # 压缩类型 html、xml、php、css、js AddOutputFilterByType DEFLATE text/html text/plain text/xml application/x-javascript application/x-httpd-php AddOutputFilter DEFLATE js css #浏览器匹配 IE1-6的不压缩 BrowserMatch \bMSIE\s[1-6] dont-vary #设置不压缩的文件,注意图片本身就是压缩过的,所以不需要再压缩 SetEnvIfNoCase Request_URI .(?:gif|jpe?g|png)$ no-gzip dont-vary SetEnvIfNoCase Request_URI .(?:exe|t?gz|zip|bz2|sit|rar)$ no-gzip dont-vary SetEnvIfNoCase Request_URI .(?:pdf|doc)$ no-gzip dont-vary </IfModule></pre><h2 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 1em; font-weight: 300; line-height: 1.225; font-size: 1.75em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; padding-bottom: 0.5em; border-bottom: 1px solid rgb(238, 238, 238);"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background: transparent; transition: all 0.3s linear 0s; outline: none !important;"></a>三、测试</h2><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px;">1)生成HTML数据页面</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">for i in `seq 1 20`;do cat /etc/passwd >> /usr/local/apache/htdocs/test_deflate.htmldone</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px;">2)未启用压缩前通过浏览器访问该页面,通过开发者工具查看页面大小</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px;"><img src="https://www.maxiaoke.com/uploads/images/20231230/96989d3e0f9fc03d9cb8f7566788784b.png" alt="" width="1145" height="656"/></p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px;">3)启用压缩再次通过浏览器访问该页面,通过开发者工具查看页面大小,如果明显变小了则说明压缩成功。也可以从响应头中看出多了压缩字段。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px;"><img src="https://www.maxiaoke.com/uploads/images/20231230/a43bc0eb502efe4f293d709c893b8c3d.png" alt="" width="1151" height="502"/></p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px;"><img src="https://www.maxiaoke.com/uploads/images/20231230/a8697fe774807c14a91ef3d4c9fc3611.png" alt="" width="1136" height="764"/></p><p>上</p><p><br/></p>
<h2 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 1em; font-weight: 300; line-height: 1.225; font-size: 1.75em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; padding-bottom: 0.5em; border-bottom: 1px solid rgb(238, 238, 238); color: rgb(77, 82, 89); text-wrap: wrap;">一、静态缓存介绍</h2><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">用户每次访问网站都会将页面中的所有元素都请求一遍,全部下载后通过浏览器渲染,展示到浏览器中。但是,网站中的某些元素我们一般都是固定不变的,比如logo,框架文件等元。,用户每次访问都需要加载这些元素。这样做好处是保证了数据的新鲜,可是这些数据不是常变化的,很久才变化一次。每次都请求、下载浪费了用户时间和公司带宽。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">所以我们通过静态缓存的方式,将这些不常变化的数据缓存到用户本地磁盘,用户以后再访问这些请求,直接从本地磁盘打开加载,这样的好处是加载速度快,且节约公司带宽及成本。</p><h4 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 16px; font-weight: 300; line-height: 1.4; font-size: 1.25em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; color: rgb(77, 82, 89); text-wrap: wrap;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a><strong style="box-sizing: border-box;">应用场景:数据缓存</strong></h4><h4 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 16px; font-weight: 300; line-height: 1.4; font-size: 1.25em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; color: rgb(77, 82, 89); text-wrap: wrap;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a><strong style="box-sizing: border-box;">优化目的:提升用户访问页面加载速度,节约带宽</strong></h4><h2 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 1em; font-weight: 300; line-height: 1.225; font-size: 1.75em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; padding-bottom: 0.5em; border-bottom: 1px solid rgb(238, 238, 238); color: rgb(77, 82, 89); text-wrap: wrap;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a>二、静态缓存实现</h2><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">1)修改apache主配置文件,加载缓存模块</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">LoadModule expires_module modules/mod_expires.so</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">2)针对虚拟主机或者目录设置缓存策略</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;"><IfModule expires_module> #开启缓存 ExpiresActive on #针对不同类型元素设置缓存时间 ExpiresByType image/gif "access plus 1 days" ExpiresByType image/jpeg "access plus 24 hours" ExpiresByType image/png "access plus 24 hours" ExpiresByType text/css "now plus 2 hour" ExpiresByType application/x-javascript "now plus 2 hours" ExpiresByType application/x-shockwave-flash "now plus 2 hours” #其他数据不缓存 ExpiresDefault "now plus 0 min" </IfModule> 缓存起始点 access 从当前访问时间开始 now (equivalent to 'access') 相当于access modification 从修改时间算起 缓存时间单位 years months weeks days hours minutes seconds</pre><h2 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 1em; font-weight: 300; line-height: 1.225; font-size: 1.75em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; padding-bottom: 0.5em; border-bottom: 1px solid rgb(238, 238, 238); color: rgb(77, 82, 89); text-wrap: wrap;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a>三、验证测试</h2><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;"><strong style="box-sizing: border-box;">缓存没有生效前</strong><br/><img src="https://www.maxiaoke.com/uploads/images/20231230/56b04d57f667a061ebea1ef833bc38b6.png" alt="" width="919" height="559"/></p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;"><strong style="box-sizing: border-box;">缓存生效后测试</strong><br/><img src="https://www.maxiaoke.com/uploads/images/20231230/3892f40e922975685fc76138fb5cdb1c.png" alt="" width="920" height="775"/><br/>响应头中加载了缓存字段</p><p style="box-sizing: border-box; margin-top: 0px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap; margin-bottom: 0px !important;">Cache-control 和 Expires,并且缓存的时间和我们预设的一致,成功啦。</p><p><br/></p>
<p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px;"><strong style="box-sizing: border-box;">案例需求</strong><br/>写一个程序,模拟KFC点餐系统,要求有以下功能:</p><ul style="box-sizing: border-box; margin-bottom: 16px; padding: 0px 0px 0px 2em;" class=" list-paddingleft-2"><li><p>1、点餐功能</p></li><li><p>2、结算功能</p></li><li><p>3、打印流水单</p></li></ul><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px;"><strong style="box-sizing: border-box;">案例步骤</strong></p><ul style="box-sizing: border-box; margin-bottom: 16px; padding: 0px 0px 0px 2em;" class=" list-paddingleft-2"><li><p>1、交互点餐</p></li><li><p>2、结账收银</p></li><li><p>3、打印流水单给客户</p></li></ul><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px;"><strong style="box-sizing: border-box;">案例代码</strong></p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">#!/bin/bash # #Author:www.zutuanxue.com # #Release: #Description: #1)录入单价 HBB=19.8 JC=12.3 KL=9.9 #2)定义输出 cat <<EOF welcome to KFC 今天KFC提供菜品如下: 1)汉堡 2)鸡翅 3)可乐 EOF echo -e "\n请您输入希望购买菜品的数量,不需要输入0\n" ###1.用户交互 #定义变量类型为整形 declare -i NUM_HBB declare -i NUM_JC declare -i NUM_KL read -p "汉堡: " NUM_HBB read -p "鸡翅: " NUM_JC read -p "可乐: " NUM_KL ###2.计算输出 HBB_price=`echo "scale=2;$HBB*$NUM_HBB"|bc` JC_price=`echo "scale=2;$JC*$NUM_JC"|bc` KL_price=`echo "scale=2;$KL*$NUM_KL"|bc` total_price=`echo "scale=2;$HBB_price+$JC_price+$KL_price"|bc` ###3.付款 echo -n "合计: $total_price " read -p "请付款: " USER_price ###4.打印小票 clear echo -e "\t\tKFC结算单" echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" echo -e "商品\t单价\t数量\t合计" echo -e "汉堡包\t$HBB\t$NUM_HBB\t$HBB_price" echo -e "鸡翅\t$JC\t$NUM_JC\t$JC_price" echo -e "可乐\t$KL\t$NUM_KL\t$KL_price" echo -e "\n\n" echo "总计: $total_price" echo -e "支付:$USER_price" echo -e "找零: `echo "scale=2;$USER_price-$total_price"|bc`" echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" echo -e "地址:北京东大桥路33号KFC店\n联系电话:400-123-456\nwww.kfc.com"</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px;"><strong style="box-sizing: border-box;">案例效果</strong></p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 0px !important;"><img src="https://www.maxiaoke.com/uploads/images/20231230/6d68b3f528b66d82c1e094d122cad552.gif" alt=""/></p><p><br/></p><p><br/></p>
<p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;"><strong style="box-sizing: border-box;">计算机编程就是三大步:输入、运算、输出</strong></p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">那么计算机运算有哪些呢,计算机能做哪些运算呢?</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;"><strong style="box-sizing: border-box;">我们来看看常见的计算机运算</strong></p><h2 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 1em; font-weight: 300; line-height: 1.225; font-size: 1.75em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; padding-bottom: 0.5em; border-bottom: 1px solid rgb(238, 238, 238); color: rgb(77, 82, 89); text-wrap: wrap;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a>一、赋值运算</h2><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">赋值运算符 =</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;"> a=10 name='baism' 重点:字符串必须用引号引起来</pre><h2 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 1em; font-weight: 300; line-height: 1.225; font-size: 1.75em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; padding-bottom: 0.5em; border-bottom: 1px solid rgb(238, 238, 238); color: rgb(77, 82, 89); text-wrap: wrap;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a>二、算术运算[四则运算]</h2><h3 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 16px; font-weight: 300; line-height: 1.43; font-size: 1.5em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; color: rgb(77, 82, 89); text-wrap: wrap;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a>2.1 运算符与命令</h3><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;"><strong style="box-sizing: border-box;">四则运算符:</strong> + - <em style="box-sizing: border-box;">\ 【加减乘除】<br/><strong style="box-sizing: border-box;">扩展:</strong> % *</em> 【取余 开方】</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;"><strong style="box-sizing: border-box;">运算命令:</strong></p><ul style="box-sizing: border-box; margin-bottom: 16px; padding: 0px 0px 0px 2em; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;" class=" list-paddingleft-2"><li><p>整形运算<br/>– expr<br/>– let<br/>– $(())<br/>– bc</p></li><li><p>浮点运算<br/>– bc</p></li></ul><h3 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 16px; font-weight: 300; line-height: 1.43; font-size: 1.5em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; color: rgb(77, 82, 89); text-wrap: wrap;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a>2.2 整形运算</h3><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">expr 命令:只能做整数运算,格式比较古板,注意空格</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">[root@zutuanxue ~]# expr 1 + 1 2 [root@zutuanxue ~]# expr 5 - 2 3 [root@zutuanxue ~]# expr 5 \* 2 #注意*出现应该转义,否则认为是通配符 10 [root@zutuanxue ~]# expr 5 / 2 2 [root@zutuanxue ~]# expr 5 % 2 1</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">let命令:只能做整数运算,且运算元素必须是变量,无法直接对整数做运算</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">[root@zutuanxue ~]# let a=100+3;echo $a 103 root@zutuanxue ~]# let a=100-3;echo $a 97 [root@zutuanxue ~]# let a=100/3;echo $a 33 [root@zutuanxue ~]# let a=100*3;echo $a 300 [root@zutuanxue ~]# let a=100%3;echo $a 1 [root@zutuanxue ~]# let a=100**3;echo $a 1000000 [root@zutuanxue ~]# a=100 [root@zutuanxue ~]# let a++;echo $a 101 [root@zutuanxue ~]# let a--;echo $a 100 [root@zutuanxue ~]# let a-=3;echo $a 97 [root@zutuanxue ~]# let a+=5;echo $a 102</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">双小圆括号运算,在shell中(( ))也可以用来做数学运算</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">[root@zutuanxue ~]# echo $(( 100+3)) 103 [root@zutuanxue ~]# echo $(( 100-3)) 97 [root@zutuanxue ~]# echo $(( 100%3)) 1 [root@zutuanxue ~]# echo $(( 100*3)) 300 [root@zutuanxue ~]# echo $(( 100/3)) 33 [root@zutuanxue ~]# echo $(( 100**3)) #开方运算 1000000</pre><h3 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 16px; font-weight: 300; line-height: 1.43; font-size: 1.5em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; color: rgb(77, 82, 89); text-wrap: wrap;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a>2.3 浮点运算</h3><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">浮点运算是采用的命令组合的方式来实现的 echo “scale=N;表达式”|bc</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">[root@zutuanxue ~]# echo "scale=2;3+100"|bc 103 [root@zutuanxue ~]# echo "scale=2;100-3"|bc 97 [root@zutuanxue ~]# echo "scale=2;100/3"|bc 33.33 [root@zutuanxue ~]# echo "scale=2;100*3"|bc 300</pre><h3 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 16px; font-weight: 300; line-height: 1.43; font-size: 1.5em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; color: rgb(77, 82, 89); text-wrap: wrap;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a>2.4、练习案例</h3><h4 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 16px; font-weight: 300; line-height: 1.4; font-size: 1.25em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; color: rgb(77, 82, 89); text-wrap: wrap;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a>2.4.1 实现一个四则运算计算器</h4><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;"><strong style="box-sizing: border-box;">案例思考:</strong> 计算器的功能: + - * \</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;"><strong style="box-sizing: border-box;">实现步骤:</strong><br/>1、要求用户传输三个参数,num1 算术运算符 num2<br/>2、运算输出结果</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;"><strong style="box-sizing: border-box;">实现代码</strong></p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">##03_calculator.sh #!/bin/bash # #Author: www.zutuanxue.com #Release: #Description: 简单计算器 echo "$1 $2 $3"|bc</pre><h4 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 16px; font-weight: 300; line-height: 1.4; font-size: 1.25em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; color: rgb(77, 82, 89); text-wrap: wrap;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a>2.4.2 内存使用率统计,要求打印内存使用率</h4><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;"><strong style="box-sizing: border-box;">案例思考:</strong></p><ul style="box-sizing: border-box; margin-bottom: 16px; padding: 0px 0px 0px 2em; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;" class=" list-paddingleft-2"><li><p>物料1、内存总量 获得方式是什么 free top /proc/meminfo</p></li><li><p>物料2、内存使用量</p></li><li><p>物料3、内存使用率公式 使用量/总量*100%</p></li></ul><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;"><strong style="box-sizing: border-box;">实现步骤:</strong></p><ul style="box-sizing: border-box; margin-bottom: 16px; padding: 0px 0px 0px 2em; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;" class=" list-paddingleft-2"><li><p>1、获取内存总量</p></li><li><p>2、获取内存使用量</p></li><li><p>3、运算输出结果</p></li></ul><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;"><strong style="box-sizing: border-box;">实现代码</strong></p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">#job实现代码 04_memory_use.sh #!/bin/bash # #Author: www.zutuanxue.com #Release: #Description:内存使用率计算脚本 #free #1、获得内存总量 memory_total=`free -m|grep -i "mem"|tr -s " "|cut -d " " -f2` #2、获得内存使用的量 memory_use=`free -m|grep -i "mem"|tr -s " "|cut -d " " -f3` #3、计算输出 #运算的时候是否需要小数点 浮点运算,要考虑使用的命令 (难点 重点) #echo "内存使用率: $((memory_use*100/memory_total))%" #难点:浮点运算中,同优先级的情况下,大数除以小数 尽可能保证精确 echo "内存使用率: `echo "scale=2;$memory_use*100/$memory_total"|bc`%"</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;"><strong style="box-sizing: border-box;">实现效果</strong></p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">#运算结果[root@zutuanxue day2]# sh memory_use.sh Memory使用率: 2.61%</pre><h2 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 1em; font-weight: 300; line-height: 1.225; font-size: 1.75em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; padding-bottom: 0.5em; border-bottom: 1px solid rgb(238, 238, 238); color: rgb(77, 82, 89); text-wrap: wrap;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a>三、比较运算</h2><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">计算机除了算术和赋值运算外,还有比较运算,比如说比较两个数的关系,比较两个字符串的关系【用户登录系统】等。接下来我们学习如何在shell中进行比较运算</p><h3 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 16px; font-weight: 300; line-height: 1.43; font-size: 1.5em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; color: rgb(77, 82, 89); text-wrap: wrap;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a>3.1、整形比较运算</h3><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;"> 运算符解释: 精确比较 -eq 等于 equal -gt 大于 -lt 小于 模糊比较 -ge 大于或等于 -le 小于或等于 -ne 不等于</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;"><strong style="box-sizing: border-box;">通过test命令比较两个整数关系</strong></p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">[root@zutuanxue ~]# test 100 -gt 300;echo $? 1 [root@zutuanxue ~]# test 100 -ge 300;echo $? 1 [root@zutuanxue ~]# test 100 -eq 300;echo $? 1 [root@zutuanxue ~]# test 100 -le 300;echo $? 0 [root@zutuanxue ~]# test 100 -lt 300;echo $? 0 [root@zutuanxue ~]# test 100 -ne 300;echo $? 0 备注:linux命令test只能比较两个整数的关系,不会返回结果,需要通过$?才能看到结果</pre><h3 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 16px; font-weight: 300; line-height: 1.43; font-size: 1.5em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; color: rgb(77, 82, 89); text-wrap: wrap;"><br/><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a></h3>
<p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">只要不使用C的标准库函数,Go中是可以直接调用C和汇编语言的。其实道理很简单,Go的运行时库就是用C和汇编实现的,Go必须是能够调用到它们的。当然,会有一些额外的约束,这就是函数调用协议。</p><h2 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 1em; font-weight: 300; line-height: 1.225; font-size: 1.75em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; padding-bottom: 0.5em; border-bottom: 1px solid rgb(238, 238, 238); color: rgb(77, 82, 89); text-wrap: wrap;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a>Go中调用汇编</h2><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">假设我们做一个汇编版本的加法函数。首先GOPATH的src下新建一个add目录,然后在该目录加入add.go的文件,内容如下:</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">package add func Add(a, b uint64) uint64 { return a+b }</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">这个函数将两个uint64的数字相加,并返回结果。我们写一个简单的函数调用它,内容如下:</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">package main import ( "fmt" "add" ) func main() { fmt.Println(add.Add(2, 15)) }</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">可以看到输出了结果为17。好的,接下来让我们删除Add函数的实现,只留下定义部分:</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">package add func Add(a, b uint64) uint64</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">然后在add.go同一目录中建立一个add_amd64.s的文件(假设你使用的是64位系统),内容如下:</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">TEXT ·Add+0(SB),$0-24 MOVQ a+0(FP),BX MOVQ b+8(FP),BP ADDQ BP,BX MOVQ BX,res+16(FP) RET ,</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">虽然汇编是相当难理解的,但我相信读懂上面这段不会有困难。前两条MOVQ指令分别将第一个参数放到寄存器BX,第二个参数放到寄存器BP,然后ADDQ指令将两者相加后,最后的MOVQ和RET指令返回结果。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">现在,再次运行前面的main函数,它将使用自定义的汇编版本函数,可以看到成功的输出了结果17。从这个例子中可以看出Go是可以直接调用汇编实现的函数的。大多时候不必要你去写汇编,即使是研究Go的内部实现,能读懂汇编已经很足够了。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">也许你真的觉得在Go中写汇编很酷,但是不要忽视了这些忠告:</p><ul style="box-sizing: border-box; margin-bottom: 16px; padding: 0px 0px 0px 2em; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;" class=" list-paddingleft-2"><li><p>汇编很难编写,特别是很难写好。通常编译器会比你写出更快的代码。</p></li><li><p>汇编仅能运行在一个平台上。在这个例子中,代码仅能运行在 amd64 上。这个问题有一个解决方案是给 Go 对于 x86 和 不同版本的代码分别写一套代码,文件名相应的以_386.s和_arm.s结尾。</p></li><li><p>汇编让你和底层绑定在一起,而标准的 Go 不会。例如,slice 的长度当前是 32 位整数。但是也不是不可能为长整型。当发生这些变化时,这些代码就被破坏了。</p></li></ul><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">当前Go编译器不能将汇编编译为函数的内联,但是对于小的Go函数是可以的。因此使用汇编可能意味着让你的程序更慢。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">有时需要汇编给你带来一些力量(不论是性能方面的原因,还是一些相当特殊的关于CPU的操作)。对于什么时候应该使用它,Go源码包括了若干相当好的例子(可以看看 crypto 和 math)。由于它非常容易实践,所以这绝对是个学习汇编的好途径。</p><h2 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 1em; font-weight: 300; line-height: 1.225; font-size: 1.75em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; padding-bottom: 0.5em; border-bottom: 1px solid rgb(238, 238, 238); color: rgb(77, 82, 89); text-wrap: wrap;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a>Go中调用C</h2><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">接下来,我们继续尝试在Go中调用C,跟调用汇编的过程很类似。首先删掉前面的add_amd64.s文件,并确保add.go文件中只是给出了Add函数的声明部分:</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">package add func Add(a, b uint64) uint64</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">然后在add.go同目录中,新建一个add.c文件,内容如下:</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">#include "runtime.h" void ·Add(uint64 a, uint64 b, uint64 ret) { ret = a + b; FLUSH(&ret); }</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">编译该包,运行前面的测试函数:</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">go install add</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">会发现输出结果为17,说明Go中成功地调用到了C写的函数。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">要注意的是不管是C或是汇编实现的函数,其函数名都是以·开头的。还有,C文件中需要包含runtime.h头文件。这个原因在该文件中有说明:<br/>Go用了特殊寄存器来存放像全局的struct G和struct M。包含这个头文件可以让所有链接到Go的C文件都知道这一点,这样编译器可以避免使用这些特定的寄存器作其它用途。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">让我们仔细看一下这个C实现的函数。可以看到函数的返回值为空,而参数多了一个,第三个参数实际上被作为了返回值使用。其中FLUSH是在pkg/runtime/runtime.h中定义为USED(x),这个定义是Go的C编译器自带的primitive,作用是抑制编译器优化掉对*x的赋值的。如果你很好奇USED是怎样定义的,可以去$GOROOT/include/libc.h文件里去找找。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">被调函数中对参数ret的修改居然返回到了调用函数,这个看起来似乎不可理解,不过早期的C编译器确实是可以这么做的。</p><h2 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 1em; font-weight: 300; line-height: 1.225; font-size: 1.75em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; padding-bottom: 0.5em; border-bottom: 1px solid rgb(238, 238, 238); color: rgb(77, 82, 89); text-wrap: wrap;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a>函数调用时的内存布局</h2><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">Go中使用的C编译器其实是plan9的C编译器,和我们平时理解的gcc等会有一些区别。我们将上面的add.c汇编一下:</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">go tool 6c -I $GOROOT/src/pkg/runtime -S add.c</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">生成的汇编代码大概是这个样子的:</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">"".Add t=1 size=16 value=0 args=0x18 locals=0 000000 00000 (add.c:3) TEXT "".Add+0(SB),4,$0-24 000000 00000 (add.c:3) NOP , 000000 00000 (add.c:3) NOP , 000000 00000 (add.c:3) FUNCDATA $2,gcargs.0<>+0(SB) 000000 00000 (add.c:3) FUNCDATA $3,gclocals.1<>+0(SB) 000000 00000 (add.c:4) MOVQ a+8(FP),AX 0x0005 00005 (add.c:4) ADDQ b+16(FP),AX 0x000a 00010 (add.c:4) MOVQ AX,c+24(FP) 0x000f 00015 (add.c:5) RET , 000000 48 8b 44 24 08 48 03 44 24 10 48 89 44 24 18 c3 H.D$.H.D$.H.D$..</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">这是Go使用的汇编代码,是一种类似plan9的汇编代码。类似a+8(FP)这种表示的含义是“变量名+偏移(寄存器)”。其中FP是帧寄存器,它是一个伪寄存器,实际上是内存位置的一个引用,其实就是BP(栈基址寄存器)上移一个机器字长位置的内存地址。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">函数调用之前,a+8(FP),b+16(FP)分别表示参数a和b,而参数3的位置被空着,在被调函数中,这个位置将用于存放返回值。此时的其内存布局如下所示:</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">参数3参数2参数1 <-SP</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">进入被调函数之后,内存布局如下所示:</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">参数3参数2参数1 <-FP保存PC <-SP......</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">CALL指令会使得SP下移,SP位置的内存用于保存返回地址。帧寄存器FP此时位置在SP上面。在plan9汇编中,进入函数之后的前几条指令并没有出现<code style="box-sizing: border-box; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; font-size: 14px; color: rgb(232, 62, 140); overflow-wrap: break-word; padding: 3px; margin: 0px 4px 0px 5px; background: rgb(246, 246, 246); border-radius: 3px; border: 1px solid rgb(238, 238, 238);">push ebp; mov esp ebp</code>这种模式。plan9函数调用协议中采用的是caller-save的模式,也就是由调用者负责保存寄存器。注意这和传统的C是不同的。传统C中是callee-save的模式,被调函数要负责保存它想使用的寄存器,在函数退出时恢复这些寄存器。</p><p style="box-sizing: border-box; margin-top: 0px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap; margin-bottom: 0px !important;">需要注意的是参数和返回值都是有对齐的。这里是按Structrnd对齐的,Structrnd在源代码中义为sizeof(uintptr)。</p><p><br/></p>
<p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">什么?nil是一种数据结构么?为什么会讲到它,没搞错吧?没搞错。不仅仅是Go语言中,每门语言中nil都是非常重要的,它代表的是空值的语义。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">在不同语言中,表示空这个概念都有细微不同。比如在scheme语言(一种lisp方言)中,nil是true的!而在ruby语言中,一切都是对象,连nil也是一个对象!在C中NULL跟0是等价的。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">按照Go语言规范,任何类型在未初始化时都对应一个零值:布尔类型是false,整型是0,字符串是””,而指针,函数,interface,slice,channel和map的零值都是nil。</p><h2 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 1em; font-weight: 300; line-height: 1.225; font-size: 1.75em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; padding-bottom: 0.5em; border-bottom: 1px solid rgb(238, 238, 238); color: rgb(77, 82, 89); text-wrap: wrap;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a>interface</h2><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">一个interface在没有进行初始化时,对应的值是nil。也就是说<code style="box-sizing: border-box; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; font-size: 14px; color: rgb(232, 62, 140); overflow-wrap: break-word; padding: 3px; margin: 0px 4px 0px 5px; background: rgb(246, 246, 246); border-radius: 3px; border: 1px solid rgb(238, 238, 238);">var v interface{}</code>,</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">此时v就是一个nil。在底层存储上,它是一个空指针。与之不同的情况是,interface值为空。比如:</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">var v *T var i interface{} i = v</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">此时i是一个interface,它的值是nil,但它自身不为nil。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">Go中的error其实就是一个实现了Error方法的接口:</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">type error interface { Error() string }</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">因此,我们可以自定义一个error:</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">type Error struct { errCode uint8 } func (e *Error) Error() string { switch e.errCode { case 1: return "file not found" case 2: return "time out" case 3: return "permission denied" default: return "unknown error" } }</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">如果我们这样使用它:</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">func checkError(err error) { if err != nil { panic(err) } } var e *Error checkError(e)</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">e是nil的,但是当我们checkError时就会panic。请读者思考一下为什么?</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">总之,interface跟C语言的指针一样非常灵活,关于空的语义,也跟空指针一样容易困扰新手的,需要注意。</p><h2 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 1em; font-weight: 300; line-height: 1.225; font-size: 1.75em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; padding-bottom: 0.5em; border-bottom: 1px solid rgb(238, 238, 238); color: rgb(77, 82, 89); text-wrap: wrap;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a>string和slice</h2><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">string的空值是””,它是不能跟nil比较的。即使是空的string,它的大小也是两个机器字长的。slice也类似,它的空值并不是一个空指针,而是结构体中的指针域为空,空的slice的大小也是三个机器字长的。</p><h2 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 1em; font-weight: 300; line-height: 1.225; font-size: 1.75em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; padding-bottom: 0.5em; border-bottom: 1px solid rgb(238, 238, 238); color: rgb(77, 82, 89); text-wrap: wrap;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a>channel和map</h2><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">channel跟string或slice有些不同,它在栈上只是一个指针,实际的数据都是由指针所指向的堆上面。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">跟channel相关的操作有:初始化/读/写/关闭。channel未初始化值就是nil,未初始化的channel是不能使用的。下面是一些操作规则:</p><ul style="box-sizing: border-box; margin-bottom: 16px; padding: 0px 0px 0px 2em; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;" class=" list-paddingleft-2"><li><p>读或者写一个nil的channel的操作会永远阻塞。</p></li><li><p>读一个关闭的channel会立刻返回一个channel元素类型的零值。</p></li><li><p>写一个关闭的channel会导致panic。</p></li></ul><p style="box-sizing: border-box; margin-top: 0px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap; margin-bottom: 0px !important;">map也是指针,实际数据在堆中,未初始化的值是nil。</p><p><br/></p>
<p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">上一节已经介绍了哈希表的基本原理并实现了一个基本的哈希表,而在实际项目中,<br/>对哈希表的需求远不止那么简单。对性能,灵活性都有不同的要求。下面我们看看PHP中的哈希表是怎么实现的。</p><h2 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 1em; font-weight: 300; line-height: 1.225; font-size: 1.75em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; padding-bottom: 0.5em; border-bottom: 1px solid rgb(238, 238, 238); color: rgb(77, 82, 89); text-wrap: wrap;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a>PHP的哈希实现</h2><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">PHP内核中的哈希表是十分重要的数据结构,PHP的大部分的语言特性都是基于哈希表实现的,<br/>例如:变量的作用域、函数表、类的属性、方法等,Zend引擎内部的很多数据都是保存在哈希表中的。</p><h3 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 16px; font-weight: 300; line-height: 1.43; font-size: 1.5em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; color: rgb(77, 82, 89); text-wrap: wrap;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a>数据结构及说明</h3><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">上一节提到PHP中的哈希表是使用拉链法来解决冲突的,具体点讲就是使用链表来存储哈希到同一个槽位的数据,<br/>Zend为了保存数据之间的关系使用了双向链表来链接元素。</p><h4 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 16px; font-weight: 300; line-height: 1.4; font-size: 1.25em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; color: rgb(77, 82, 89); text-wrap: wrap;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a>哈希表结构</h4><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">PHP中的哈希表实现在Zend/zend_hash.c中,还是按照上一小节的方式,先看看PHP实现中的数据结构,<br/>PHP使用如下两个数据结构来实现哈希表,HashTable结构体用于保存整个哈希表需要的基本信息,<br/>而Bucket结构体用于保存具体的数据内容,如下:</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">[c] typedef struct _hashtable { uint nTableSize; // hash Bucket的大小,最小为8,以2x增长。 uint nTableMask; // nTableSize-1 , 索引取值的优化 uint nNumOfElements; // hash Bucket中当前存在的元素个数,count()函数会直接返回此值 ulong nNextFreeElement; // 下一个数字索引的位置 Bucket *pInternalPointer; // 当前遍历的指针(foreach比for快的原因之一) Bucket *pListHead; // 存储数组头元素指针 Bucket *pListTail; // 存储数组尾元素指针 Bucket **arBuckets; // 存储hash数组 dtor_func_t pDestructor; // 在删除元素时执行的回调函数,用于资源的释放 zend_bool persistent; //指出了Bucket内存分配的方式。如果persisient为TRUE,则使用操作系统本身的内存分配函数为Bucket分配内存,否则使用PHP的内存分配函数。 unsigned char nApplyCount; // 标记当前hash Bucket被递归访问的次数(防止多次递归) zend_bool bApplyProtection;// 标记当前hash桶允许不允许多次访问,不允许时,最多只能递归3次 #if ZEND_DEBUG int inconsistent; #endif } HashTable;</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">nTableSize字段用于标示哈希表的容量,哈希表的初始容量最小为8。首先看看哈希表的初始化函数:</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">[c] ZEND_API int _zend_hash_init(HashTable *ht, uint nSize, hash_func_t pHashFunction, dtor_func_t pDestructor, zend_bool persistent ZEND_FILE_LINE_DC) { uint i = 3; //... if (nSize >= 0x80000000) { /* prevent overflow */ ht->nTableSize = 0x80000000; } else { while ((1U << i) < nSize) { i++; } ht->nTableSize = 1 << i; } // ... ht->nTableMask = ht->nTableSize - 1; /* Uses ecalloc() so that Bucket* == NULL */ if (persistent) { tmp = (Bucket **) calloc(ht->nTableSize, sizeof(Bucket *)); if (!tmp) { return FAILURE; } ht->arBuckets = tmp; } else { tmp = (Bucket **) ecalloc_rel(ht->nTableSize, sizeof(Bucket *)); if (tmp) { ht->arBuckets = tmp; } } return SUCCESS; }</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">例如如果设置初始大小为10,则上面的算法将会将大小调整为16。也就是始终将大小调整为接近初始大小的<br/>2的整数次方。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">为什么会做这样的调整呢?我们先看看HashTable将哈希值映射到槽位的方法,上一小节我们使用了取模的方式来将哈希值<br/>映射到槽位,例如大小为8的哈希表,哈希值为100, 则映射的槽位索引为: 100 % 8 = 4,由于索引通常从0开始,<br/>所以槽位的索引值为3,在PHP中使用如下的方式计算索引:</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">[c] h = zend_inline_hash_func(arKey, nKeyLength); nIndex = h & ht->nTableMask;</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">从上面的_zend_hash_init()函数中可知,ht->nTableMask的大小为ht->nTableSize -1。<br/>这里使用&操作而不是使用取模,这是因为是相对来说取模操作的消耗和按位与的操作大很多。</p><blockquote style="box-sizing: border-box; margin: 0px 0px 16px; border-left: 4px solid rgb(238, 238, 238); font-size: 14px; padding: 0px 15px 0px 20px; color: rgb(102, 102, 102); font-style: italic; font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;"><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 0px;"><strong style="box-sizing: border-box;">NOTE</strong><br/>mask的作用就是将哈希值映射到槽位所能存储的索引范围内。 例如:某个key的索引值是21,<br/>哈希表的大小为8,则mask为7,则求与时的二进制表示为: 10101 & 111 = 101 也就是十进制的5。<br/>因为2的整数次方-1的二进制比较特殊:后面N位的值都是1,这样比较容易能将值进行映射,<br/>如果是普通数字进行了二进制与之后会影响哈希值的结果。那么哈希函数计算的值的平均分布就可能出现影响。</p></blockquote><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">设置好哈希表大小之后就需要为哈希表申请存储数据的空间了,如上面初始化的代码,<br/>根据是否需要持久保存而调用了不同的内存申请方法。如前面PHP生命周期里介绍的,是否需要持久保存体现在:持久内容能在多个请求之间访问,而非持久存储是会在请求结束时释放占用的空间。<br/>具体内容将在内存管理章节中进行介绍。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">HashTable中的nNumOfElements字段很好理解,每插入一个元素或者unset删掉元素时会更新这个字段。<br/>这样在进行count()函数统计数组元素个数时就能快速的返回。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">nNextFreeElement字段非常有用。先看一段PHP代码:</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">[php] <?php $a = array(10 => 'Hello'); $a[] = 'TIPI'; var_dump($a); // ouput array(2) { [10]=> string(5) "Hello" [11]=> string(5) "TIPI" }</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">PHP中可以不指定索引值向数组中添加元素,这时将默认使用数字作为索引,<br/>和<a href="http://en.wikipedia.org/wiki/Enumerated_type" style="box-sizing: border-box; color: rgb(51, 202, 187); text-decoration-line: none; background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;">C语言中的枚举</a>类似,<br/>而这个元素的索引到底是多少就由nNextFreeElement字段决定了。<br/>如果数组中存在了数字key,则会默认使用最新使用的key + 1,例如上例中已经存在了10作为key的元素,<br/>这样新插入的默认索引就为11了。</p><h4 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 16px; font-weight: 300; line-height: 1.4; font-size: 1.25em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; color: rgb(77, 82, 89); text-wrap: wrap;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a>数据容器:槽位</h4><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">下面看看保存哈希表数据的槽位数据结构体:</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">[c] typedef struct bucket { ulong h; // 对char *key进行hash后的值,或者是用户指定的数字索引值 uint nKeyLength; // hash关键字的长度,如果数组索引为数字,此值为0 void *pData; // 指向value,一般是用户数据的副本,如果是指针数据,则指向pDataPtr void *pDataPtr; //如果是指针数据,此值会指向真正的value,同时上面pData会指向此值 struct bucket *pListNext; // 整个hash表的下一元素 struct bucket *pListLast; // 整个哈希表该元素的上一个元素 struct bucket *pNext; // 存放在同一个hash Bucket内的下一个元素 struct bucket *pLast; // 同一个哈希bucket的上一个元素 // 保存当前值所对于的key字符串,这个字段只能定义在最后,实现变长结构体 char arKey[1]; } Bucket;</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">如上面各字段的注释。h字段保存哈希表key哈希后的值。这里保存的哈希值而不是在哈希表中的索引值,<br/>这是因为索引值和哈希表的容量有直接关系,如果哈希表扩容了,那么这些索引还得重新进行哈希在进行索引映射,<br/>这也是一种优化手段。<br/>在PHP中可以使用字符串或者数字作为数组的索引。<br/>数字索引直接就可以作为哈希表的索引,数字也无需进行哈希处理。h字段后面的nKeyLength字段是作为key长度的标示,<br/>如果索引是数字的话,则nKeyLength为0。在PHP数组中如果索引字符串可以被转换成数字也会被转换成数字索引。<br/><strong style="box-sizing: border-box;">所以在PHP中例如’10’,’11’这类的字符索引和数字索引10, 11没有区别。</strong></p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">上面结构体的最后一个字段用来保存key的字符串,而这个字段却申明为只有一个字符的数组,<br/>其实这里是一种长见的<a href="http://stackoverflow.com/a/4690976/319672" style="box-sizing: border-box; color: rgb(51, 202, 187); text-decoration-line: none; background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;">变长结构体</a>,主要的目的是增加灵活性。<br/>以下为哈希表插入新元素时申请空间的代码</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">[c] p = (Bucket *) pemalloc(sizeof(Bucket) - 1 + nKeyLength, ht->persistent); if (!p) { return FAILURE; } memcpy(p->arKey, arKey, nKeyLength);</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">如代码,申请的空间大小加上了字符串key的长度,然后把key拷贝到新申请的空间里。<br/>在后面比如需要进行hash查找的时候就需要对比key这样就可以通过对比p->arKey和查找的key是否一样来进行数据的<br/>查找。申请空间的大小-1是因为结构体内本身的那个字节还是可以使用的。</p><blockquote style="box-sizing: border-box; margin: 0px 0px 16px; border-left: 4px solid rgb(238, 238, 238); font-size: 14px; padding: 0px 15px 0px 20px; color: rgb(102, 102, 102); font-style: italic; font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;"><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 0px;"><strong style="box-sizing: border-box;">NOTE</strong><br/>在PHP5.4中将这个字段定义成const char* arKey类型了。</p></blockquote><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">上图来源于<a href="http://gsm56.com/?p=124" style="box-sizing: border-box; color: rgb(51, 202, 187); text-decoration-line: none; background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;">网络</a>。</p><ul style="box-sizing: border-box; margin-bottom: 16px; padding: 0px 0px 0px 2em; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;" class=" list-paddingleft-2"><li><p>Bucket结构体维护了两个双向链表,pNext和pLast指针分别指向本槽位所在的链表的关系。</p></li><li><p>而pListNext和pListLast指针指向的则是整个哈希表所有的数据之间的链接关系。<br/>HashTable结构体中的pListHead和pListTail则维护整个哈希表的头元素指针和最后一个元素的指针。</p></li></ul><blockquote style="box-sizing: border-box; margin: 0px 0px 16px; border-left: 4px solid rgb(238, 238, 238); font-size: 14px; padding: 0px 15px 0px 20px; color: rgb(102, 102, 102); font-style: italic; font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;"><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 0px;"><strong style="box-sizing: border-box;">NOTE</strong><br/>PHP中数组的操作函数非常多,例如:array_shift()和array_pop()函数,分别从数组的头部和尾部弹出元素。<br/>哈希表中保存了头部和尾部指针,这样在执行这些操作时就能在常数时间内找到目标。<br/>PHP中还有一些使用的相对不那么多的数组操作函数:next(),prev()等的循环中,<br/>哈希表的另外一个指针就能发挥作用了:pInternalPointer,这个用于保存当前哈希表内部的指针。<br/>这在循环时就非常有用。</p></blockquote><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">如图中左下角的假设,假设依次插入了Bucket1,Bucket2,Bucket3三个元素:</p><ol style="box-sizing: border-box; margin-bottom: 16px; padding: 0px 0px 0px 2em; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;" class=" list-paddingleft-2"><li><p>插入Bucket1时,哈希表为空,经过哈希后定位到索引为1的槽位。此时的1槽位只有一个元素Bucket1。<br/>其中Bucket1的pData或者pDataPtr指向的是Bucket1所存储的数据。此时由于没有链接关系。pNext,<br/>pLast,pListNext,pListLast指针均为空。同时在HashTable结构体中也保存了整个哈希表的第一个元素指针,<br/>和最后一个元素指针,此时HashTable的pListHead和pListTail指针均指向Bucket1。</p></li><li><p>插入Bucket2时,由于Bucket2的key和Bucket1的key出现冲突,此时将Bucket2放在双链表的前面。<br/>由于Bucket2后插入并置于链表的前端,此时Bucket2.pNext指向Bucket1,由于Bucket2后插入。<br/>Bucket1.pListNext指向Bucket2,这时Bucket2就是哈希表的最后一个元素,这是HashTable.pListTail指向Bucket2。</p></li><li><p>插入Bucket3,该key没有哈希到槽位1,这时Bucket2.pListNext指向Bucket3,因为Bucket3后插入。<br/>同时HashTable.pListTail改为指向Bucket3。</p></li></ol><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">简单来说就是哈希表的Bucket结构维护了哈希表中插入元素的先后顺序,哈希表结构维护了整个哈希表的头和尾。<br/>在操作哈希表的过程中始终保持预算之间的关系。</p><h3 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 16px; font-weight: 300; line-height: 1.43; font-size: 1.5em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; color: rgb(77, 82, 89); text-wrap: wrap;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a>哈希表的操作接口</h3><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">和上一节类似,将简单介绍PHP哈希表的操作接口实现。提供了如下几类操作接口:</p><ul style="box-sizing: border-box; margin-bottom: 16px; padding: 0px 0px 0px 2em; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;" class=" list-paddingleft-2"><li><p>初始化操作,例如zend_hash_init()函数,用于初始化哈希表接口,分配空间等。</p></li><li><p>查找,插入,删除和更新操作接口,这是比较常规的操作。</p></li><li><p>迭代和循环,这类的接口用于循环对哈希表进行操作。</p></li><li><p>复制,排序,倒置和销毁等操作。</p></li></ul><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">本小节选取其中的插入操作进行介绍。<br/>在PHP中不管是对数组的添加操作(zend_hash_add),还是对数组的更新操作(zend_hash_update),<br/>其最终都是调用_zend_hash_add_or_update函数完成,这在面向对象编程中相当于两个公有方法和一个公共的私有方法的结构,<br/>以实现一定程度上的代码复用。</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">[c] ZEND_API int _zend_hash_add_or_update(HashTable *ht, const char *arKey, uint nKeyLength, void *pData, uint nDataSize, void **pDest, int flag ZEND_FILE_LINE_DC) { //...省略变量初始化和nKeyLength <=0 的异常处理 h = zend_inline_hash_func(arKey, nKeyLength); nIndex = h & ht->nTableMask; p = ht->arBuckets[nIndex]; while (p != NULL) { if ((p->h == h) && (p->nKeyLength == nKeyLength)) { if (!memcmp(p->arKey, arKey, nKeyLength)) { // 更新操作 if (flag & HASH_ADD) { return FAILURE; } HANDLE_BLOCK_INTERRUPTIONS(); //..省略debug输出 if (ht->pDestructor) { ht->pDestructor(p->pData); } UPDATE_DATA(ht, p, pData, nDataSize); if (pDest) { *pDest = p->pData; } HANDLE_UNBLOCK_INTERRUPTIONS(); return SUCCESS; } } p = p->pNext; } p = (Bucket *) pemalloc(sizeof(Bucket) - 1 + nKeyLength, ht->persistent); if (!p) { return FAILURE; } memcpy(p->arKey, arKey, nKeyLength); p->nKeyLength = nKeyLength; INIT_DATA(ht, p, pData, nDataSize); p->h = h; CONNECT_TO_BUCKET_DLLIST(p, ht->arBuckets[nIndex]); //Bucket双向链表操作 if (pDest) { *pDest = p->pData; } HANDLE_BLOCK_INTERRUPTIONS(); CONNECT_TO_GLOBAL_DLLIST(p, ht); // 将新的Bucket元素添加到数组的链接表的最后面 ht->arBuckets[nIndex] = p; HANDLE_UNBLOCK_INTERRUPTIONS(); ht->nNumOfElements++; ZEND_HASH_IF_FULL_DO_RESIZE(ht); /* 如果此时数组的容量满了,则对其进行扩容。*/ return SUCCESS; }</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">整个写入或更新的操作流程如下:</p><ol style="box-sizing: border-box; margin-bottom: 16px; padding: 0px 0px 0px 2em; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;" class=" list-paddingleft-2"><li><p>生成hash值,通过与nTableMask执行与操作,获取在arBuckets数组中的Bucket。</p></li><li><p>如果Bucket中已经存在元素,则遍历整个Bucket,查找是否存在相同的key值元素,如果有并且是update调用,则执行update数据操作。</p></li><li><p>创建新的Bucket元素,初始化数据,并将新元素添加到当前hash值对应的Bucket链表的最前面(CONNECT_TO_BUCKET_DLLIST)。</p></li><li><p>将新的Bucket元素添加到数组的链接表的最后面(CONNECT_TO_GLOBAL_DLLIST)。</p></li><li><p>将元素个数加1,如果此时数组的容量满了,则对其进行扩容。这里的判断是依据nNumOfElements和nTableSize的大小。<br/>如果nNumOfElements > nTableSize则会调用zend_hash_do_resize以2X的方式扩容(nTableSize << 1)。</p></li></ol><p><br/></p>
<p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">PHP中使用最为频繁的数据类型非字符串和数组莫属,PHP比较容易上手也得益于非常灵活的数组类型。<br/>在开始详细介绍这些数据类型之前有必要介绍一下哈希表(HashTable)。 哈希表是PHP实现中尤为关键的数据结构。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">哈希表在实践中使用的非常广泛,例如编译器通常会维护的一个符号表来保存标记,很多高级语言中也显式的支持哈希表。<br/>哈希表通常提供查找(Search),插入(Insert),删除(Delete)等操作,这些操作在最坏的情况下和链表的性能一样为O(n)。<br/>不过通常并不会这么坏,合理设计的哈希算法能有效的避免这类情况,通常哈希表的这些操作时间复杂度为O(1)。<br/>这也是它被钟爱的原因。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">正是因为哈希表在使用上的便利性及效率上的表现,目前大部分动态语言的实现中都使用了哈希表。</p><h2 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 1em; font-weight: 300; line-height: 1.225; font-size: 1.75em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; padding-bottom: 0.5em; border-bottom: 1px solid rgb(238, 238, 238); color: rgb(77, 82, 89); text-wrap: wrap;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a>基本概念</h2><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">为了方便读者阅读后面的内容,这里列举一下HashTable实现中出现的基本概念。<br/>哈希表是一种通过哈希函数,将特定的键映射到特定值的一种数据结构,它维护键和值之间一一对应关系。</p><ul style="box-sizing: border-box; margin-bottom: 16px; padding: 0px 0px 0px 2em; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;" class=" list-paddingleft-2"><li><p>键(key):用于操作数据的标示,例如PHP数组中的索引,或者字符串键等等。</p></li><li><p>槽(slot/bucket):哈希表中用于保存数据的一个单元,也就是数据真正存放的容器。</p></li><li><p>哈希函数(hash function):将key映射(map)到数据应该存放的slot所在位置的函数。</p></li><li><p>哈希冲突(hash collision):哈希函数将两个不同的key映射到同一个索引的情况。</p></li></ul><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">哈希表可以理解为数组的扩展或者关联数组,数组使用数字下标来寻址,如果关键字(key)的范围较小且是数字的话,<br/>我们可以直接使用数组来完成哈希表,而如果关键字范围太大,如果直接使用数组我们需要为所有可能的key申请空间。<br/>很多情况下这是不现实的。即使空间足够,空间利用率也会很低,这并不理想。同时键也可能并不是数字,<br/>在PHP中尤为如此,所以人们使用一种映射函数(哈希函数)来将key映射到特定的域中:</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">h(key) -> index</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">通过合理设计的哈希函数,我们就能将key映射到合适的范围,因为我们的key空间可以很大(例如字符串key),<br/>在映射到一个较小的空间中时可能会出现两个不同的key映射被到同一个index上的情况,<br/>这就是我们所说的出现了冲突。 目前解决hash冲突的方法主要有两种:链接法和开放寻址法。</p><h2 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 1em; font-weight: 300; line-height: 1.225; font-size: 1.75em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; padding-bottom: 0.5em; border-bottom: 1px solid rgb(238, 238, 238); color: rgb(77, 82, 89); text-wrap: wrap;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a>冲突解决</h2><h3 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 16px; font-weight: 300; line-height: 1.43; font-size: 1.5em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; color: rgb(77, 82, 89); text-wrap: wrap;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a>链接法</h3><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">链接法通过使用一个链表来保存slot值的方式来解决冲突,也就是当不同的key映射到一个槽中的时候使用链表来保存这些值。<br/>所以使用链接法是在最坏的情况下,也就是所有的key都映射到同一个槽中了,这样哈希表就退化成了一个链表,<br/>这样的话操作链表的时间复杂度则成了O(n),这样哈希表的性能优势就没有了,<br/>所以选择一个合适的哈希函数是最为关键的。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">由于目前大部分的编程语言的哈希表实现都是开源的,大部分语言的哈希算法都是公开的算法,<br/>虽然目前的哈希算法都能良好的将key进行比较均匀的分布,而这个假使的前提是key是随机的,正是由于算法的确定性,<br/>这就导致了别有用心的黑客能利用已知算法的可确定性来构造一些特殊的key,让这些key都映射到<br/>同一个槽位导致哈希表退化成单链表,导致程序的性能急剧下降,从而造成一些应用的吞吐能力急剧下降,<br/>尤其是对于高并发的应用影响很大,通过大量类似的请求可以让服务器遭受DoS(服务拒绝攻击),<br/>这个问题一直就存在着,只是最近才被各个语言重视起来。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">哈希冲突攻击利用的哈希表最根本的弱点是:<strong style="box-sizing: border-box;">开源算法和哈希实现的确定性以及可预测性</strong>,<br/>这样攻击者才可以利用特殊构造的key来进行攻击。要解决这个问题的方法则是让攻击者无法轻易构造<br/>能够进行攻击的key序列。</p><blockquote style="box-sizing: border-box; margin: 0px 0px 16px; border-left: 4px solid rgb(238, 238, 238); font-size: 14px; padding: 0px 15px 0px 20px; color: rgb(102, 102, 102); font-style: italic; font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;"><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 0px;"><strong style="box-sizing: border-box;">NOTE</strong><br/>在笔者编写这节内容的时候PHP语言也采取了相应的措施来防止这类的攻击,PHP采用的是一种<br/>治标不治本的做法: <a href="http://cn2.php.net/manual/en/info.configuration.php#ini.max-input-vars" style="box-sizing: border-box; color: rgb(51, 202, 187); text-decoration-line: none; background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;">限制用户提交数据字段数量</a><br/>这样可以避免大部分的攻击,不过应用程序通常会有很多的数据输入方式,比如,SOAP,REST等等,<br/>比如很多应用都会接受用户传入的JSON字符串,在执行json_decode()的时候也可能会遭受攻击。<br/>所以最根本的解决方法是让哈希表的碰撞key序列无法轻易的构造,目前PHP中还没有引入不增加额外的复杂性情况下的完美解决方案。</p></blockquote><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">目前PHP中HashTable的哈希冲突解决方法就是链接法。</p><h3 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 16px; font-weight: 300; line-height: 1.43; font-size: 1.5em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; color: rgb(77, 82, 89); text-wrap: wrap;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a>开放寻址法</h3><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">通常还有另外一种解决冲突的方法:开放寻址法。使用开放寻址法是槽本身直接存放数据,<br/>在插入数据时如果key所映射到的索引已经有数据了,这说明发生了冲突,这是会寻找下一个槽,<br/>如果该槽也被占用了则继续寻找下一个槽,直到寻找到没有被占用的槽,在查找时也使用同样的策略来进行。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">由于开放寻址法处理冲突的时候占用的是其他槽位的空间,这可能会导致后续的key在插入的时候更加容易出现<br/>哈希冲突,所以采用开放寻址法的哈希表的装载因子不能太高,否则容易出现性能下降。</p><blockquote style="box-sizing: border-box; margin: 0px 0px 16px; border-left: 4px solid rgb(238, 238, 238); font-size: 14px; padding: 0px 15px 0px 20px; color: rgb(102, 102, 102); font-style: italic; font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;"><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 0px;"><strong style="box-sizing: border-box;">NOTE</strong><br/><em style="box-sizing: border-box;">装载因子</em>是哈希表保存的元素数量和哈希表容量的比,通常采用链接法解决冲突的哈希表的装载<br/>因子最好不要大于1,而采用开放寻址法的哈希表最好不要大于0.5。</p></blockquote><h2 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 1em; font-weight: 300; line-height: 1.225; font-size: 1.75em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; padding-bottom: 0.5em; border-bottom: 1px solid rgb(238, 238, 238); color: rgb(77, 82, 89); text-wrap: wrap;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a>哈希表的实现</h2><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">在了解到哈希表的原理之后要实现一个哈希表也很容易,主要需要完成的工作只有三点:</p><ol style="box-sizing: border-box; margin-bottom: 16px; padding: 0px 0px 0px 2em; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;" class=" list-paddingleft-2"><li><p>实现哈希函数</p></li><li><p>冲突的解决</p></li><li><p>操作接口的实现</p></li></ol><h3 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 16px; font-weight: 300; line-height: 1.43; font-size: 1.5em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; color: rgb(77, 82, 89); text-wrap: wrap;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a>数据结构</h3><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">首先我们需要一个容器来保存我们的哈希表,哈希表需要保存的内容主要是保存进来的的数据,<br/>同时为了方便的得知哈希表中存储的元素个数,需要保存一个大小字段,<br/>第二个需要的就是保存数据的容器了。作为实例,下面将实现一个简易的哈希表。基本的数据结构主要有两个,<br/>一个用于保存哈希表本身,另外一个就是用于实际保存数据的单链表了,定义如下:</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">[c] typedef struct _Bucket { char *key; void *value; struct _Bucket *next; } Bucket; typedef struct _HashTable { int size; int elem_num; Bucket** buckets; } HashTable;</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">上面的定义和PHP中的实现类似,为了便于理解裁剪了大部分无关的细节,在本节中为了简化,<br/>key的数据类型为字符串,而存储的数据类型可以为任意类型。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">Bucket结构体是一个单链表,这是为了解决多个key哈希冲突的问题,也就是前面所提到的的链接法。<br/>当多个key映射到同一个index的时候将冲突的元素链接起来。</p><h3 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 16px; font-weight: 300; line-height: 1.43; font-size: 1.5em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; color: rgb(77, 82, 89); text-wrap: wrap;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a>哈希函数实现</h3><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">哈希函数需要尽可能的将不同的key映射到不同的槽(slot或者bucket)中,首先我们采用一种最为简单的哈希算法实现:<br/>将key字符串的所有字符加起来,然后以结果对哈希表的大小取模,这样索引就能落在数组索引的范围之内了。</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">[c] static int hash_str(char *key) { int hash = 0; char *cur = key; while(*cur != '\0') { hash += *cur; ++cur; } return hash; } // 使用这个宏来求得key在哈希表中的索引 #define HASH_INDEX(ht, key) (hash_str((key)) % (ht)->size)</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">这个哈希算法比较简单,它的效果并不好,在实际场景下不会使用这种哈希算法,<br/>例如PHP中使用的是称为<a href="http://blog.csdn.net/zuiaituantuan/archive/2010/12/06/6057586.aspx" style="box-sizing: border-box; color: rgb(51, 202, 187); text-decoration-line: none; background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;">DJBX33A</a>算法,<br/><a href="http://blog.minidx.com/2008/01/27/446.html" style="box-sizing: border-box; color: rgb(51, 202, 187); text-decoration-line: none; background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;">这里</a>列举了Mysql,OpenSSL等开源软件使用的哈希算法,<br/>有兴趣的读者可以前往参考。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">有兴趣的读者可以运行本小节实现的哈希表实现,在输出日志中将看到很多的哈希冲突,<br/>这是本例中使用的哈希算法过于简单造成的.</p><h3 style="box-sizing: border-box; margin-top: 0.3em; margin-bottom: 16px; font-weight: 300; line-height: 1.43; font-size: 1.5em; font-family: Raleway, 微軟正黑體, "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: 0.5px; position: relative; color: rgb(77, 82, 89); text-wrap: wrap;"><a class="reference-link" style="box-sizing: border-box; color: rgb(51, 202, 187); background-image: initial; background-position: initial; background-size: initial; background-repeat: initial; background-attachment: initial; background-origin: initial; background-clip: initial; transition: all 0.3s linear 0s; outline: none !important;"></a>操作接口的实现</h3><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">为了操作哈希表,实现了如下几个操作接口函数:</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">[c] int hash_init(HashTable *ht); // 初始化哈希表 int hash_lookup(HashTable *ht, char *key, void **result); // 根据key查找内容 int hash_insert(HashTable *ht, char *key, void *value); // 将内容插入到哈希表中 int hash_remove(HashTable *ht, char *key); // 删除key所指向的内容 int hash_destroy(HashTable *ht);</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">下面以初始化、插入和获取操作函数为例:</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">[c] int hash_init(HashTable *ht) { ht->size = HASH_TABLE_INIT_SIZE; ht->elem_num = 0; ht->buckets = (Bucket **)calloc(ht->size, sizeof(Bucket *)); if(ht->buckets == NULL) return FAILED; LOG_MSG("[init]\tsize: %i\n", ht->size); return SUCCESS; }</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">初始化的主要工作是为哈希表申请存储空间,函数中使用calloc函数的目的是确保<br/>数据存储的槽位都初始化为0,以便后续在插入和查找时确认该槽位是否被占用。</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">[c] int hash_insert(HashTable *ht, char *key, void *value) { // check if we need to resize the hashtable resize_hash_table_if_needed(ht); int index = HASH_INDEX(ht, key); Bucket *org_bucket = ht->buckets[index]; Bucket *tmp_bucket = org_bucket; // check if the key exits already while(tmp_bucket) { if(strcmp(key, tmp_bucket->key) == 0) { LOG_MSG("[update]\tkey: %s\n", key); tmp_bucket->value = value; return SUCCESS; } tmp_bucket = tmp_bucket->next; } Bucket *bucket = (Bucket *)malloc(sizeof(Bucket)); bucket->key = key; bucket->value = value; bucket->next = NULL; ht->elem_num += 1; if(org_bucket != NULL) { LOG_MSG("[collision]\tindex:%d key:%s\n", index, key); bucket->next = org_bucket; } ht->buckets[index]= bucket; LOG_MSG("[insert]\tindex:%d key:%s\tht(num:%d)\n", index, key, ht->elem_num); return SUCCESS; }</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">上面这个哈希表的插入操作比较简单,简单的以key做哈希,找到元素应该存储的位置,并检查该位置是否已经有了内容,<br/>如果发生碰撞则将新元素链接到原有元素链表头部。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">由于在插入过程中可能会导致哈希表的元素个数比较多,如果超过了哈希表的容量,<br/>则说明肯定会出现碰撞,出现碰撞则会导致哈希表的性能下降,为此如果出现元素容量达到容量则需要进行扩容。<br/>由于所有的key都进行了哈希,扩容后哈希表不能简单的扩容,而需要重新将原有已插入的预算插入到新的容器中。</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">[c] static void resize_hash_table_if_needed(HashTable *ht) { if(ht->size - ht->elem_num < 1) { hash_resize(ht); } } static int hash_resize(HashTable *ht) { // double the size int org_size = ht->size; ht->size = ht->size * 2; ht->elem_num = 0; LOG_MSG("[resize]\torg size: %i\tnew size: %i\n", org_size, ht->size); Bucket **buckets = (Bucket **)calloc(ht->size, sizeof(Bucket *)); Bucket **org_buckets = ht->buckets; ht->buckets = buckets; int i = 0; for(i=0; i < org_size; ++i) { Bucket *cur = org_buckets[i]; Bucket *tmp; while(cur) { // rehash: insert again hash_insert(ht, cur->key, cur->value); // free the org bucket, but not the element tmp = cur; cur = cur->next; free(tmp); } } free(org_buckets); LOG_MSG("[resize] done\n"); return SUCCESS; }</pre><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">哈希表的扩容首先申请一块新的内存,大小为原来的2倍,然后重新将元素插入到哈希表中,<br/>读者会发现扩容的操作的代价为O(n),不过这个问题不大,因为只有在到达哈希表容量的时候才会进行。</p><p style="box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap;">在查找时也使用插入同样的策略,找到元素所在的位置,如果存在元素,<br/>则将该链表的所有元素的key和要查找的key依次对比, 直到找到一致的元素,否则说明该值没有匹配的内容。</p><pre class="prettyprint linenums prettyprinted" style="box-sizing: border-box; font-variant-numeric: normal; font-variant-east-asian: normal; font-variant-alternates: normal; font-kerning: auto; font-optical-sizing: auto; font-feature-settings: normal; font-variation-settings: normal; font-variant-position: normal; font-stretch: normal; font-size: 13.6px; line-height: 1.6; font-family: "YaHei Consolas Hybrid", Consolas, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Helvetica, monospace, monospace; margin-top: 0px; margin-bottom: 16px; overflow: auto; color: rgb(47, 111, 159); background-color: rgb(246, 246, 246); border: 1px solid rgb(238, 238, 238); padding: 10px; border-radius: 3px; overflow-wrap: break-word; text-wrap: wrap;">[c] int hash_lookup(HashTable *ht, char *key, void **result) { int index = HASH_INDEX(ht, key); Bucket *bucket = ht->buckets[index]; if(bucket == NULL) goto failed; while(bucket) { if(strcmp(bucket->key, key) == 0) { LOG_MSG("[lookup]\t found %s\tindex:%i value: %p\n", key, index, bucket->value); *result = bucket->value; return SUCCESS; } bucket = bucket->next; } failed: LOG_MSG("[lookup]\t key:%s\tfailed\t\n", key); return FAILED; }</pre><p style="box-sizing: border-box; margin-top: 0px; color: rgb(77, 82, 89); font-family: "Microsoft YaHei", Helvetica, "Meiryo UI", "Malgun Gothic", "Segoe UI", "Trebuchet MS", Monaco, monospace, Tahoma, STXihei, 华文细黑, STHeiti, "Helvetica Neue", "Droid Sans", "wenquanyi micro hei", FreeSans, Arimo, Arial, SimSun, 宋体, Heiti, 黑体, sans-serif; text-wrap: wrap; margin-bottom: 0px !important;">PHP中数组是基于哈希表实现的,依次给数组添加元素时,元素之间是有先后顺序的,<br/>而这里的哈希表在物理位置上显然是接近平均分布的,这样是无法根据插入的先后顺序获取到这些元素的,<br/>在PHP的实现中Bucket结构体还维护了另一个指针字段来维护元素之间的关系。<br/>具体内容在后一小节PHP中的HashTable中进行详细说明。上面的例子就是PHP中实现的一个精简版。</p><p><br/></p>