C語言標準定義了32個關鍵字
union聲明聯合數據類型
- Union declaration - cppreference.com
- 維護足夠的空間來置放多個數據成員中的“一種”,而不是為每一個數據成員配置空間,在 union 中所有的數據成員共用一個空間,同一時間只能儲存其中一個數據成員,所有的數據成員具有相同的起始地址
union StateMachine
{char character;int number; char *str; double exp;
};
- 一個 union 只配置一個足夠大的空間以來容納最大長度的數據成員,以上例而言,最大 長度是 double 型態,所以 StateMachine 的空間大小就是 double 數據類型的大小。
- union主要目的是為了壓縮數據存儲的空間,如果變量不會在同一時間被使用到就可以使用 union?
enum聲明枚舉類型
- Enumeration declaration - cppreference.com?
rigister 聲明寄存器變量
- Storage class specifiers - cppreference.com
- https://en.wikibooks.org/wiki/C%2B%2B_Programming/Programming_Languages/C%2B%2B/Code/Keywords/register
- 這個關鍵字請求編譯器盡可能的將變量存在 CPU 內部寄存器中而不是通過內存尋址訪問以提高效率。注意是盡可能,不是絕對。因為 CPU 的寄存器有限
- 注意: 寄存器在 c 和 c++之間有不同的語義。在 c 語言中,可以通過聲明數組寄存器來禁止數組到指針的轉換: register int a [1] ;?
- 從 C++ 17 開始,auto 關鍵字不再是 C++ 存儲類說明符,且 register 關鍵字被棄用
- 這意味著變量的最大尺寸等于寄存器的大小(通常是一個詞),且不能對它應用一元的 '&' 運算符(因為它沒有內存位置)。
- 存儲空間分配不同,auto類型分配在棧上,屬于動態存儲類別,占動態存儲區空間,函數調用結束后自動釋放;
- 而static分配在靜態存儲區,在程序整個運行期間都不釋放。兩者之間的作用域相同,但生存期不同
- static局部變量在所處模塊的初次運行時進行初始化工作,且只初始化一次。
- 對于局部靜態變量,如果不賦初值,編譯期會自動賦初值0或空字符;而auto類型的初值是不確定的。(對于C++中的class對象例外,class的對象實例如果不初始化,則會自動調用默認構造函數,不管是否是static類型)?
- register 變量必須是 能被 CPU 寄存器所接受的類型。意味著 register 變量必須是一個單個的值,并且其長度應小 于或等于整型的長度
volatile說明變量類型可以被隱含改變
- cv (const and volatile) type qualifiers - cppreference.com
- 編 譯器對訪問該變量的代碼就不再進行優化,從而可以提供對特殊地址的穩定訪問
- const volatile修飾的變量 - Jeremy's blog
int i=10;
int j = i;//(1)語句
int k = i;//(2)語句
- 這時候編譯器對代碼進行優化,因為在(1)、(2)兩條語句中,i 沒有被用作左值。這時候 編譯器認為 i 的值沒有發生改變,所以在(1)語句時從內存中取出 i 的值賦給 j 之后,這個 值并沒有被丟掉,而是在(2)語句時繼續用這個值給 k 賦值。編譯器不會生成出匯編代碼 重新從內存里取 i 的值,這樣提高了效率。但要注意:(1)、(2)語句之間 i 沒有被用作左 值才行。
volatile int i=10;
int j = i;//(3)語句
int k = i;//(4)語句
- ?volatile 關鍵字告訴編譯器 i 是隨時可能發生變化的,每次使用它的時候必須從內存中取出 i 的值,因而編譯器生成的匯編代碼會重新從 i 的地址處讀取數據放在 k 中。這樣看來,如果 i 是一個寄存器變量或者表示一個端口數據或者是多個線程的共享數 據,就容易出錯,所以說 volatile 可以保證對特殊地址的穩定訪問。
但是注意:在 VC++6.0 中,一般 Debug 模式沒有進行代碼優化,所以這個關鍵字的作 用有可能看不出來。你可以同時生成 Debug 版和 Release 版的程序做個測試。
mutable 存儲類
- mutable?說明符僅適用于類的對象,這將在本教程的最后進行講解。它允許對象的成員替代常量。也就是說,mutable 成員可以通過 const 成員函數修改。
thread_local 存儲類
- 使用 thread_local 說明符聲明的變量僅可在它在其上創建的線程上訪問。 變量在創建線程時創建,并在銷毀線程時銷毀。 每個線程都有其自己的變量副本。
- thread_local 說明符可以與 static 或 extern 合并。
- 可以將 thread_local 僅應用于數據聲明和定義,thread_local 不能用于函數聲明或定義。
- 以下演示了可以被聲明為 thread_local 的變量:
thread_local int x; // 命名空間下的全局變量
class X
{static thread_local std::string s; // 類的static成員變量
};
static thread_local std::string X::s; // X::s 是需要定義的void foo()
{thread_local std::vector<int> v; // 本地變量
}
定義
- 所謂的定義就是(編譯器)創建一個對象,為這個對象分配一塊內存,取上一個名字,這個名字就是變量名或對象名。
- 但注意,這個名字一旦和 這塊內存匹配起來,它們就同 生共死,終生不離不棄。并且這塊內存的位置也不能被改變。
- 一個變量或對象在一定的區 域內(比如函數內,全局等)只能被定義一次,如果定義多次,編譯器會提示你重復定義 同一個變量或對象。
聲明? 兩重含義
第一重
- 通知編譯器,變量名字 已經匹配到一塊內存空間,此刻出現的變量或對象是在別的地方已經定義過了
- 聲明可以出現多次
第二重
- 通知編譯器,名字已經提前預定了,別的地方再也不能用它來作為變量名或對象名
- 例子:void fun(int i, char c);
例子
- int i; 定義
-
extern int i; 聲明
重點:
- 定義聲明最重要的區別:定義創建了對象并為這個對象分配了內存,聲明沒有分配內存
基本數據類型
- 使用sizeof 查看具體平臺數據類型的大小
命名規則
- ?所有宏定義、枚舉常數、只讀變量全用大寫字母命名,用下劃線分割單詞。?
const int MAX_LENGTH = 100; //這不是常量,而是一個只讀變量,具體請往后看
#define FILE_PATH “/usr/tmp”
- 定義變量的同時千萬千萬別忘了初始化。因為,定義變量時編譯器并不一定清空了 這塊內存,它的值可能是無效的數據
- 不同類型數據之間的運算要注意精度擴展問題,一般低精度數據將向高精度 數據擴展。
sizeof
- sizeof(p) 和 sizeof(*p)的區別?
- sizeof(p)是指針類型,64位平臺是8,32位平臺是4
- sizeof(*p) 是指針指向的數據類型的大小,如果是long double就是16,double是8,int 是4
int main(){long double *p = nullptr;std::cout << sizeof(p) <<std::endl;std::cout << sizeof(*p) <<std::endl;
}
int main(){int a[100];std::cout << sizeof(a) <<std::endl; //400std::cout << sizeof(&a) <<std::endl; //8std::cout << sizeof(&a[0]) <<std::endl;//8
}
#include <iostream>int b[100];
void fun(int b[100]){std::cout << sizeof(b) <<std::endl; //8
}int main(){fun(b);
}
#include <iostream>
#include <cstring>int main(){char a[1000];for (int i = 0; i < 1000; ++i) {a[i] = -1 - i;}printf("%d",strlen(a));
}
- for 循環內,當 i 的值為 0 時,a[0]的值為-1。關鍵就是-1 在內存里面如何存儲。
- 計算機系統中,數值一律用補碼來表示(存儲)。主要原因是使用補碼,可以將符號位和其它位統一處理;同時,減法也可按加法來處理。另外,兩個用補碼表示的數 相加時,如果最高位(符號位)有進位,則進位被舍棄。正數的補碼與其原碼一致;負數的 補碼:符號位為 1,其余位為該數絕對值的原碼按位取反,然后整個數加 1。
按照負數補碼的規則,可以知道-1 的補碼為 0xff,-2 的補碼為 0xfe......當 i 的值為 127 時,a[127]的值為-128,而-128 是 char 類型數據能表示的最小的負數。當 i 繼續增加,a[128] 的值肯定不能是-129。因為這時候發生了溢出,-129 需要 9 位才能存儲下來,而 char 類型 數據只有 8 位,所以最高位被丟棄。剩下的 8 位是原來 9 位補碼的低 8 位的值,即 0x7f。 當 i 繼續增加到 255 的時候,-256 的補碼的低 8 位為 0。然后當 i 增加到 256 時,-257 的補 碼的低 8 位全為 1,即低八位的補碼為 0xff,如此又開始一輪新的循環...... - 按照上面的分析,a[0]到 a[254]里面的值都不為 0,而 a[255]的值為 0。strlen 函數是計 算字符串長度的,并不包含字符串最后的‘\0’。而判斷一個字符串是否結束的標志就是看 是否遇到‘\0’。如果遇到‘\0’,則認為本字符串結束。分析到這里,strlen(a)的值為 255 應該完全能理解了。這個問題的關鍵就是要明白 char 類型默認情況下是有符號的,其表示的值的范圍為[-128,127],超出這個范圍的值會產生溢 出。另外還要清楚的就是負數的補碼怎么表示。弄明白了這兩點,這個問題其實就很簡單了。
所謂原碼就是前面所介紹的二進制定點表示法,即最高位為符號位,“0”表示正,“1”表示負,其余位表示數值的大小。反碼表示法規定:正數的反碼與其原碼相同;負數的反碼是對其原碼逐位取反,但符號位除外。補碼表示法規定:正數的補碼與其原碼相同;負數的補碼是在其反碼的末位加1。根據原碼的定義:正零和負零的原碼為:+0 : 0000 0000 0000 0000 0000 0000 0000 0000 (32 bit)-0 : 1000 0000 0000 0000 0000 0000 0000 0000而反碼為:+0 : 0000 0000 0000 0000 0000 0000 0000 0000-0 : 1111 1111 1111 1111 1111 1111 1111 1111補碼為:+0 : 0000 0000 0000 0000 0000 0000 0000 0000-0 : 1 0000 0000 0000 0000 0000 0000 0000 0000可以看出,-0的補碼發生溢出,舍棄最高位后,其跟+0在內存的表示一樣,都是:0000 0000 0000 0000 0000 0000 0000 0000
switch case
- case 后面只能是整型或字符型的常量或常量表達式(想想字符型數據在內存里 是怎么存的)?
- switch case排列順序
- 把正常情況放在前面,而把異常情況放在后面
- 按執行頻率排列 case 語句
- switch 里面不可以使用 continue?
- ?在switch case 語句中能否使用continue關鍵字?_Keep Fighting All The Time-CSDN博客_switch語句中的continue
循環代碼
- 多重循環中,如果有可能,應當將最長的循環放在最內層,最短的循環放在最外層,以減少 CPU 跨切循環層的次數。
void
- void *可以指向任何類型的數據?
-
void 不能代表一個真實的變量,因為沒有內存空間
return關鍵字
- c++ - Return char* from function - Stack Overflow
return char*
- 函數內新建一個static char數組,這樣函數結束數組也不會被銷毀? ?/? ?使用const char * 字符串存儲在常量區
- 函數內部動態申請內存,使用完需要釋放內存
- 全局聲明數組,不好,別人會改
- 函數返回char* 的解決方案_芒果兒-CSDN博客_c++ 返回char*
const
- case 語句后必須是一個常量,const 修飾的只讀變量仍然是變量,所以是不可以的
- 【C語言】const關鍵字用法 - 代碼先鋒網
- const 定義的只讀變量從匯編的角度來看,只是給出了對應的內存地址,而不是象#define 一樣給出的是立即數,所以,const 定義的只讀變量在程序運行過程中只有一份拷貝(因為 它是全局的只讀變量,存放在靜態區),
- 而#define 定義的宏常量在內存中有若干個拷貝。 #define 宏是在預編譯階段進行替換,而 const 修飾的只讀變量是在編譯的時候確定其值。
- #define 宏沒有類型,而 const 修飾的只讀變量具有特定的類型。?
- const 離哪個近,修飾哪個變量,不可以改變?
- 修飾函數的參數? 告訴編譯器 ,形參輸入,在函數體中的不能改變,從而防止了使用者的一些無意的或錯誤的修改。?
- 修飾函數的返回值 const 修飾符也可以修飾函數的返回值,返回值不可被改變。例如: const int Fun (void);
參考鏈接
- What does sizeof (int) * p semantically mean?
- c - Difference between sizeof(*p) and sizeof(p)? - Stack Overflow
- c語言中的 %u 什么意思啊?_百度知道
struct關鍵字
- 多種數據組合起來的一個整體,其表現形式是一個結構體
- 傳入傳出都是結構體的形式,可以壓縮傳輸的參數
- 結構體所占的內存大小是其成員所占內存之和? sizeof(struct),這里還涉及到結構體的內存對齊
- 即使是空的結構體,使用sizeof求內存,其大小是1,主要是為其分配一個地址,空類也是一樣的
柔性數組
- 結構中的最后一個元素允許是未知大小的數組,這就叫做柔性數組成員,但結構中的柔性數組成員前面必須至少一個其他成員。
- 柔性數組成員允許結構中包含一個大小可變的數組。sizeof 返回的這種結構大小不包括柔性數組的內存。包含柔性數組成員的結構用 malloc ()函數進行內存的動態分配,并且分配的內存應該大于結構的大小,以適應柔性數組的預期大小。
- 用 sizeof(type_a)得到的只有 4,就是 sizeof(i)=sizeof(int)。那個 0 個元素的數組沒有占用空間,而后我們可以進行變長操作了。使用malloc分配內存之后,使用sizeof探測整體的數據大小,仍然是 4
- 柔性數組只是編外人員,不占結構體的編制。只是說 在使用柔性數組時需要把它當作結構體的一個成員,僅此而已。再說白點,柔性數組其實與 結構體沒什么關系,只是“掛羊頭賣狗肉”而已,算不得結構體的正式成員
- 當然,上面既然用 malloc 函數分配了內存,肯定就需要用 free 函數來釋放內存
- 這個柔性數組的概念 實際使用很少
#include <iostream>
#include <cstring>typedef struct st_type{int i;int a[];
}type_a;
int main(){std::cout << sizeof(type_a) << std::endl; //4type_a * p = (type_a*) malloc(sizeof (type_a) + 100 * sizeof(int));std::cout << sizeof(type_a) << std::endl; //4free(p);
}
?大端模式 和 小端模式
- 大端模式(Big_endian):字數據的高字節存儲在低地址中,而字數據的低字節則存放在高地址中。
- 小端模式(Little_endian):字數據的高字節存儲在高地址中,而字數據的低字節則存放在低地址中。
?
- 變量i占4個字節,但只有一個字節的值為1,另外三個字節的值都為0。如果取出低地址上的值為0,毫無疑問,這是大端模式;如果取出低地址上的值為1,毫無疑問,這是小端模式。
使用程序驗證
#include <iostream>
#include <cstring>int checkSystem(){union check{int i;char ch;}c;c.i = 1;return (c.ch == 1);
}
int main(){int a = checkSystem();std::cout << a << std::endl;
}
直接查看內存
?
- ?枚舉變量的大小,實質是常數所占內存空間的大小(常數為int類型,當前主流的編譯器中一般是32位機器和64位機器中int型都是4個字節),枚舉類型所占內存大小也是這樣。參考鏈接:enum枚舉變量所占內存大小_bulebin的博客-CSDN博客_枚舉類型大小
?