數據類型是建立變量的模型。變量名是連續存儲空間的別名,程序中使用變量命名存儲空間,經過變量可使用存儲空間。變量所佔的內存大小取決於建立變量的數據類型。數組
有符號數中數據類型的最高位用於標識數據的符號,最高位爲1表示爲負數,最高位爲0表示爲正數。安全
計算機中有符號數一般使用補碼錶示,正數的補碼爲正數自己,負數的補碼爲負數的絕對值的各位取反後加1。ide
計算機中無符號數一般使用原碼錶示,無符號數默認爲正數,沒有符號位。對於無符號數來講,MAX_VALUE + 1等於MIN_VALUE,MIN_VALUE - 1等於MAX_VALUE。函數
無符號數與有符號數進行混合運算時,會將有符號數轉換爲無符號數後再進行計算,結果爲無符號數。測試
float與double類型的數據在計算機中的表示方法相同,因爲所佔存儲空間不一樣,分貝可以表示的數值範圍和精度不一樣。優化
浮點數在計算機內部存儲方式分爲三段,符號位、指數、尾數spa
浮點數的轉換須要先將浮點數轉換爲二進制,將獲得的二進制浮點數使用科學計數法表示,根據數據類型計算偏移後的指數。指數的偏移量和數據類型有關,float類型加127,double類型加1023。線程
8.25的float表示以下:指針
8.25的二進制表示:1000.01==>1.00001(2^3)調試
符號位:0
指數:127+3 = 130 = b10000010
小數:00001
8.25的float表示爲:0 10000010 00001 000000000000000000 = 0x41040000
因爲float和int類型都佔四個字節,float能表示的具體數字的個數與int相同,可是float表示的數值時不連續的,不能做爲精確數使用,所以float類型的表示範圍比int類型表示的範圍大。Double類型與float類型在計算機中的表示方法相同,可是double類型佔用的存儲空間大,所以所能表示的精度更高。
C語言中的數據類型能夠進行轉換,包括顯示類型轉換和隱式類型轉換。
強制類型轉換時,若是目標類型可以容納目標值,則轉換後結果不變;若是目標類型不能容納目標值,則結果將產生數據截斷。
隱式類型轉換時編譯器進行的類型轉換,低類型到高類型的隱式轉換是安全的,結果不會產生數據截斷;高類型到低類型的隱式轉換是不安全的,結果產生類型截斷,結果多是不正確的。
隱式類型轉換髮生的時機:
A、算術運算中,低類型轉換爲高類型
B、賦值表達式中,表達式的值轉換爲左值的類型
C、函數調用時,實參轉換爲形參的類型
D、函數返回時,return表達式轉換爲返回值類型
void修飾函數返回值和參數
void不能用於定義變量,標準C語言編譯器中sizeof(void)會報錯,GCC編譯器中由於進行了擴展,不會報錯,結果爲1
if...else
if語句根據條件選擇執行語句,else不能獨立存在,可是老是和離它最近的if匹配。
float變量不能直接和0進行比較,須要肯定變量在某個較小的區間內。
if(-0.0000001 < a && a < 0.0000001)
switch
switch語句對應單個條件多個值的狀況,通常狀況下case語句須要有break,不然會致使分支重疊。Default分支須要加上,用於處理特殊狀況。
case語句中的值只能是整型或char類型。
do語句先執行後判斷,循環體至少執行一次
do
{
}while();
while語句先判斷後執行,循環體可能不執行
while()
{
}
for語句先判斷後執行
for(; ; )
break表示終止循環的執行
continue表示停止本次循環,進入下一次循環
const修飾的變量告訴編譯期變量是隻讀的,但仍是變量,向編譯器指明變量不能作左值
const修飾的局部變量在棧上分配空間
const修飾的全局變量在全局數據區分配空間
const只在編譯期有效,在運行期無效
#include <stdio.h>
const int g = 10;
int main(int argc, char **argv)
{
const int c = 1;
int *p = (int *)&c;
*p = 100;
printf("%d\n", c);
p = (int *)&g;
*p = 1000;
printf("%d\n", c);
return 0;
}
編譯正常,運行時對使用指針能夠對const局部變量進行修改,但使用指針對const全局變量修改時發生段錯誤。
標準C語言編譯器不會將const修飾的全局變量存儲於只讀存儲區,而是存儲在能夠修改的全局數據區,其值能夠經過指針修改。可是現代編譯器如GCC將const全局變量存儲在只讀存儲區,經過指針修改其值將發生段錯誤。
const修飾函數參數表示在函數體內部不但願修改參數的值
const修飾函數返回值表示返回值不可改變
volatile指示編譯器不能優化,必須每次到內存中取變量的值,主要修飾被多個線程訪問的變量、或是被未知緣由更改的變量
const volatile int i = 10;
做爲局部變量,i不能夠作左值,但可使用指針修改值,編譯器不對變量i進行優化,每次都到內存取值。
柔性數組是數組大小待定的數組
結構體中最後的成員能夠是柔性數組,柔性數組只是一個標識符,不佔存儲空間。
union只分配最大成員的空間,全部成員共享這個空間。
union的使用會受到計算機大小端的影響
使用union測試計算機大小端的代碼以下:
#include <stdio.h>
void big_little(void);
int main(int argc, char *argv[])
{
big_little();
return 0;
}
void big_little(void)
{
union Mode
{
char c;
int i;
};
union Mode m;
m.i = 1;
if(m.c)
{
printf("little\n");
}
else
{
printf("big\n");
}
}
enum是C語言中的一種自定義類型,enum的值是自定義的整型值,第一個定義的enum值默認爲0,默認狀況下enum的值是前一個定義的值加1,也能夠指定。enum中定義的值是C語言中真正的常量
sizeof是編譯器的內置指示符,用於計算類型或變量所佔內存大小,sizeof的值在編譯期就已經肯定。
int var = 0;
int size = sizeof(var++);
var並不會執行var++,而是在編譯時就將sizeof(var++0替換爲值4
typedef用於給一個已經存在的類型進行重命名,本質上不產生新類型
typedef重命名的源類型能夠在typedef語句後定義,typedef不能使用unsigned、signed修飾。
C語言中接續符(\)能夠指示編譯器,編譯器會將接續符刪除,跟在接續符後面的字符自動接續到前一行。在接續單詞時,接續符後面不能有空格,接續符的下一行以前也不能有空格。一般接續符使用在定義宏代碼塊時使用。
C語言中單引號用來表示字符字面量,雙引號用來表示字符串字面量
‘a’表示字符字面量,佔一個字節大小,’a’+1表示’b’,」a」表示字符串字面量,」a」+1表示指針運算,結果爲指向」a」字符串的結束符’\0’。
char c = 「hello」;
字符串字面量「hello」的地址賦值給字符變量c,因爲地址佔用四個字節空間,賦值給字符類型後會發生類型截斷。
++、--參與混合運算的結果是不肯定的。
三目運算符返回變量的值,而不是變量自己。根據隱式類型轉換規則肯定返回值類型。
int a = 1;
int b = 2;
int c = 0;
c = a < b ? a : b;
(a < b ? a : b) = 3;//error
*(a < b ? &a : &b) = 3;//ok
#define由預處理器處理,直接進行文本替換,不會進行語法檢查,#define定義的宏能夠出如今程序的任意位置
#define定義的宏常量本質爲字面量
宏由預處理器處理,編譯器不知道宏的存在。
宏表達式沒有任何的調用開銷,不能出現遞歸定義
編譯器內置的宏:
__FILE__:被編譯的文件名
__LINE__:當前行號
__DATE__:編譯時的日期
__TIME__:編譯時的時間
__STDC__:編譯器是否遵循標準C規範
#defineLOG(s) printf("[%s] File:%s, Line:%d %s \n", __DATE__, __FILE__, __LINE__, s)
條件編譯是預編譯指示命令,用於控制是否編譯某段代碼。預編譯器根據條件編譯指令有選擇的刪除代碼,編譯器不知道代碼分支的存在。
條件編譯能夠解決頭文件重複包含的編譯錯誤
#ifndef _FILE_H_
#define _FILE_H_
//source code
#endif
條件編譯經過不一樣的條件編譯不一樣的代碼,生成不一樣條件的目標,實際工程中可使用條件編譯將同一份工程代碼生成不一樣的產品線或是區分產品的調試和發佈版。
#error用於生成一個編譯錯誤消息
語法:#error message
message不須要雙引號
#ifndef __cplusplus
#error This file should be processed with C++ compiler.
#endif
編譯過程當中產生錯誤信息意味着編譯將終止,沒法生成最終的可執行程序
#line用於強制指定新的行號和編譯文件名,並對源程序的代碼進行從新編號
#line number filename
#line本質上是對__LINE__和__FILE__宏的重定義
#progma用於指示編譯器完成某些特定的動做,所定義的不少關鍵字和編譯器有關,在不一樣編譯器間是不能夠移植的。預處理器將忽略不認識的#progma指令,不一樣的編譯器可能會對#progma指令的解釋不一樣。
#progma message
編譯時輸出消息到編譯器輸窗口,用於提示信息
#if defined(ANDROID20)
#pragma message("Compile Android SDK 2.0...")
#define VERSION "Android 2.0"
#elif defined(ANDROID23)
#pragma message("Compile Android SDK 2.3...")
#define VERSION "Android 2.3"
#elif defined(ANDROID40)
#pragma message("Compile Android SDK 4.0...")
#define VERSION "Android 4.0"
#else
#error Compile Version is not provided!
#endif
與#error不一樣,#progma massage僅表明一條編譯消息,不表明編譯出錯
#progma once
#progma once用於保證頭文件只被編譯一次,和編譯器相關,編譯器不必定支持。
工程代碼使用以下:
#ifndef_FILE_H_
#define _FILE_H_
#progma once
#endif
#progma pack
#progma pack用於指定內存對齊方式,通常成對使用
#progma pack(n)
//source code
#progma pack()
#運算符用於在預處理期將宏參數轉換爲字符串,只能在宏定義中有效
#define STRING(x) #x
printf("%s\n", STRING(Hello world!));
#define CALL(f, p) (printf("Call function %s\n", #f), f(p))
##運算符用於在預處理期粘連兩個標識符,只在宏定義中有效
#define NAME(n) name##n
int main()
{
int NAME(1);
int NAME(2);
NAME(1) = 1;
NAME(2) = 2;
printf("%d\n", NAME(1));
printf("%d\n", NAME(2));
return 0;
}