sizeof   字符串 strlen

1.以字符串形式出現的,編譯器都會爲該字符串自動添加一個0作爲結束符,如在代碼中寫

 "abc",那麼編譯器幫你存儲的是"abc\0"


2."abc"是常量嗎?答案是有時是,有時不是。


 不是常量的情況:"abc"作爲字符數組初始值的時候就不是,如

                 char str[] = "abc";

   因爲定義的是一個字符數組,所以就相當於定義了一些空間來存放"abc",而又因爲

   字符數組就是把字符一個一個地存放的,所以編譯器把這個語句解析爲

   char str[3] = {'a','b','c'};

                 又根據上面的總結1,所以char str[] = "abc";的最終結果是

   char str[4] = {'a','b','c','\0'};

   做一下擴展,如果char str[] = "abc";是在函數內部寫的話,那麼這裏

   的"abc\0"因爲不是常量,所以應該被放在棧上。


 是常量的情況:  把"abc"賦給一個字符指針變量時,如

                 char* ptr = "abc";

   因爲定義的是一個普通指針,並沒有定義空間來存放"abc",所以編譯器得幫我們

   找地方來放"abc",顯然,把這裏的"abc"當成常量並把它放到程序的常量區是編譯器

   最合適的選擇。所以儘管ptr的類型不是const char*,並且ptr[0] = 'x';也能編譯

   通過,但是執行ptr[0] = 'x';就會發生運行時異常,因爲這個語句試圖去修改程序

   常量區中的東西。

   記得哪本書中曾經說過char* ptr = "abc";這種寫法原來在c++標準中是不允許的,

   但是因爲這種寫法在c中實在是太多了,爲了兼容c,不允許也得允許。雖然允許,

   但是建議的寫法應該是const char* ptr = "abc";這樣如果後面寫ptr[0] = 'x'的

   話編譯器就不會讓它編譯通過,也就避免了上面說的運行時異常。

   又擴展一下,如果char* ptr = "abc";寫在函數體內,那麼雖然這裏的"abc\0"被

   放在常量區中,但是ptr本身只是一個普通的指針變量,所以ptr是被放在棧上的,

   只不過是它所指向的東西被放在常量區罷了。

3.數組的類型是由該數組所存放的東西的類型以及數組本身的大小決定的。

 如char s1[3]和char s2[4],s1的類型就是char[3],s2的類型就是char[4],

 也就是說盡管s1和s2都是字符數組,但兩者的類型卻是不同的。


4.字符串常量的類型可以理解爲相應字符常量數組的類型,

 如"abcdef"的類型就可以看成是const char[7]


5.sizeof是用來求類型的字節數的。如int a;那麼無論sizeof(int)或者是sizeof(a)都

 是等於4,因爲sizeof(a)其實就是sizeof(type of a)


6.對於函數參數列表中的以數組類型書寫的形式參數,編譯器把其解釋爲普通

 的指針類型,如對於void func(char sa[100],int ia[20],char *p)

 則sa的類型爲char*,ia的類型爲int*,p的類型爲char*

7.根據上面的總結,來實戰一下:

 對於char str[] = "abcdef";就有sizeof(str) == 7,因爲str的類型是char[7],

 也有sizeof("abcdef") == 7,因爲"abcdef"的類型是const char[7]。

 對於char *ptr = "abcdef";就有sizeof(ptr) == 4,因爲ptr的類型是char*。

 對於char str2[10] = "abcdef";就有sizeof(str2) == 10,因爲str2的類型是char[10]。

 對於void func(char sa[100],int ia[20],char *p);

 就有sizeof(sa) == sizeof(ia) == sizeof(p) == 4,

 因爲sa的類型是char*,ia的類型是int*,p的類型是char*。


接下來通過分析實例來區分sizeof與strlen


例1:

char* ss = "0123456789";

sizeof(ss) 結果 4 ===》ss是指向字符串常量的字符指針

sizeof(*ss) 結果 1 ===》*ss是第一個字符,字符佔1個字節


char ss[] = "0123456789";

sizeof(ss) 結果 11 ===》ss是數組,計算到\0位置,因此是10+1

sizeof(*ss) 結果 1 ===》*ss是第一個字符


char ss[100] = "0123456789";

sizeof(ss) 結果是100 ===》ss表示在內存中的大小 100×1

strlen(ss) 結果是10 ===》strlen是個函數內部實現是用一個循環計算到\0爲止之前


int ss[100] = "0123456789";

sizeof(ss) 結果 400 ===》ss表示再內存中的大小 100×4,32位機種int×××數據佔4個字節

strlen(ss) 錯誤 ===》strlen的參數只能是char* 且必須是以''\0''結尾的


char q[]="abc";

char p[]="a\n";

sizeof(q),sizeof(p),strlen(q),strlen(p);

結果是 4 3 3 2    

例2:

class X

{

int i;

int j;

char k;

};

X x;

cout<第三個例子:char szPath[MAX_PATH]

  如果在函數內這樣定義,那麼sizeof(szPath)將會是MAX_PATH,但是將szPath作爲虛參聲明時(void fun(char szPath[MAX_PATH])),sizeof(szPath)卻會是4(指針大小)




深入理解Sizeof與strlen的本質


簡單的講,sizeof  是求變量或類型的存儲長度。而strlen  是統計字符串中字符的個數,不包括終止符‘\0’。sizeof 是求得實體(就是"()"裏面包含的變量或者類型字)的字節單位長度。按照類型來判斷;strlen 是求得字符的長度,累加搜索,直到 \0截至爲止。




sizeof(xxx)是個保留字,用來求分配給xxx的內存空間大小的,舉例來說sizeof(int)就是求分配給int型變量的內存大小。strlen(const char*)這是個字符串相關函數,其中,參數必須是個字符指針(指向字符串的指針)或字符串,而返回值就是這個指針指向的字符串的長度,字符串結束必須以'\0'爲標記,但'\0'不計入字符串長度。

Sizeof與strlen的區別及聯繫


1.sizeof操作符的結果類型是size_t,它在頭文件中typedef爲unsigned int類型。

該類型保證能容納實現所建立的最大對象的字節大小。  

2.sizeof是算符,strlen是函數。


3.sizeof可以用類型做參數,strlen只能用char*做參數,且必須是以''\0''結尾的。


4.數組做sizeof的參數不退化,傳遞給strlen就退化爲指針了。


5.大部分編譯程序在編譯的時候就把sizeof計算過了是類型或是變量的長度這就是sizeof(x)可以用來定義數組維數的原因

char str[20]="0123456789";//str是編譯期大小已經固定的數組

int a=strlen(str); //a=10;//strlen()在運行起確定

int b=sizeof(str); //而b=20;//sizeof()在編譯期確定


6.strlen的結果要在運行的時候才能計算出來,是用來計算字符串的實際長度,不是類型佔內存的大小。


7.sizeof後如果是類型必須加括弧,如果是變量名可以不加括弧。這是因爲sizeof是個操作符不是個函數。

char c;

sizeof c;//變量名可以不加括弧


8.當適用了於一個結構類型時或變量, sizeof 返回實際的大小,

當適用一靜態地空間數組, sizeof 歸還全部數組的尺寸。

sizeof 操作符不能返回動態地被分派了的數組或外部的數組的尺寸


9.數組作爲參數傳給函數時傳的是指針而不是數組,傳遞的是數組的首地址,

如:

fun(char [8])

fun(char [])

都等價於 fun(char *)

在C++裏參數傳遞數組永遠都是傳遞指向數組首元素的指針,編譯器不知道數組的大小

如果想在函數內知道數組的大小, 需要這樣做:

進入函數後用memcpy拷貝出來,長度由另一個形參傳進去

fun(unsiged char *p1, int len)

{

   unsigned char* buf = new unsigned char[len+1]

   memcpy(buf, p1, len);

}


10.計算結構變量的大小就必須討論數據對齊問題。爲了CPU存取的速度最快(這同CPU取數操作有關,詳細的介紹可以參考一些計算機原理方面的書),C++在處理數據時經常把結構變量中的成員的大小按照4或8的倍數計算,這就叫數據對齊(data alignment)。這樣做可能會浪費一些內存,但理論上速度快了。當然這樣的設置會在讀寫一些別的應用程序生成的數據文件或交換數據時帶來不便。MS VC++中的對齊設定,有時候sizeof得到的與實際不等。一般在VC++中加上#pragma pack(n)的設定即可.或者如果要按字節存儲,而不進行數據對齊,可以在Options對話框中修改Advanced compiler頁中的Data alignment爲按字節對齊。

11.sizeof操作符不能用於函數類型,不完全類型或位字段。不完全類型指具有未知存儲大小的數據類型,如未知存儲大小的數組類型、未知內容的結構或聯合類型、void類型等。如sizeof(max)若此時變量max定義爲int max(),sizeof(char_v) 若此時char_v定義爲char char_v [MAX]且MAX未知,sizeof(void)都不是正確形式。




我們能常在用到 sizeof 和 strlen 的時候,通常是計算字符串數組的長度

看了上面的詳細解釋,發現兩者的使用還是有區別的,從這個例子可以看得很清楚:


har str[11]="0123456789";//注意這裏str大小因該大於等於11,應考慮'\0'在內,否則編譯器會報錯

int a=strlen(str); //a=10; >>>> strlen 計算字符串的長度,以結束符 0x00 爲字符串結束。

int b=sizeof(str); //而b=11; >>>> sizeof 計算的則是分配的數組 str[11] 所佔的內存空間的大小,不受裏面存儲的內容改變。


上面是對靜態數組處理的結果,如果是對指針,結果就不一樣了


char* ss = "0123456789";

sizeof(ss) 結果 4 ===》ss是指向字符串常量的字符指針,sizeof 獲得的是一個指針的之所佔的空間,應該是長整型的,所以是4

sizeof(*ss) 結果 1 ===》*ss是第一個字符 其實就是獲得了字符串的第一位'0' 所佔的內存空間,是char類型的,佔了 1 位strlen(ss)= 10 >>>> 如果要獲得這個字符串的長度,則一定要使用 strlen


另外,下面的方法可以用於確定該靜態數組可以容納元素的個數:

int a[3]={1,2,3};


cout << sizeof a/sizeof ( typeid( a[0] ).name() );




個人補充實例:


對於字符串數組 char SS[7]="abcdef",其後自動添加'/0'表示結束符,該字符串數組在內存中佔用單元是以單個字符存取的,因此又可以表示爲 char SS[7]={'a','b','c','d','e','f','/0'}


結果:sizeof(ss)=7   strlen(ss[])=6




小結:Sizeof計算分配的字符串數組/數據類型/函數所佔的內存空間的大小,不受裏面存儲的內容實際大小而改變;strlen計算字符串的長度,以結束符0x00('/0')爲字符串結束。


例如:char ss3[100]="0123456789"


int a=sizeof(ss3) a值爲100,實際ss3數組中只有‘0’,‘1’,‘2’,‘3’,‘4’,‘5’,‘6’,‘7’,‘8’,‘9’,‘/0’共10個字符串,因此ss[10]='/0'


int b=strlen(ss3[]) b值爲10,這裏不計入字符串結束標誌'/0'




sizeof使用場合。


1.sizeof操作符的一個主要用途是與存儲分配和I/O系統那樣的例程進行通信。例如:    


void *malloc(size_t size), 


size_t fread(void * ptr,size_t size,size_t nmemb,FILE * stream)。

2.用它可以看看一類型的對象在內存中所佔的單元字節。 void * memset(void * s,int c,sizeof(s))


3.在動態分配一對象時,可以讓系統知道要分配多少內存。

4.便於一些類型的擴充,在windows中就有很多結構內型就有一個專用的字段是用來放該類型的字節大小。

5.由於操作數的字節數在實現時可能出現變化,建議在涉及到操作數字節大小時用sizeof來代替常量計算。

6.如果操作數是函數中的數組形參或函數類型的形參,sizeof給出其指針的大小。


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