1. 程式人生 > >C++基礎語法知識點彙總(一)

C++基礎語法知識點彙總(一)

Const 關鍵字

  • 定義普通變數時,只能初始化一次,不可再修改。
  • 定義指標變數時,在型別前則值不能改,在型別後則地址不能改;若兩者都有,則都不能改。
int a = 2;
const int* p1 = &a; //值不可以修改,地址可以修改
int* const p2 = &a; //值可以修改,地址不可以修改
  • 在類中定義的const函式,不能修改類成員變數的值。
  • 在函式中的const形參,可以防止意外修改傳進來的引數值。

結構體

  • C++中struct與class的唯一區別是:預設訪問限定符為public,而class為private。
  • C中struct不允許有函式,內部成員變數訪問許可權只能是public,且不可以繼承。
  • struct vs union:
    • (1)struct中各成員有各自的記憶體空間,struct所佔用記憶體是各成員所佔用記憶體之和;union中各成員共享一段記憶體空間,union所佔用記憶體是最大變數或物件所佔用記憶體(需要考慮記憶體對齊問題)。
    • (2)對struct不同成員賦值互不影響,而對於union不同成員賦值,原來成員的值將不復存在。

指標與陣列

  • 嚴格地說,陣列不是指標,指標也不能說是陣列。指標僅在記憶體中代表一個地址,而陣列是許多連續的記憶體塊,多個型別相似的元素儲存在其中。
  • 陣列名變數是陣列中首元素的地址。它表現得像一個不能被修改的常指標一樣,但卻並不是一個指標,比如當陣列名作為sizeof操作符的運算元時,返回的是整個陣列的大小,而不是指向陣列的常指標的大小;當陣列名作為&操作符的運算元時,返回的是一個指向陣列的指標,而不是一個指向某個指標常量的指標,並且,“&常指標”不一定是合法的。
  • 陣列指標是指向該陣列首元素地址的一個指標變數,只是一個普通的變數,可以賦為任何的值。
  • 指標陣列,表示一個數組,它的內容是一個個的指標。陣列指標,表示一個指向陣列的指標。一般情況下陣列名的值是一個指標常量,是陣列首元素的地址。對於一維整型陣列,其為整型變數的 指標;對於二維整型陣列,其為一維陣列的指標。而陣列名取地址所產生的是一個指向陣列的指標。
    int arr1[4];
    int arr2[4][4];

    int *(a[4]); //定義指標陣列,括號可以省略
    a[0] = &arr1[0];
    *a[0] = 1; 
    printf("arr1[0]=%d
\n"
, *a[0]); //arr1[0]=1 int (*b)[4];//定義陣列指標,為長度4的一維int型陣列的指標 //b = arr1; //錯誤,arr1是int型變數的指標 b = &arr1; //正確,&arr1是長度4一維int型陣列的指標 b = arr2; //正確,arr2是長度4一維int陣列的指標 //b = &arr2;//錯誤,&arr2是4x4二維int型陣列的指標 b = &arr1; (*b)[0] = 2; //小括號不可省略,因為中括號優先順序>星號 printf("arr1[0]=%d\n", (*b)[0]);//arr1[0]=2
  • 陣列表示法array_name[index]是一種語法糖,編譯器會翻譯為*(array_name+index),array_name表示陣列中第一個元素的地址, +index則表示用於指標運算的偏移量。在程式與陣列互動的時候,可以直接用指標表示法代替陣列表示法。

二維陣列

  • 引數傳遞:在被調函式中對形引數組定義時,必須指定第二維(以及更高維)大小,第一維可以省略,如void Func(int array[][])是不合法的。如果在形參中不說明列數,則系統無法決定應為多少行多少列。
  • 如果傳遞的實參大於形參,則在函式中只對實參的部分進行運算,比如下列程式碼中Func只會訪問到陣列的3行10列:
void Func(int array[3][10]);
int a[5][10];
Func(a);
  • 對於靜態二維陣列而言,a[i][j],a[i*n+j]和*((int*)a+i*n+j)這些定址方式是等價的,通過後一種定址方式,我們也可以利用Func((int**a, int n))來實現二維陣列的引數傳遞。
  • 對於動態二維陣列而言,每個row內的記憶體地址是連續的,但是每個row之間的記憶體地址一般是不連續的,也就是說在定址表示式中凡是出現n(每個row的長度,即列數)都是不正確的,比如a[i*n+j]和*((int*)a+i*n+j)這兩種定址方式都是非法的。a[i][j]無論二維陣列是靜態還是動態,都是正確的;除此之外,*(*((int*)a+i)+j)這種方式就等同於a[i][j](提示:連續運用兩次語法糖array_name[index] = *(array_name+index)),都可以用。
  • 為什麼*(*((int*)a+i)+j)可以定址a[i][j]? 因為陣列a中存貯的都是int型指標(int*),這些指標指向一個int陣列,陣列a中的每個元素就是指向每個row的首地址的指標,*((int*)a+i)就是解引用a[i]得到第i行的陣列的首地址,加上偏移量j就拿到了元素a[i][j]的地址了。
  • 動態二維陣列可以通過下列語句生成,注意釋放記憶體的寫法:
int **a;
a = new int*[num_row];
for (int i=0; i<num_row; ++i) {
  a[i] = new int[num_colume](); // 初始化為0
}
/* do something */
for (int i=0; i<num_row; ++i) {
  delete [] a[i];
}
delete [] a;
  • 動態二維陣列的第二種寫法,需要使用STL中的vector。
vector<vector(int)> a(num_row, vector<int>(num_column));

引用

  • 引用與指標的異同點
    • C++ 標準只規定了引用是什麼樣子的(用法),但沒有規定引用該怎麼實現,具體編譯器對於引用的實現方式不盡相同。通常情況下,引用是通過指標實現的,不過提供了一種比指標更容易理解的語法,同時在編譯時會進行優化,引用就類似於常量指標。
    • C++11 標準 § 8.3.2/4
      It is unspecified whether or not a reference requires storage.
      C++11 標準 § 8.3.2/5
      There shall be no references to references, no arrays of references, and no pointers to references.
    • 相同點:兩者都是地址的概念,指標指向一塊記憶體,它的內容是所指記憶體的地址,引用是某塊記憶體的別名,反彙編得到的內部實現是隻讀指標,引用變數記憶體空間儲存的是被引用變數的地址。
    • 區別點
      • a. 指標是一個實體,是獨立存在的,而引用僅是個別名,是依附於所引用的物件而存在的。
      • b. 引用需要在定義時初始化,之後不可變,並且不能為空(NULL),而指標沒有這些限制。
      • c. 通過引用訪問比通過指標訪問更方便,無需解引用(*),指標需要解引用。
      • d. sizeof運算子:sizeof(引用)返回的所指向變數(物件)的大小,sizeof(指標)返回的是指標本身的大小,即所指向變數或物件的地址的大小。
      • e. 自增運算子號(++):引用的++實際上是所指向變數或物件的++,而指標的++是記憶體地址的++,將指向指標之後的記憶體地址。
  • 按值傳遞(pass by value)和按引用傳遞(pass by reference)
    • 按值傳遞會生成實參的一份拷貝,傳遞大物件會有不可忽視的分配儲存空間(記憶體)的開銷。按引用傳遞可以避免大物件的拷貝開銷,只會有引用的拷貝,相當於一個常量指標的拷貝開銷,可以忽略。
    • 按引用傳遞時,形參作為實參的別名使用,對形參變數的操作就是對被引用變數或物件的操作。而按值傳遞時,對形參變數的操作只針對拷貝副本,不影響實參本身。
  • 將引用作為函式返回值的注意事項
    • 不能返回區域性變數的引用,區域性變數在函式返回後被銷燬,會導致返回的引用失效,造成執行時錯誤。
    • 不能返回函式內部new分配出來記憶體的引用,沒有正確釋放會造成記憶體洩漏(memory leak)。
    • 可以返回類資料成員的引用,但最好是const,返回非常量引用會破壞封裝。
    • 流操作符過載的返回值必須是引用,其他操作符不能返回引用。