1. 程式人生 > >字元陣列,字元指標,Sizeof總結

字元陣列,字元指標,Sizeof總結

Sizeof與字串

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給出其指標的大小。