2020-4-11ali晚面

strlen函數與sizeof的區別

  • 表面上都可求字符串的長度,
  • 但二者卻存在着許多不同之處及本質區別。

  • strlen 是一個函數,計算指定字符串str長度,不包括結束字符
size_t strlen(char const* str);
  • 所以需要進行一次函數調用,調用示例如下面的代碼所示:
char sArr[] = "ILOVEC";
/*用strlen()求長度*/
printf("sArr的長度=%d\n", strlen(sArr));
  • 運行結果爲 6(因爲不包括結束字符 null)。
  • 注意的是,函數 strlen 返回的是一個類型爲 size_t 的值,從而有可能讓程序導致意想不到的結果,如下面的示例代碼所示:
/*判斷一*/
if(strlen(x)>= strlen(y))
{
}
/*判斷二*/
if(strlen(x)- strlen(y)>= 0)
{
}
  • 判斷表達式一沒什麼問題,程序也能夠完全按照預想的那樣工作;但判斷表達式二的結果就不一樣了,它將永遠是真,爲什麼?
  • 因爲函數 strlen 的返回結果是 size_t (即無符號整型),size_t 類型絕不可能是負的。因此,語句“if(strlen(x)-strlen(y)>=0)”將永遠爲真。

  • 就算表達式中同時包含了有符號整數和無符號整數,還是有可能產生意想不到的結果,如下面的代碼:
/*判斷一*/
if(strlen(x)>= 5)
{
}
/*判斷二*/
if(strlen(x)- 5>=0)
{
}
  • 顯然,判斷表達式二的結果還是永遠是真,其原因與上面相同。

  • sizeof 是一個單目運算符,不是一個函數。
  • 參數可以是數組、指針、類型、對象、函數等
char sArr[] = "ILOVEC";
/*用sizeof求長度*/
printf("sArr的長度=%d\n", sizeof(sArr));
  • 運行結果爲 7(因爲它包括結束字符 null)。
  • 對 sizeof 而言,因爲緩衝區已經用已知字符串進行初始化,其長度固定,所以sizeof 在編譯時計算緩衝區的長度。
  • 由於在編譯時計算,sizeof 不能用來返回動態分配的內存空間的大小。

參考鏈接

  • http://c.biancheng.net/view/342.html

C語言中sizeof和strlen的區別與聯繫

  • strlen:統計字符串中字符的個數
  • sizeof:統計對象所佔的單元(字節)的個數,
    • 一般以8位二進制作爲一個存儲單元,
    • 所以字節數一般等於存儲單元的個數。

  • 返回值
    • 整數
    • 整數
  • 參數
    • 類型、數組、指針‘函數
    • 數組
  • 是否包含“\0”
    • 包含
    • 不包含(以“\0結束”)
  • 本質
    • 運算符
    • 函數
  • 計算時間
    • 編譯
    • 運行
  • 一般用途
    • 統計存儲單元個數
    • 統計字符串中字符的個數,包括空格

  • sizeof用法注意:
  • 用於測定類型所佔存儲單元時,類型必須用sizeof(類型)
  • 用於數組時,表示數組所佔的存儲空間的大小,可以不用(),
    • sizeof(name)=sizeof name,name爲數組


#include <stdio.h>
#include <string.h>
#define PRAISE "What a super marvelous name!"
 
int main(void)
{
   char name[40];
   printf("What's your name?\n");
   scanf("%s",name);
   printf("Hello,%s.%s\n",name,PRAISE);
   printf("Your name of %d letters occupies %d memory cells.\n",strlen(name),sizeof(name));
   printf("The phrase of praise has %d letters",strlen(PRAISE));
   printf("and occupies %d cells.\n",sizeof(PRAISE));
   
   return 0;
}

int sum(int ar[], int n)
{
   int i;
   int total = 0;
   
   for(i=0;i<n;i++)
   {
    total += ar[i];
   }
   printf("The size of ar is %zd bytes.\n",sizeof ar);
 
   return total;
}
  • sizeof後面的對象如果是實參數組名,
    • 結果爲該數組的存儲空間,
    • 但sizeof如果爲一個指向數組首元素的指針,
      • 則對於4字節地址的計算機系統,指針的大小爲4字節
  • 如以上的sum函數,ar是一個指向數組的首元素的指針,
    • 所以該函數輸出的結果爲4。

悲觀鎖與樂觀鎖

  • 悲觀鎖
  • 每次去拿數據的時候都認爲別人會修改,所以每次在拿數據的時候都會上鎖,這樣別人想拿這個數據就會block直到它拿到鎖。
  • 關係型數據庫裏邊就用到了很多這種鎖機制,
    • 如行鎖,表鎖等,讀鎖,寫鎖等,都是在做操作之前先上鎖。
    • 它指的是對數據被外界(包括本系統當前的其他事務,以及來自外部系統的事務處理)修改持保守態度,因此,在整個數據處理過程中,將數據處於鎖定狀態。
    • 悲觀鎖的實現,往往依靠數據庫提供的鎖機制(也只有數據庫層提供的鎖機制才能真正保證數據訪問的排他性,否則,即使在本系統中實現了加鎖機制,也無法保證外部系統不會修改數據)。

  • 樂觀鎖
  • 每次去拿數據的時候都認爲別人不會修改,所以不會上鎖,但在更新的時候會判斷一下在此期間別人有沒有去更新這個數據,可以使用版本號等機制。
  • 適用於多讀的應用類型,這樣可以提高吞吐量,像數據庫如果提供類似於write_condition機制的其實都是提供的樂觀鎖。

  • 兩種鎖各有優缺點,
  • 樂觀鎖適用於寫比較少的情況下,即衝突真的很少發生的時候,這樣可以省去了鎖的開銷,加大了系統的整個吞吐量。
  • 但如果經常產生衝突,上層應用會不斷的進行retry,這樣反倒是降低了性能,所以這種情況下用悲觀鎖就比較合適。

在這裏插入圖片描述

參考鏈接

  • https://blog.csdn.net/coderDogg/article/details/85093741

最通俗易懂的樂觀鎖與悲觀鎖原理及實現

  • 樂觀鎖 總認爲不會產生併發問題,每次去取數據的時候總認爲不會有其他線程對數據修改,因此不上鎖,
  • 但在更新時會判斷其他線程在這之前有沒有對數據進行修改,一般會使用版本號機制或CAS操作實現。

  • version方式:一般是在數據表中加上一個數據版本號version字段,表示數據被修改的次數,當數據被修改時,version值會加一。
  • 當線程A要更新數據值時,在讀取數據的同時也會讀取version值,在提交更新時,若剛纔讀取到的version值爲當前數據庫中的version值相等時才更新,否則重試更新操作,直到更新成功。
update table set x=x+1, version=version+1 where id=#{id} and version=#{version};   

  • CAS操作方式:即compare and swap 或者 compare and set,涉及到三個操作數,數據所在的內存值,預期值,新值。
  • 當需要更新時,判斷當前內存值與之前取到的值是否相等,若相等,則用新值更新,若失敗則重試,一般情況下是一個自旋操作,即不斷的重試。

  • 悲觀鎖
  • 每次取數據時都認爲其他線程會修改,所以都會加鎖(讀鎖、寫鎖、行鎖等),
  • 當其他線程想要訪問數據時,都需要阻塞掛起。
  • 可以依靠數據庫實現,如行鎖、讀鎖和寫鎖等,都是在操作之前加鎖,
  • Java中,synchronized的思想也是悲觀鎖。

參考鏈接

  • https://blog.csdn.net/L_BestCoder/article/details/79298417

B樹

  • 從算法邏輯上來講,二叉查找樹的查找速度和比較次數都是最小的。
  • 但我們不得不考慮一個現實問題磁盤IO

  • 數據庫索引是存儲在磁盤上的,當數據量較大的時候,
  • 索引的大小可能有幾個G

  • 當利用索引查詢的時候,能把整個索引全部加載到內存嗎?
  • 不可能。
  • 只有逐一加載每一個磁盤頁,這裏的磁盤頁對應着索引樹的節點。

在這裏插入圖片描述

  • 如果用二又查找樹作爲索引結構
  • 設樹高4,查找的值是10,那麼流程如下

在這裏插入圖片描述

在這裏插入圖片描述

在這裏插入圖片描述

在這裏插入圖片描述

  • 磁盤I0的次數是4次,索引樹的高度也是4。

  • 最壞情況下,磁盤I0數等於樹高

  • 爲了減少磁盤I0次數,需把原本“瘦高”的樹結構變得“矮胖”。

  • 這就是B-樹的特徵之ー。

  • B樹是一種多路平衡查找樹,每一個節點最多包含k個孩子,k
    被稱爲B樹的階。

  • k取決於磁盤頁的大小。

在這裏插入圖片描述

  • 3階B-樹爲例,看看B樹具體結構。
  • 樹中的具體元素和剛纔的二叉查找樹是一樣的。

在這裏插入圖片描述

在這裏插入圖片描述

  • 查5吧!

在這裏插入圖片描述

在這裏插入圖片描述

在這裏插入圖片描述

  • B樹在查詢中的比較次數其實不比二又查找樹少,尤其當單一節點中的元素數量很多時。

  • 可是相比磁盤I0的速度,內存中的比較耗時幾乎可以略。

  • 所以只要樹的高度足夠低,I0次數足夠少,就可
    以提升查找性能。

  • 相比之下節點內部元素多一些也沒有關係,僅僅是多了幾內存交互,只
    要不超過磁盤頁的大小即可。

  • 這就是B-樹的優勢之ー。

這兒還有點沒寫

  • B-樹主要應用於文件系統及部分數據庫索引,如著名的非關係
    型數據庫 Mongodb。
  • 大部分關係型數據庫,如Mysq1用B+樹作爲索引。

參考鏈接

B+樹

  • B+樹是B-樹的一種変體,
  • 有着比B-樹更高的查詢性能。

在這裏插入圖片描述

在這裏插入圖片描述

在這裏插入圖片描述

  • 每個父節點的元素都出現在子節點中,是子節點的最大(或最小)
    元素。

在這裏插入圖片描述

  • 注意,根節點的最大元素(15),也就等同於整個B+樹的最大元素。
  • 以後無論插入刪除多少元素,始終要保持最大
    元素在根節點當中。

  • 至於葉子節點,由於父節點的元素都出現在子節點,因此所有葉子節點包含了全量元素信息。
  • 每一個葉子節點都帶有指向下ー個節點的指針,形成了一個
    有序鏈表。

在這裏插入圖片描述

  • 衛星數據,指的是索引元素所指向的數據記錄,比如數據庫中的某一行。
  • B樹中,無論中間節點還是葉子節點都帶有衛星數據。

在這裏插入圖片描述

  • B+樹當中,只有葉子節點帶衛星數據,其餘中間節點僅僅是索
    引,沒有任何數據關聯。

在這裏插入圖片描述

  • 數據庫的聚集索引中,葉子節點直接包含衛星數據。
  • 非聚集索引中,葉子節點帶有指向衛星數據的指針。

  • B+樹的好處主要體現在查詢性能上。
  • 通過單行查詢和範圍查詢來分析。
  • 單元素查詢的時候,B+樹會自頂向下逐層查找節點,最終找到匹配
    的葉子節點。
  • 比如要查找的是元素3

在這裏插入圖片描述

在這裏插入圖片描述

  • 兩點不同。

  • 首先,B樹的中間節點沒有衛星數據,所以同樣大小的
    磁盤頁可以容納更多的節點元素。

  • 意味着,數據量相同的情況下,B+樹比B樹更“矮胖”
    因此查詢時10次數也更少。

  • B+樹的查詢必須最終查找到葉子,B樹只要找到匹配元素
    ,無論匹配元素處於中間節點還
    是葉子節點。
  • B樹查找性能並不穩定(最
    好情況是隻查根節點,最壞情況是查
    到葉子節點)。而B樹的每一次查找
    都是穩定的。

參考鏈接

添加鏈接描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章