1. 程式人生 > 其它 >下標 獲取字元_陣列下標1你見過嗎?

下標 獲取字元_陣列下標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位元組
lenallocflagsbuf

所以我們看到前面這樣的程式碼也就不足為奇了:

oldtype=s[-1]&SDS_TYPE_MASK;

這裡的-1相當於將指標指向了flags欄位:

1位元組1位元組1位元組
lenallocflagsbuf

相信到這裡你應該能理解-1的作用了。

那麼為什麼要這麼做呢?想象一下,使用strlen是不是直接可以計算sds字串的長度了呢?

總結

一般來說-1這樣的用法是不太建議的,或者說,在使用下標訪問陣列時,必須確保不越界。

預告

redis中為什麼不用普通的char*儲存字串,而要使用所謂的簡單動態字串?背後究竟隱藏著怎樣的祕密?請看下文詳細分解。


●編號750,輸入編號直達本文

●輸入m獲取文章目錄

C語言與C++程式設計

4697b0a6343b6ea439ba6675a7aeb75d.png

分享C/C++技術文章