日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

musl中strlen源码实现和分析

發布時間:2023/11/16 windows 48 coder
生活随笔 收集整理的這篇文章主要介紹了 musl中strlen源码实现和分析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

最近在學習《C 和指針》的第 6 章指針部分,在 6.12 章節看到了 strlen 函數的實現,聯想到最近有在看 musl 的源碼,于是就把 musl 中 strlen 的源碼認真地分析了一下,發現源碼中有一些有意思的點,特地寫這篇文章跟各位感興趣的小伙伴分享一下。本文重點對 musl 的 strlen 源碼中的一些有意思的點進行分析,希望能對你理解 strlen 函數實現有所幫助。

1. strlen 源碼

要計算一個字符串的長度,可以通過以下的代碼實現:

/*
** 計算一個字符串的長度
*/
#include <stdlib.h>
size_t strlen(char *string)
{
	int length = 0;
	// 依次訪問字符串的內容,計數字符數,直到遇見 NUL 終止符。
	while(*string++ != '\0')
		length++;
		
	return length;
}

在指針到達字符串的末尾的 NUL 字符之前,while 語句中 *string++ 表達式的值一直為真。它同時增加指針的值,用于下一次測試。這個表達式甚至可以正確地處理空字符串。
需要注意的是,如果調用這個函數時,傳入的參數是 NULL 指針,那么 while 語句中的間接訪問就會失敗。這是因為在 C 語言中 NULL 是一個宏定義,表示一個空指針常量,其值為 0。當我們試圖對 NULL 指針進行解引用操作時,實際上是在嘗試訪問一個不存在的內存地址。解引用操作是指通過指針訪問其指向的內存位置的值。當我們對一個 NULL 指針進行解引用時,由于 NULL 指針并不指向任何有效的內存地址,因此無法獲取到有效的值。這會導致程序在運行時發生錯誤,通常會觸發一個異常,導致程序崩潰。
那么問題來了,既然 strlen 傳入的參數有可能為 NULL,那么有沒有必要在 strlen 函數的實現中加入指針為空的判斷呢?答案是不需要,因為檢查指針變量是否為 NULL,應該在指針變量創建之后就進行是否為 NULL 的判斷,這樣只需要檢查一次就行了,后邊再用指針變量的時候直接使用就行,不需要再對指針變量是否為 NULL 進行判斷。

2. musl 中 strlen 的源碼

上節中的示例代碼很簡單也很好理解,但是 musl 中的代碼就沒那么好理解了,musl 代碼的 strlen 實現如下:

#include <string.h>
#include <stdint.h>
#include <limits.h>

#define ALIGN (sizeof(size_t))
#define ONES ((size_t)-1/UCHAR_MAX)
#define HIGHS (ONES * (UCHAR_MAX/2+1))
#define HASZERO(x) ((x)-ONES & ~(x) & HIGHS)

size_t strlen(const char *s)
{
	const char *a = s;
#ifdef __GNUC__
	typedef size_t __attribute__((__may_alias__)) word;
	const word *w;
	for (; (uintptr_t)s % ALIGN; s++) if (!*s) return s-a;
	for (w = (const void *)s; !HASZERO(*w); w++);
	s = (const void *)w;
#endif
	for (; *s; s++);
	return s-a;
}

看到這里,建議各位小伙伴花點兒時間仔細閱讀一下上面的源碼,并試著回答以下幾個問題:

  • 在 64 位操作系統中,宏 ALIGNONESHIGHS 的值分別為多少?
  • #ifdef __GNUC__#endif 中的代碼的作用是什么?只寫 #endif 后面的代碼一樣可以實現字符串長度的計算,為什么還要寫 #ifdef __GNUC__#endif 中的代碼呢?
  • HASZERO(*w) 的作用是什么呢?試著舉例說明 HASZERO(*w) 檢測 word 中是否存在著 0 的過程。

2.1 問題 1 分析

在 64 位操作系統中,宏 ALIGN 的值為8,ONES 的值為0x0101010101010101,HIGHS 的值為 0x8080808080808080。
計算過程如下:

  • ALIGN 的計算:sizeof(size_t) 的結果為8,所以 ALIGN 的值為 8。
  • ONES 的計算:UCHAR_MAX 的值為255,所以 (size_t)-1/UCHAR_MAX 的結果為 0xffffffffffffffff/0xff=0x0101010101010101
  • HIGHS 的計算:ONES 的值為0x0101010101010101,所以 UCHAR_MAX/2+1 的結果為128,所以 ONES * (UCHAR_MAX/2+1) 的結果為 0x8080808080808080。

2.2 問題 2 分析

#ifdef __GNUC__#endif 中的代碼的作用是檢查是否使用的是 GCC 編譯器。如果是 GCC 編譯器,那么就會執行 #ifdef __GNUC__#endif 之間的代碼塊。這段代碼塊中定義了一些類型別名和變量,用于優化字符串長度的計算。如果不是 GCC 編譯器,那么這段代碼塊會被忽略。
盡管只寫 #endif 后面的代碼也可以實現字符串長度的計算,但是通過使用 GCC 特定的優化技巧,可以提高計算效率。因此,為了在 GCC 編譯器下獲得更好的性能,才需要寫 #ifdef __GNUC__#endif 中的代碼。
這樣設計的好處是,在一次位運算操作中,就可以快速檢查一個 64 位無符號整數中是否存在字節值為 0 的字節,而不需要使用循環或其他復雜的邏輯。這種設計可以提高代碼的效率和性能。

2.3 問題 3 分析

HASZERO(*w) 的作用是檢查一個 size_t 類型的值中是否存在字節值為0的字節。它的計算過程是將該值減去 ONES,然后與其取反進行按位與操作,再與 HIGHS 進行按位與操作。如果最終的結果為非 0,說明該值中存在字節值為 0 的字節。
看到這里后,各位小伙伴可能對 HASZERO(*w) 的具體計算過程仍然不太理解,如果對這塊不太感興趣的話,后邊的內容可以簡單看一下,對這部分有點印象就行,等后邊有用到的時候再花點時間研究一下就好。這里列舉兩個例子來說明具體的計算過程,需要說明的是,在 64 位操作系統中 sizeof(size_t)=8*w 為一個 8 個字節的數,例 1 中 *w 的值為 0x1101110111011101,各個字節都非 0;例 2 中 *w 的值為 0x1100110111011101,第二個字節為 0。

2.3.1 例 1 的計算過程

例 1,在 64 位操作系統中 ONES 的值為0x0101010101010101,HIGHS 的值為0x8080808080808080,*w 的值為 0x1101110111011101。那么,HASZERO(*w) 的計算過程如下:

  1. *w 減去 ONES:0x1101110111011101 - 0x0101010101010101 = 0x1000100010001000
  2. 將結果與其取反進行按位與操作:0x1000100010001000 & ~0x1101110111011101 = 0x1000100010001000 & 0xEEFEEEFEEEFEEEFE=0x0000000000000000
  3. 將結果與 HIGHS 進行按位與操作:0x0000000000000000 & 0x8080808080808080 = 0x0000000000000000
    由于最終的結果為0,說明 *w 中不存在字節值為 0 的字節。

2.3.2 例 2 的計算過程

例 2,在 64 位操作系統中 ONESHIGHS 的值同上例一樣,本例中 *w 的值為 0x1100110111011101。那么,HASZERO(*w) 的計算過程如下:

  1. *w 減去 ONES:0x1100110111011101 - 0x0101010101010101 = 0x0FFF100010001000
  2. 將結果與其取反進行按位與操作:0x0fff100010001000 & ~0x1100110111011101 = 0x0FFF100010001000 & 0xEEFFEEFEEEFEEEFE=0x0EFF000000000000
  3. 將結果與 HIGHS 進行按位與操作:0x0000000000000000 & 0x8080808080808080 = 0x0080000000000000
    由于最終的結果不為 0,說明 *w 中存在字節值為 0 的字節。

2.3.3 對上述兩個例子的總結

通過上面的兩個例子可以發現,利用 HASZERO(*w) 可以有效地確定字 *w 的 8個字節(64 位操作系統中,1字=8字節)中是否存在值為 0 的字節。
上面的兩個例子很好地介紹了 HASZERO(*w) 的計算過程,但是多字節的計算過程較為復雜,接下來我們以單字節為例,來分析 HASZERO(*w) 可以檢測字中是否存在 0 的原因:
首先,我們知道一個無符號字節的表示范圍為:0~255,可以把這個范圍分為如下幾段,并計算 HASZERO(*w) 的結果(本例中以 8 位操作系統為例),由于只涉及單字節的計算過程,所以計算過程較為簡單,并且很容易發現規律:

范圍 HASZERO(*w)
0 0x80
1~127 0x00
128 0x00
129~255 0x00
分析上面的表格可以發現,只有在 *w=0 的時候,HASZERO(*w) 的結果才為非 0。

3. 總結

上面對 musl 中 strlen 的源碼實現啰嗦了這么多,那么 HASZERO(*w) 對我們的日常編碼有什么用呢?一個容易想到的用處是,可以參考 strlen 的源碼寫出判斷 int 類型或者 long 類型的數據中是否存在著字節值為 0 的字節。當然,如果用循環+移位的方法也可以很方便的實現,但在追求效率的情況下,可以考慮利用 strlen 源碼中的 HASZERO(*w) 來實現。當然知道有這么個用處只是一方面,我想更重要的是通過本文的分析,你可以對 strlen 的設計有更深的了解,對一些細節也有了更深的認識。
希望本文對你有所幫助!

總結

以上是生活随笔為你收集整理的musl中strlen源码实现和分析的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。