下標 獲取字元_陣列下標1你見過嗎?
技術標籤:下標 獲取字元
作者:守望,Linux應用開發者,目前在公眾號【程式設計珠璣】 分享Linux/C/C++/資料結構與演算法/工具等原創技術文章和學習資源。
不知道你有沒有見過-1作為陣列下標的,我算是見到了。當然這一點在Python之類的語言中毫不稀奇。
下標-1的程式碼
這裡redis原始碼中的一部分:
sdssdsMakeRoomFor(sdss,size_taddlen){
void*sh,*newsh;
size_tavail=sdsavail(s);
size_tlen,newlen;
chartype,oldtype=s[-1]&SDS_TYPE_MASK;
inthdrlen;
//摘取部分程式碼
其中sds定義如下:
typedefchar*sds
我們忽略其中的邏輯,其實可以看到在sds中有很多使用-1作為下標的。那麼這裡到底有什麼含義?又有什麼好處呢?別急!
陣列下標訪問
我們都知道,陣列下標可以以O(1)複雜度訪問一個數組的元素:
intarr[]={1,2,3,4,5};
printf("%d\n",arr[2]);
上面的示例程式碼中,就是訪問了陣列的第三個元素,實際上,作用等價於;
printf("%d\n",*(arr+2));
這一點,我已經在《C語言入坑指南-陣列之謎》中解釋過了。arr相當於int*型別指標,+2,意味著指標向前移動了sizeof(int) * 2,即8位元組的位置,最終指向數字3。關於指標的算術運算,也可以參考《void*是怎樣的存在》。
再看下面:
int*pArr=arr+2;
printf("%d\n",pArr[-1]);//printf("%d\n",*(pArr-1))
這裡也很好理解,無非就是pArr指向數字3,然後又使用下標-1,訪問了前一個位置,最終自然指向了數字2,列印的也是2。
到目前為止,一切都還合情合理。
但是,我們別忘了,陣列越界是一件很可怕事情。比如,你試試:
printf("%d\n",arr[16]);
printf("%d\n",arr[-1]);
運氣不好的時候,程式不會掛死,只是打印出一些莫名其妙的值,運氣好的時候,程式掛死。(有人可能會問,為什麼程式不會掛死,運氣還算好呢?因為不掛死的時候,一些隱藏的問題更讓人抓狂)。
到這裡我們明白了,為了陣列下標訪問不越界,通常下標範圍是0~size-1,其中size是陣列元素個數。
那麼問題來了,redis的原始碼中為什麼要用-1作為下標呢?
巧妙的-1
實際上,sds其中的一個結構(8bit範圍長度)定義是這樣的:
struct__attribute__((__packed__))sdshdr8{
uint8_tlen;/*used*/
uint8_talloc;/*excludingtheheaderandnullterminator*/
unsignedcharflags;/*3lsboftype,5unusedbits*/
charbuf[];
};
其中
__attribute__((__packed__))
是取消位元組對齊,關於位元組對齊,可以參考《理一理位元組對齊的那些事》,本文不再贅述。
而在每一次建立新的sds結構的時候,返回的指標,都是指向buf這裡從原始碼的sdsnewlen函式中很容易看出:
即:
1位元組 | 1位元組 | 1位元組 | |
---|---|---|---|
len | alloc | flags | buf |
↑ |
所以我們看到前面這樣的程式碼也就不足為奇了:
oldtype=s[-1]&SDS_TYPE_MASK;
這裡的-1相當於將指標指向了flags欄位:
1位元組 | 1位元組 | 1位元組 | |
---|---|---|---|
len | alloc | flags | buf |
↑ |
相信到這裡你應該能理解-1的作用了。
那麼為什麼要這麼做呢?想象一下,使用strlen是不是直接可以計算sds字串的長度了呢?
總結
一般來說-1這樣的用法是不太建議的,或者說,在使用下標訪問陣列時,必須確保不越界。
預告
redis中為什麼不用普通的char*儲存字串,而要使用所謂的簡單動態字串?背後究竟隱藏著怎樣的祕密?請看下文詳細分解。
●編號750,輸入編號直達本文
●輸入m獲取文章目錄
C語言與C++程式設計分享C/C++技術文章