乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      理解c語言的sizeof

       mzsm 2015-05-23

      c語言有很多用起來需要特別注意的地方,我們(計算機學習微信公眾號:jsj_xx)以后會分析其中有使用價值的點。今天我們一起看看sizeof。c語言通過類型長度來達到指針的靈活性,我們覺得,某種意義上講,是sizeof功能成就了c指針。

      基礎知識

      首先,要知道sizeof 是關鍵字不是函數(shù)。也就是說,用到sizeof的地方其實在編譯階段就已經(jīng)計算出結果了,不是(也不能)在程序運行時動態(tài)地計算。換句話說,代碼中同一個sizeof的調用只能輸出一個值,而不可能有其它別的值(后文會看到,其實變長數(shù)組是顛覆了這個規(guī)律的)。再換句話說,就是反匯編就能看到sizeof調用的結果!

      其次,sizeof的計算結果跟編譯器的字節(jié)對齊方式有關。在默認情況下,c編譯器為每一個變量按其類型大小分配空間,這種默認方式是可以修改的,通過#pragma pack (n)或者__attribute((aligned (n)))

      最后,要知道對齊是個性能要求,不是必須的。我們這里僅考慮gcc編譯器,不考慮vc編譯器。比如ia32下,gcc對double、long long這樣的8字節(jié)變量,仍然是按4字節(jié)對齊,即使設置#pragma pack(8)的情況下。到了x64_64下,開始統(tǒng)一了,全部真的都是按照類型大小對齊的了。至于為何跟性能相關,我們以后講到cpu cache時再重新考慮這個問題,現(xiàn)在我們只要知道,因為地址總線放地址時肯定都是對齊的,所以不對齊的話會增加讀取周期就行了。

      下文全部以gcc+x86_64+結構體,來求解sizeof。

      計算原理

      對于每個數(shù)據(jù)類型都有一個align_size,其實就是類型大?。?/span>char1,short2,int4,long8double8,long double16。

      一個基本原則就是結構體里的每個成員都能按照自己類型的align_size去對齊。計算過程如下:


      很顯然,編譯器在計算過程中會自動填充空余部分。否則,想想一個結構體,內部空間都不是連續(xù)填充的,會讓編譯器分配空間時瘋掉的。

      代碼示例

      我們舉例看看,long double的類型大小是16字節(jié)。

      struct S{

      char a;

      long double b[1];

      };

      struct S sample;

      printf('%ld,%ld\n', sizeof(struct S), sizeof(sample.b));

      打印結果是“32,16”。因為結構體成員中最大的align_size(數(shù)組的話,看數(shù)組里面的單個元素的align_size)是16字節(jié),所以整個結構體就是16字節(jié)對齊的,這樣char也自動填充到16字節(jié)了。故,總的結構體大小就是32字節(jié)了。

      那么,將結構體的b[1]改為b[0]呢?也就是說,結構體內部引入零數(shù)組成員。

      此時結果是“16,0”。也就是說該結構體還16字節(jié)對齊,說明零數(shù)組元素還是參與了計算結構體的align_size,但是沒有占用空間。

      那么,還是維持為b[0],我們利用數(shù)組的特性來放點數(shù)據(jù)呢?改為:

      struct S *sample =(struct S*)malloc(sizeof(long double) * 1234 +sizeof(struct S));

      結果還是“16,0”。也就是說跟零數(shù)組的數(shù)據(jù)大小沒有關系,sizeof是感知不到的。

      那么,如果不用malloc,直接初始化賦值呢?因為我們知道字符串數(shù)組是可以這么做的,但是作為結構體成員呢?改為struct s sample={'1',{1,2,3}}這種方式:

      #include <stdio.h>

      struct S{

      char a;

      long double b[];

      };

      struct S sample={'1',{1,2,3}};

      main ()

      {

      printf('%Lf, %Lf, %Lf\n', sample.b[0], sample.b[1], sample.b[2]);

      //printf('%ld,%ld\n', sizeof(sample),sizeof(sample.b));//加上此句會編譯不過

      return 0;

      }

      初始化賦值是正常的,但是sizeof那句編譯不過:

      error: invalid application of ‘sizeof’ to incomplete type ‘long double[]’

      另外,對于b[]這種非零數(shù)組成員的結構體必須為全局靜態(tài)定義,否則編譯不過:

      error: non-static initialization of a flexible array member

      必須全局靜態(tài)存儲區(qū)間存放,才允許初始化賦值,是可以理解的,因為字符串數(shù)組(即使是定義在函數(shù)內部也能直接賦值。區(qū)別是char *str='123'是存放在全局靜態(tài)空間,char str[]='123'是存放在棧空間,并且后者的sizeof是能得出數(shù)組大小的)就是這么實現(xiàn)的。但是在加上sizeof(sample.b)就編譯不過,難道是gcc沒有做好?因為編譯期間是可以通過計算全局靜態(tài)區(qū)間的b[]大小算出來的。(為方便下文描述,這種形式我們稱為非零長數(shù)組

      那么,再看看改為b[0]會怎么樣。此時是零長數(shù)組了,所以不必放在全局靜態(tài)存儲空間了。

      #include <stdio.h>

      main ()

      {

      struct S{

      char a;

      long double b[0];

      };

      struct S sample={'1',{1,2,3}};

      printf('%Lf, %Lf, %Lf\n', sample.b[0], sample.b[1], sample.b[2]);

      printf('%ld,%ld\n', sizeof(sample),sizeof(sample.b));

      return 0;

      }

      b[0]表面上可以正常初始化賦值,但是實際沒有做處理的:編譯期間可以看到gcc的數(shù)組越界初始化警告:(此時會體會到-Werror的好處了)

      warning: excess elements in array initializer [enabled by default]

      warning: (near initialization for ‘sample.b’) [enabled by default]

      帶著警告的運行實現(xiàn)結果:

      0.000000, -0.000000, 0.000000

      16,0

      顯然,b[]中3個數(shù)值沒有正確處理。而sizeof的運算結果和上面的一致:還是無法感知數(shù)組大小。

      我們最后拋開結構體內成員限制(下文有原因),觀察下變長數(shù)組:元素個數(shù)是一個變量(跟上面的b[]形式的非零長數(shù)組是有區(qū)別的)。它實際就是在棧空間(既然變長,肯定不能是全局靜態(tài)空間)分配空間的。

      #include<stdio.h>

      intmain(void)

      {

      int i;

      scanf('%d', &i);

      char str[i];

      printf('sizeof(str[%d])=%d\n',i,sizeof(str));

      return 0;

      }

      如果輸入i為3,結果為“3,3”。對sizeof而言,變長數(shù)組是維持了數(shù)組的sizeof特性,畢竟以前普通數(shù)組就是這樣的效果!需要注意的是,變長數(shù)組只能定義在棧空間,不能全局靜態(tài)存儲空間或堆空間定義,而且由編譯器會盡量放到棧幀局部變量部分的最后。放在最后,是為了方便擴展,那如果塊范圍內有多個變長數(shù)組呢?多個就一個一個放到最后,逐個順序擴展(后面會有示例)。

      那如果變長數(shù)組作為入?yún)⒛??也是維持了數(shù)組的sizeof特性,還是和以前一樣:對入?yún)⒆鰏izeof,結果就是指針長度8。(此處僅考慮一維的情況,后文會考慮多維)

      涉及多維數(shù)組以及指針的內容,我們(計算機學習微信公眾號:jsj_xx)以后再講。。。

      關于變長數(shù)組的補充

      變長數(shù)組(variable length array,即VLA)是c99引入的,我們上面看過一個例子了?,F(xiàn)在再看一個:

      #include<limits.h>

      #include<stdio.h>

      intmain(int argc, char *argv[])

      {

      int i, n;

      n = atoi(argv[1]);

      char str[n+1];

      for (i = 0; i < n; i++) {

      str[i] = (char)('0' + i);

      }

      str[n]='\0';

      printf('str is %s\n', str);

      printf('str:%ld, %ld\n',sizeof(str[ULONG_MAX]),sizeof(str));

      return 0;

      }

      當入?yún)rgv[1]為“3”時,看結果:

      str is 012

      str:1, 4

      可見,對于變長數(shù)組,感知到了數(shù)組大??!另外,我們看到sizeof(str[ULONG_MAX])也是可以的,看來計算時是只看元素類型大小,不考慮數(shù)組下標范圍的!

      總結變長數(shù)組的特點,如下:

      1)必須在塊范圍內定義,不能在文件范圍內定義(static修飾)或全局引用(extern修飾),即保證只能是??臻g分配;

      2)變長數(shù)組不能作結構體或者聯(lián)合的成員,只能以獨立數(shù)組方式在;

      3)作用域為塊范圍,即其生存周期為所在塊入棧和出棧之間的時間內;

      第二個特點也是我們不去設置變長數(shù)組成員的結構體的原因。

      其實有個函數(shù)功能和變長數(shù)組類似,就是void alloca(size_t size)。它也是在棧中分配size字節(jié)大小的空間,當本棧幀退出時釋放空間。要注意的是,alloca()執(zhí)行失敗時,不會返回一個NULL指針,因為它本質就是一條調整棧頂指針的匯編指令,不能有豐富的返回值。匯編實現(xiàn)就導致了很差的移植性,所以,相對而言,這種場景更應該用變長數(shù)組。

      最后看下,變長數(shù)組在多維時的處理??创a:

      #include <stdio.h>

      int main( void )

      {

      int i;

      for(i=0;i<2;i++) {

      int m,n;

      scanf('%d %d', &m,&n);

      char a[m][n];

      char (*p)[n]=a;

      printf('%ld %ld', sizeof(a), sizeof(*p));

      }

      return 0;

      }

      輸入“3,4”時,結果是“12,4”;輸入“5,6”時,結果是“30,6”。

      可見,變長數(shù)組在各個維度上都很好地維持了普通數(shù)組的sizeof特性。另外,通過for循環(huán)也看到了塊范圍內變長數(shù)組可使用的重復性。

      對于二維變長數(shù)組作為函數(shù)參數(shù)也維持了普通二維數(shù)組的效果:

      #include <stdio.h>

      void func(int,int,long double a[*][*]);

      void func2(long double a[2][6]);

      int main(void)

      {

      int m=2, n=3;

      long double a[m][m*n];

      func(m,n,a);

      long double b[2][6];

      func2(b);

      return 0;

      }

      void func(int m,int n,long double x[m][m*n])

      {

      printf('%ld %ld %ld\n',sizeof(x), sizeof(x[0]), sizeof(x[0][0]));

      }

      void func2(long double x[2][6]){

      printf('%ld %ld %ld\n',sizeof(x), sizeof(x[0]), sizeof(x[0][0]));

      }

      輸出結果表明,兩種處理是一樣的:

      8 96 16

      8 96 16

      可見,變長多維數(shù)組維持普通多維數(shù)組一樣的效果:直接對入?yún)⑷izeof的結果是指針長度8,其它都能正常感知數(shù)組大小。為何數(shù)組做入?yún)r,sizeof就識別不出來,只能做指針大小處理呢?看懂下面這個例子,就明白了:

      #include <stdio.h>

      void func(long double x[][3]){

      printf('%ld %ld %ld\n',sizeof(x), sizeof(x[0]), sizeof(x[0][0]));

      }

      int main(void)

      {

      long double a[1][3];

      long double b[2][3];

      func(a);

      func(b);

      return 0;

      }

      沒錯,是gcc編譯器支持的這種數(shù)組第一維可以省略的參數(shù)形式的后遺癥。

      總結



        本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內容均由用戶發(fā)布,不代表本站觀點。請注意甄別內容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權內容,請點擊一鍵舉報。
        轉藏 分享 獻花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多