strlen的神奇實現

https://blog.delphij.net/2012/04/freebsd-strlen3.html


與 Pascal 等語言不同,C 的字符串并不保存串的長度,而是在字符串末尾以 nul 字符('\0')來表示字符串結束。這個設計決策是上世紀 60 年代作出的,有都市傳說是為了省幾個字節的空間,不過我個人認為也可能是因為匯編里面到處都是判斷是否碰到了 0 的操作。不管怎么說,這個設計令 strlen 變成了一個 O(n) 的操作。

早期的 BSD Unix 采用的 strlen 是非常簡單的循環比較每一個字符是不是 nul。1993年,J.T. Conklin 為 i386 系統撰寫了一個匯編的版本,這個版本的核心用的是 REP SCASB,實際上和 C 版本的算法是一樣的(不知道為什么 C 編譯器不能寫出同樣的代碼)。

為了配合 x86_64 平臺,后來又有了一個新的匯編版本,這個版本的核心算法是按字匹配,找到包含 nul 字符的字之后,再在其中用原始的算法找到 nul 字符。

我在 2009 年根據這個 x86_64 版本的匯編的思路重寫了一個 C 的版本,并在 2010 年做了一次最終的變動,形成了目前的版本。這個版本的大致流程如下:

  • 判斷第一個字中是否存在 nul,如果存在,掃描查找其中有效位置的 nul 字符;
  • 按字掃描剩余的字符串;如果發現字中帶 nul,則掃描并返回其位置。

實現細節

整個算法中,比較難理解的是判斷字中是否帶 nul 字符。具體的方法是計算兩個中間變量:

  1. a = (x - 0x01010101)
  2. b = (~x & 0x80808080)
  3. 計算 a & b == 0

這里的 0x01010101 和 0x80808080 可以進一步擴展。第一步,如果每個字節都 <= 0x7f,只要那個字節不是 0,做差必然得到一個 < 0x80 的結果(換言之,最高位是0);如果有字節 >= 0x81,做差必然得到一個 > 0x80 的結果。對于等于 0x80 的情況,我們會得到 0x7f,但這并不重要。

注意到,此處,任何一個字節的最高 bit 是 1 的話,則必然是前面兩種情況之一:要么這個字節是 0,要么它 >= 0x81。如果不考慮后一種情況,我們直接把結果 & 0x80808080 即可;然而,由于需要考慮后一種情況,我們接著計算 ~x & 0x80808080。若某一字節 >= 0x80,則對應的結果將是那個位置上的一個 0x80。

將兩個結果做邏輯與,若結果非 0 則說明至少有一個字節是 nul。

這里說起來的過程很復雜,但事實上計算機計算這些要比一個一個去判斷每個字節是否為 0 要快。這里有幾方面的原因:

  • 按字長做操作,令 CPU 無需模擬按字節為長度操作的情況,后者是比較耗時的;
  • 前兩步操作(分別計算a, b)可以并發執行;
  • 最后一步操作可以直接在兩個寄存器之間進行,且是速度較快的與運算;

在實際的實現中,還有一些其他的技巧。

第一個技巧是,從第一個小節就開始用字長的操作。一般來說,內存分配器在分配內存時是以字邊界開始的,因此,通常 strlen() 的操作的指針都是對齊的。不過,即使不是,這個指針往前退到第一個整字位置(例如字長=8,指針 0x9,則退回 0x8)開始的一個整字必然是在同一個內存頁上,因此這個訪問不會越界。如果在這個整字中有 nul 字符,我們只需從指針開始處掃描到第一個整字結尾的地方即可知道是不是真的找到了字符串的末尾。

由于整個字已經在處理器緩存中,后續的循環也不會太慢。

第二個技巧與此類似,我們一直都用整字的操作。如果字的起始地址在內存頁中,則終止地址也必然在同一個內存頁中。這個訪問同樣也不會發生意外越界(盡管在分配內存時可能出現類似分配了 4 個字節,但訪問了 8 個字節的情況)。換言之,如果程序原先不會發生越界異常,則現在也不會。

這個版本的 strlen 源代碼可以在?這里?找到。


轉載于:https://www.cnblogs.com/marryZhan/archive/2012/05/23/2797292.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/379682.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/379682.shtml
英文地址,請注明出處:http://en.pswp.cn/news/379682.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

python求和_Python程序查找特殊求和系列的解決方案

python求和We are going to design a special sum series function which has following characteristics: 我們將設計一個特殊的求和系列函數&#xff0c;該函數具有以下特征&#xff1a; f(0) 0f(1) 1f(2) 1f(3) 0f(x) f(x-1) f(x-3)Python solution of the above sum…

linux內核設計與實現---進程管理

進程管理1 進程描述符及任務結構分配進程描述符進程描述符的存放進程狀態設置當前進程狀態進程上下文進程家族樹2 進程創建寫時拷貝fork()vfork()3 線程在Linux中的實現內核線程4 進程終結刪除進程描述符孤兒進程造成的進退微谷5 小結進程的另一個名字叫做任務&#xff08;task…

JS錯誤代碼解釋大全+VBS錯誤代碼解釋大全

JScript 運行時錯誤 JScript 運行時錯誤是指當 JScript 腳本試圖執行一個系統不能運行的動作時導致的錯誤。當正在運行腳本、計算變量表達式、或者正在動態分配內存時出現 JScript 運行時錯誤時。 錯誤號 描述 5029 數組長度必須為一有限正整數 5030 必須賦給數組長度一個有…

生日蠟燭(藍橋杯)

某君從某年開始每年都舉辦一次生日party&#xff0c;并且每次都要吹熄與年齡相同根數的蠟燭。 現在算起來&#xff0c;他一共吹熄了236根蠟燭。 請問&#xff0c;他從多少歲開始過生日party的&#xff1f; 請填寫他開始過生日party的年齡數。 注意&#xff1a;你提交的應該是…

python日歷模塊_Python日歷模塊| firstweekday()方法與示例

python日歷模塊Python calendar.firstweekday()方法 (Python calendar.firstweekday() Method) firstweekday() method is an inbuilt method of the calendar module in Python. It works on simple text calendars and returns the current setting for the weekday to start…

php 處理 mysql to json, 前臺js處理

public function GetJson(){$query"select * from table";$result mysql_query($query);$rows array();while($row mysql_fetch_array($result)){$rows [] $row;}echo json_encode($rows); } js處理 $.get( "./bll.php", option,function(data ) {var j…

Linux內核設計與實現---進程調度

進程調度1 策略I/O消耗型和處理器消耗型的進程進程優先級時間片進程搶占2 Linux調度算法可執行隊列優先級數組重新計算時間片schedule()計算優先級和時間片睡眠和喚醒負載平衡程序3 搶占和上下文切換用戶搶占內核搶占4 實時5 與調度相關的系統調用與調度策略和優先級相關的系統…

ServletContext(核心內容)

什么是ServletContext對象 ServletContext代表是一個web應用的環境&#xff08;上下文&#xff09;對象&#xff0c;ServletContext對象 內部封裝是該web應用的信息&#xff0c;ServletContext對象一個web應用只有一個 一個web應用有多個servlet對象 ServletContext對象的生…

【轉載】[TC]飛船動畫例子--《C高級實用程序設計》

【聲明和備注】本例子屬于轉載來源于《C高級實用程序設計》&#xff08;王士元&#xff0c;清華大學出版社&#xff09;第11章&#xff0c;菜單設計與動畫技術&#xff0c;第11.5節&#xff0c;一個動畫例子。 本例講解的是在一個繁星背景下&#xff0c;一個由經緯線組成的藍色…

math.sqrt 有問題_JavaScript中帶有示例的Math.SQRT2屬性

math.sqrt 有問題JavaScript | Math.SQRT2屬性 (JavaScript | Math.SQRT2 Property) Math.SQRT2 is a property in math library of JavaScript that is used to find the value of square root of 2. It is generally used to solve problems related to circular figures. Ma…

Linux內核設計與實現---系統調用

系統調用1 API、POSIX和C庫2 系統調用系統調用號3 系統調用處理程序指定恰當的系統調用參數傳遞4 系統調用的實現參數驗證5 系統調用上下文綁定一個系統調用的最后步驟從用戶空間訪問系統調用為什么不通過系統調用的方式實現1 API、POSIX和C庫 API&#xff1a;應用編程接口。一…

內核編譯配置選項含義

Linux 2.6.19.x 內核編譯配置選項簡介 作者&#xff1a;金步國 版權聲明 本文作者是一位自由軟件愛好者&#xff0c;所以本文雖然不是軟件&#xff0c;但是本著 GPL 的精神發布。任何人都可以自由使用、轉載、復制和再分發&#xff0c;但必須保留作者署名&#xff0c;亦不得對聲…

js編碼處理(轉)

js編碼處理(轉) 1. 使用 JS 中的 encodeURIComponent 或 encodeURI 方法。 說明&#xff1a; encodeURIComponent(String) 對傳遞參數進行設置。不編碼字符有 71 個&#xff1a; ! &#xff0c; &#xff0c; ( &#xff0c; ) &#xff0c; * &#xff0c; - &#…

手動去設置HTTP響應行、響應頭、響應體

①手動去設置HTTP響應行中的狀態碼&#xff0c;這里用到了response的setStatus(int sc);這個方法 package com.itheima.line;import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpSer…

Java SecurityManager checkListen()方法與示例

SecurityManager類的checkListen()方法 (SecurityManager Class checkListen() method) checkListen() method is available in java.lang package. checkListen()方法在java.lang包中可用。 checkListen() method invokes checkPermission with the given SocketPermission(&q…

基本的二分查找、尋找第一個和最后一個數的二分查找

二分查找1 二分查找的框架2 尋找一個數&#xff08;基本的二分搜索&#xff09;3 尋找左側邊界的二分搜索4 尋找右側邊界的二分查找5 合并二分查找場景&#xff1a;有序數組尋找一個數、尋找左側邊界&#xff08;有序數組第一個等目標數的下標&#xff09;、尋找右側邊界&#…

PostgreSQL 中的遞歸查詢 與oracle 的比較

PostgreSQL 中的遞歸查詢&#xff0c;2種方法&#xff1a; 1、用with decursive WITH RECURSIVE d AS (SELECT d1.id,d1.parent_id,d1.caption FROM course_types d1 where d1.dr 0 and d1.idtypeId union ALL SELECT d2.id,d2.parent_id,d2.caption FROM course_types d2, d …

教你如何玩轉GitHub

使用GitHub ①目的&#xff1a;借助GitHub托管項目代碼 基本概念&#xff1a; ①倉庫(Repository)&#xff1a; 用來存放項目代碼&#xff0c;每個項目對應一個倉庫&#xff0c;多個開源項目對應多個倉庫 ②收藏(Star)&#xff1a; 收藏項目&#xff0c;方便下次查看 ③…

Java SecurityManager checkDelete()方法與示例

SecurityManager類的checkDelete()方法 (SecurityManager Class checkDelete() method) checkDelete() method is available in java.lang package. checkDelete()方法在java.lang包中可用。 checkDelete() method calls checkPermission with FilePermission(filename,"d…

jQuery中的treeview插件

jQuery做樹狀結構真的很簡單,下面做一個最簡單的示例: 在html文件中引用: <link rel"stylesheet" href"../jquery.treeview.css" /> <link rel"stylesheet" href"../red-treeview.css" /> <link rel"styles…