[C++]深入理解sizeof-使用規則及陷阱分析

說明:

sizeof在筆試面試的時候頻頻地出現,這也是對基礎的一個考查。關於sizeof的文章很多,但感覺大家都沒有好好總結下,本着“先行先贏”和“爲人民服務”的精神,查找引用參考了很多文章,在這裏總結一下,有錯誤或者遺漏的地方還得請高手多多指教,也不要因這這些問題誤導別人,希望以後大家在學習的過程中也能節省些時間。

概要

sizeof是C語言的一種單目操作符(但有人也不這麼以爲,認爲它是一種特殊的宏),如C語言的其他操作符++、--等。它並不是函數(這是必須的)。sizeof操作符以字節形式給出了其操作數的存儲大小。操作數可以是一個表達式或括在括號內的類型名。操作數的存儲大小由操作數的類型決定,簡單的說其作用就是返回一個對象或者類型所佔的內存字節數。

sizeof的使用方法

1、用於數據類型(包括自定義類型)

sizeof使用形式:sizeof(type) 
數據類型必須用括號括住。如sizeof(int)。

2、用於變量

sizeof使用形式:sizeof(var_name)或sizeof var_name

變量名可以不用括號括住。如sizeof (var_name),sizeof var_name等都是正確形式。帶括號的用法更普遍,大多數程序員採用這種形式。

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

sizeof深入理解及陷阱分析

數據類型的sizeof

(1)應用

  • 固有類型:

32位C++中的基本數據類型,也就char,short int(short),int,long int(long),float,double, long double 
大小分別是:1,2,4,4,4,8, 10。

  • 自定義類型:

typedef short WORD; 
typedef long DWORD;

(2)舉例及陷阱

  • e.g. 1.1
// 32位機上int長度爲4 
cout<<sizeof(int)<<endl;  
// == 操作符返回bool類型,相當於 cout<<sizeof(bool)<<endl; 
cout<<sizeof(1==2)<<endl;  

在編譯階段已經被翻譯爲:

cout<<4<<endl; 
cout<<1<<endl;
  • e.g. 1.2
int a = 0; 
cout<<sizeof(a=3)<<endl; 
cout<<a<<endl;

輸出是4,0。而不是期望中的4,3。

就在於sizeof在編譯階段處理的特性。由於sizeof不能被編譯成機器碼,所以sizeof作用範圍內,也就是()裏面的內容也不能被編譯,而是被替換成類型。=操作符返回左操作數的類型,所以a=3相當於int,而代碼也被替換爲:

int a = 0; 
cout<<4<<endl; 
cout<<a<<endl; 

所以,sizeof是不可能支持鏈式表達式的,這也是和一元操作符不一樣的地方。

結論:不要把sizeof當成函數,也不要看作一元操作符,把他當成一個特殊的編譯預處理。

  • e.g. 1.3
int i = 2; 
cout<<sizeof(i)<<endl; // sizeof(object)的用法,合理 
cout<<sizeof i<<endl; // sizeof object的用法,合理 
cout<<sizeof 2<<endl; // 2被解析成int類型的object, sizeof object的用法,合理 
cout<<sizeof(2)<<endl; // 2被解析成int類型的object, sizeof(object)的用法,合理 
cout<<sizeof(int)<<endl;// sizeof(typename)的用法,合理 
cout<<sizeof int<<endl; // 錯誤!對於操作符,一定要加()

可以看出,加()是永遠正確的選擇。

結論:不論sizeof要對誰取值,最好都加上()。

  • e.g. 1.4
cout<<sizeof(unsigned int) == sizeof(int)<<endl; // 相等,輸出 1

unsigned影響的只是最高位bit的意義,數據長度不會被改變的。

結論:unsigned不能影響sizeof的取值。

  • e.g. 1.5
typedef short WORD; 
typedef long DWORD; 
cout<<(sizeof(short) == sizeof(WORD))<<endl; // 相等,輸出1 
cout<<(sizeof(long) == sizeof(DWORD))<<endl; // 相等,輸出1

結論:自定義類型的sizeof取值等同於它的類型原形。

  • e.g. 1.6
cout << sizeof(2 + 3.14) << endl;

3.14的類型爲double,2也會被提升成double類型,所以等價於 sizeof( double );

所以最後的結果是:8

 

函數類型的sizeof

sizeof也可以對一個函數調用求值,其結果是函數返回類型的大小,函數並不會被調用,並且返回值不能是void(當然也就是沒有返回值)。

C99標準規定,函數、不能確定類型的表達式以及位域(bit-field)成員不能被計算sizeof值。

  • e.g. 2.1
int f1(){return 0;}; 
double f2(){return 0.0;} 
void f3(){} 
cout<<sizeof(f1())<<endl; // f1()返回值爲int,因此被認爲是int 
cout<<sizeof(f2())<<endl; // f2()返回值爲double,因此被認爲是double 
cout<<sizeof(f3())<<endl; // 錯誤!無法對void類型使用sizeof 
cout<<sizeof(f1)<<endl;  // 錯誤!無法對函數指針使用sizeof  

 

指針類型的sizeof

學過數據結構的你應該知道指針是一個很重要的概念,它記錄了另一個對象的地址。既然是來存放地址的,那麼它當然等於計算機內部地址總線的寬度。所以在32位計算機中,一個指針變量的返回值必定是4(注意結果是以字節爲單位),可以預計,在將來的64位系統中指針變量的sizeof結果爲8。

  • e.g. 3.1
char* pc = "abc";    
int* pi;    
string* ps;    
char** ppc = &pc;    
void (*pf)();// 函數指針  
   
sizeof( pc ); // 結果爲4    
sizeof( pi ); // 結果爲4    
sizeof( ps ); // 結果爲4    
sizeof( ppc ); // 結果爲4    
sizeof( pf );// 結果爲4 

可以看到,不管是什麼類型的指針,大小都是4的,因爲指針就是32位的物理地址。

結論:只要是指針,大小就是4。(64位機上要變成8也不一定)。

 

數組類型的sizeof

數組類型的sizeof常常就和char*, char[],這些東西在一起就會誤導人,這個到最後再討論,先從例子看起。

  • e.g. 4.1
char a[] = "abcdef"; 
int b[20] = {3, 4}; 
char c[2][3] = {"aa", "bb"}; 
cout<<sizeof(a)<<endl; // 7 
cout<<sizeof(b)<<endl; // 80 
cout<<sizeof(c)<<endl; // 6

數組a的大小在定義時未指定,編譯時給它分配的空間是按照初始化的值確定的,也就是7,注意:在最後還有一個“\0”也算一位。所以是6+1=7。

本人在VS2005上得到b的結果是80,可以看出,數組的大小就是他在編譯時被分配的空間,也就是各維數的乘積*數組元素的大小。

c是多維數組,佔用的空間大小是各維數的乘積,也就是6。

結論:數組的大小是各維數的乘積*數組元素的大小。

  • e.g. 4.2
int *d = new int[10]; 
cout<<sizeof(d)<<endl; // 4

d是我們常說的動態數組,但是他實質上還是一個指針,所以sizeof(d)的值是4。

  • e.g. 4.3
double* (*a)[3][6]; 
cout<<sizeof(a)<<endl;  // 4 
cout<<sizeof(*a)<<endl;  // 72 
cout<<sizeof(**a)<<endl; // 24 
cout<<sizeof(***a)<<endl; // 4 
cout<<sizeof(****a)<<endl; // 8

這個相對麻煩些,得先了解下數組指針和指針數組的知識。

a是一個很奇怪的定義,他表示一個指向 double*[3][6]類型數組的指針。既然是指針,所以sizeof(a)就是4。

既然a是執行double*[3][6]類型的指針,*a就表示一個double*[3][6]的多維數組類型,因此sizeof(*a)=3*6*sizeof(double*)=72。

同樣的,**a表示一個double*[6]類型的數組,所以sizeof(**a)=6*sizeof(double*)=24。

***a就表示其中的一個元素,也就是double*了,所以sizeof(***a)=4。

至於****a,就是一個double了,所以sizeof(****a)=sizeof(double)=8。

 

向函數傳遞數組的sizeof

首先,我們要明確數組作爲參數被傳給函數時傳的是指針而不是數組,傳遞的是數組的首地址。也可以說是它退化成了一個指針。

  • e.g. 5.1
#include <iostream> 
using namespace std; 
int Sum(int i[]) 
{ 
int sumofi = 0; 
for (int j = 0; j < sizeof(i)/sizeof(int); j++) //實際上,sizeof(i) = 4 
{ 
  sumofi += i[j]; 
} 
return sumofi; 
} 
int main() 
{ 
int allAges[6] = {21, 22, 22, 19, 34, 12}; 
cout<<Sum(allAges)<<endl; 
system("pause"); 
return 0; 
}

Sum的本意是用sizeof得到數組的大小,然後求和。但是實際上,傳入自函數Sum的,只是一個int 類型的指針,所以sizeof(i)=4,而不是24,所以會產生錯誤的結果。解決這個問題的方法使是用指針或者引用。

使用指針的情況:

int Sum(int (*i)[6]) 
{ 
int sumofi = 0; 
for (int j = 0; j < sizeof(*i)/sizeof(int); j++) //sizeof(*i) = 24 
{ 
  sumofi += (*i)[j]; 
} 
return sumofi; 
} 
int main() 
{ 
int allAges[] = {21, 22, 22, 19, 34, 12}; 
cout<<Sum(&allAges)<<endl; 
system("pause"); 
return 0; 
}

在這個Sum裏,i是一個指向i[6]類型的指針,注意,這裏不能用int Sum(int (*i)[])聲明函數,而是必須指明要傳入的數組的大小,不然sizeof(*i)無法計算。但是在這種情況下,再通過sizeof來計算數組大小已經沒有意義了,因爲此時大小是指定爲6的。

使用引用的情況和指針相似:

int Sum(int (&i)[6]) 
{ 
int sumofi = 0; 
for (int j = 0; j < sizeof(i)/sizeof(int); j++) 
{ 
  sumofi += i[j]; 
} 
return sumofi; 
} 
int main() 
{ 
int allAges[] = {21, 22, 22, 19, 34, 12}; 
cout<<Sum(allAges)<<endl; 
system("pause"); 
return 0; 
}

這種情況下sizeof的計算同樣無意義,所以用數組做參數,而且需要遍歷的時候,函數應該有一個參數來說明數組的大小,而數組的大小在數組定義的作用域內通過sizeof求值。

因此上面的函數正確形式應該是:

#include <iostream> 
using namespace std; 
int Sum(int *i, unsigned int n) 
{ 
int sumofi = 0; 
for (int j = 0; j < n; j++) 
{ 
  sumofi += i[j]; 
} 
return sumofi; 
} 
int main() 
{ 
int allAges[] = {21, 22, 22, 19, 34, 12}; 
cout<<Sum(i, sizeof(allAges)/sizeof(int))<<endl; 
system("pause"); 
return 0; 
}
結構體的sizeof

結構體的sizeof相對還是麻煩一些,寫了一篇專門的文章僅供參考:

鏈接:http://pppboy.blog.163.com/blog/static/30203796201082494026399/

類的sizeof

[提示]這個可以看看孫鑫的那個《深入淺出MFC》的第一章的那個C++基礎部分,就有這個類的大小的一個例子很不錯。

先從例子看起吧。

關於虛繼承也有個總結:

http://pppboy.blog.163.com/blog/static/30203796201041005953744/

  • e.g. 7.1
    //空類,相當於一個空結構體 
    class A 
    { 
    }; 
    //和結構體相同 
    class B  
    { 
        int a; 
        double b; 
    }; 
    //有一個非虛函數 
    class C 
    { 
        int a; 
        double b; 
         
        void f(){} 
    }; 
    //有1個虛函數 
    class D 
    { 
        int a; 
        double b; 
        virtual void vf(){} 
    }; 
     
    //有2個虛函數 
    class E 
    { 
        int a; 
        double b; 
        virtual void vf(){} 
        virtual void vf2(){} 
    }; 
    cout << "A: " << sizeof(A) << endl; 
    cout << "B: " <<  sizeof(B) << endl; 
    cout << "C: " <<  sizeof(C) << endl; 
     cout << "D: " <<  sizeof(D) << endl; 
     cout << "E: " <<  sizeof(E) << endl;

在8字節對齊的情況下結果爲:

A: 1 
B: 16 
C: 16 
D: 24 
E: 24 
請按任意鍵繼續. . .

可見:

(1)類的大小和結構體大小一樣

(2)非虛擬成員函數不佔大小

(3)如果有虛擬函數,則會多佔用4個字節(虛擬函數表)

  • e.g. 7.2
    class A 
    { 
        int a; 
        double b; 
        void f(){} 
        virtual void vf(){} 
    }; 
    class B :public A 
    { 
    }; 
    class C : public virtual A 
    { 
    }; 
    class D  
    { 
        char a; 
    }; 
    class E  
    { 
        char a; 
        virtual void vfd(){} 
    }; 
     
   
    class F1 : public A, public D 
    { 
    }; 
    class F2 : public A, public E 
    { 
    }; 
    class G1 : public virtual A, public virtual D 
    { 
    }; 
    class G2 : public virtual A, public virtual E 
    { 
    }; 
    cout << "A: " << sizeof(A) << endl; 
    cout << "B: " <<  sizeof(B) << endl; 
    cout << "C: " <<  sizeof(C) << endl; 
     cout << "D: " <<  sizeof(D) << endl; 
     cout << "E: " <<  sizeof(E) << endl; 
    cout << "F1: " <<  sizeof(F1) << endl; 
    cout << "F2: " <<  sizeof(F2) << endl; 
    cout << "G1: " <<  sizeof(G1) << endl; 
    cout << "G2: " <<  sizeof(G2) << endl;

在8字節對齊的情況下,結果爲:

A: 24 
B: 24 
C: 32 
D: 1 
E: 8 
F1: 32 
F2: 32 
G1: 33 
G2: 40 
請按任意鍵繼續. . .

結論:

(1)在繼承的時候,單一繼承和多重繼承的大小一樣,也就相當於子類有一個自己的虛函數表,這個虛函數表是自己重新定義的,相當於它把父親的虛函數表改成自己的,並且只有一個。

(2)在有虛繼承時,涉及虛指針的問題,也就是說,他會用父類的虛函數表,如果父類沒有,那麼他也就沒有這個父類的虛函數表,相當於他不具有自己獨立的虛函數表,而是用它父親的,有幾個父親有表,那麼它都可以隨時用它父親的,也算是他自己的大小。

  • e.g. 7.3

父類指針指向子類的問題

    class Base 
    { 
        int a; 
        virtual void vf(){}; 
    }; 
    class Derived : public Base 
    { 
        char a; 
    }; 
    Base* pBase = new Derived; 
    Derived* pDerived = new Derived; 
    cout << "Base:     " << sizeof(Base) << endl;//8 
    cout << "Derived:  " << sizeof(Derived) << endl;//12 
    cout << "pBase:    " << sizeof(pBase) << endl;//指針大小都爲4 
    cout << "*pBase:   " << sizeof(*pBase) << endl;//8 
    cout << "pDerived: " << sizeof(pDerived) << endl;//指針大小都爲4 
    cout << "*pDerived:" << sizeof(*pDerived) << endl;//12

//在8字節對齊時的結果爲:

Base:     8 
Derived:  12 
pBase:    4 
*pBase:   8 
pDerived: 4 
*pDerived:12 
請按任意鍵繼續. . .

結論:sizeof 操作符不能返回動態地被分派了的數組或外部的數組的尺寸。它返回的只是類型的大小(可以這麼理解)

 

字符串的sizeof和strlen

這個在實際的應用中是最容易忽悠人的,應該好好滴留意一下。

  • e.g. 8.1
char a[] = "abcdef"; 
char b[20] = "abcdef"; 
string s = "abcdef"; 
cout<<strlen(a)<<endl;  // 6,字符串長度 
cout<<sizeof(a)<<endl;  // 7,字符串容量 
cout<<strlen(b)<<endl;  // 6,字符串長度 
cout<<strlen(b)<<endl;  // 20,字符串容量 
cout<<sizeof(s)<<endl;  // 12, 這裏不代表字符串的長度,而是string類的大小 
cout<<strlen(s)<<endl;  // 錯誤!s不是一個字符指針。 
a[1] = '\0'; 
cout<<strlen(a)<<endl;  // 1 
cout<<sizeof(a)<<endl;  // 7,sizeof是恆定的

strlen是尋找從指定地址開始,到出現的第一個0之間的字符個數,他是在運行階段執行的

而sizeof是得到數據的大小,在這裏是得到字符串的容量。所以對同一個對象而言,sizeof的值是恆定的。

string是C++類型的字符串,他是一個類,所以sizeof(s)表示的並不是字符串的長度,而是類string的大小。strlen(s)根本就是錯誤的,因爲strlen的參數是一個字符指針,如果想用strlen得到s字符串的長度,應該使用sizeof(s.c_str()),因爲string的成員函數c_str()返回的是字符串的首地址。實際上,string類提供了自己的成員函數來得到字符串的容量和長度,分別是Capacity()和Length()。string封裝了常用了字符串操作,所以在C++開發過程中,最好使用string代替C類型的字符串。

 

下面引用VCbase裏的內容再探討一下。

(1)sizeof是算符,strlen是函數。

(2)sizeof可以用類型做參數,strlen只能用char*做參數,且必須是以''\0''結尾的。sizeof還可以用函數做參數

short f(); 
printf("%d\n", sizeof(f()));

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

char var[10]; 
    int test(char var[]) 
    { 
        return sizeof(var); 
    }

var[]等價於*var,已經退化成一個指針,所以大小是4.

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

char str[20]="0123456789"; 
int a=strlen(str); //a=10; 
int b=sizeof(str); //而b=20;

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

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

(7)當適用了於一個結構類型時或變量, sizeof 返回實際的大小, 當適用一靜態地空間數組, sizeof 歸還全部數組的尺寸。 sizeof 操作符不能返回動態地被分派了的數組或外部的數組的尺寸

(8)數組作爲參數傳給函數時傳的是指針而不是數組,傳遞的是數組的首地址,如:

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); 
} 

//看來這個《程序員面試寶典》也是抄這段文字了。。。

 

和我一起做題目

容易迷糊的幾個問題
  • e.g. 1.1
char* ss = "0123456789";

sizeof(ss) 結果 4  ss是指向字符串常量的字符指針 
sizeof(*ss) 結果 1  *ss是第一個字符

  • e.g. 1.2
char ss[] = "0123456789";

sizeof(ss) 結果 11  ss是數組,計算到\0位置,因此是10+1 
sizeof(*ss) 結果 1 *ss是第一個字符

  • e.g. 1.3
char ss[100] = "0123456789";

sizeof(ss) 結果是100, ss表示在內存中的大小 100×1 
strlen(ss) 結果是10,strlen是個函數內部實現是用一個循環計算到\0爲止之前

  • e.g. 1.4
int ss[100] = "0123456789";

sizeof(ss) 結果 400 ,ss表示再內存中的大小 100×4 
strlen(ss) 錯誤 ,strlen的參數只能是char* 且必須是以''\0''結尾的

  • e.g. 1.5
char q[]="abc"; 
char p[]="a\n"; 
sizeof(q),sizeof(p),strlen(q),strlen(p);

結果是 4 3 3 2

這個容易迷糊,好好看清楚。。注意sizeof(p)是“\n”算一位,而strlen(p)在“\n”的時候就已經結束

e.g. 1.6

char szPath[MAX_PATH]

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

還是退化成指針的問題

 

網上找到的幾個牛B點的
  • e.g. 2.1
1. sizeof(char) =                         
2. sizeof 'a'   =                            
3. sizeof "a"   =                         
4. strlen("a")) =

答案:1,1,2,1(說明:原來作者給的答案是1,4,2,1。但第二個應該是1而不是4)

  • e.g. 2.2
short (*ptr[100])[200]; 
1. sizeof(ptr)           = 
2. sizeof(ptr[0])        = 
3. sizeof(*ptr[0])       = 
4. sizeof((*ptr[0])[0])) =  

答案:400,4,400,2

這裏我們定義了一個100個指針數組,每個指針均指向有200個元素的數組,其內存佔用爲200*sizeof(short)字節。那麼這100個數組指針的大小sizeof(ptr)爲100*sizeof(short*)。接着,指針數組的第一個指針ptr[0]指向第一個數組,所以這個指針ptr[0]的大小實際上就是一個普通指針的大小,即sizeof(short*)。*ptr[0]指向第一個數組的起始地址,所以sizeof(*ptr[0])實際上求的是第一個組的內存大小200*sizeof(short)。(*ptr[0])[0])是第一個數組的第一個元素,因爲是short型,所以這個元素的大小sizeof((*ptr[0])[0]))等價於sizeof(short)。

e.g. 2.3

#include <stdio.h> 
#pragma pack(push) 
#pragma pack(2) 
typedef struct _fruit 
{ 
  char          apple; 
  int           banana; 
  short         orange;    
  double        watermelon; 
  unsigned int  plum:5; 
  unsigned int  peach:28;  
  char*         tomato; 
  struct fruit* next;      
} fruit; 
#pragma pack(4) 
   
typedef struct _fruit2 
{ 
  char           apple; 
  int            banana;    
  short          orange; 
  double         watermelon; 
  unsigned int   plum:5; 
  unsigned int   peach:28;    
  char*          tomato; 
  struct fruit2* next;      
} fruit2;   
#pragma pack(pop) 
int main(int argc, char *argv[]) 
{ 
  printf("fruit=%d,fruit2=%d\n",sizeof(fruit),sizeof(fruit2)); 
} 

答案:fruit=30,fruit2=36

聽聽作者怎麼說:

如果你回答錯誤,那麼你對數據結構的對齊還沒有吃透。這裏#pragma pack(2)強制設置編譯器對齊屬性爲2,所以第一個數據結構以2對齊,sizeof(fruit)=(sizeof(apple)+1)+sizeof(banana)+sizeof(orange)+sizeof(watermelon)+((plum:5bit+peach:28bit+15bit)/8bit)+sizeof(tomato)+sizeof(next)(注意式子中1 和 15bit 表示補齊內存,使其以2對齊,),既sizeof(fruit)=(sizeof(char)+1)+sizeof(int)+sizeof(short)+sizeof(double)+sizeof(char*)+sizeof(struct fruit*)。第一個數據結構聲明完了之後,又使用#pragma pack(4)強制設置編譯器對齊屬性爲4,所以同理,可以得到sizeof(fruit2)=(sizeof(char)+3)+sizeof(int)+(sizeof(short)+2)+sizeof(double)+((5bit+28bit+31bit)/8bit)+sizeof(char*)+sizeof(struct fruit2*)。

注:#pragma pack(push)保存默認對齊,#pragma pack(pop)恢復默認對齊。

(這個沒有細細看,太累了,明天再看這個例子)

 

引用申明

有文章在引用了其它作者的很多文字,所有權都歸原作者所有,這裏僅供本人總結學習,下面給出參考過或者引用過的鏈接,謝謝作者。

1.http://edu.codepub.com/2009/1117/17745.php

2.http://dev.yesky.com/143/2563643.shtml

3.http://blog.chinaunix.net/u2/62684/showart_489736.html

4.http://www.vckbase.com/document/viewdoc/?id=1054

5.《程序員面試寶典》第二版 p52-p63

本文出處:
http://pppboy.blog.163.com/blog/static/302037962010825105652390/
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章