程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C >> C語言入門知識 >> C語言01深入理解基本概念(一)

C語言01深入理解基本概念(一)

編輯:C語言入門知識

基本數據類型分析

數據類型
什麼是數據類型 ?
? 數據類型可以理解為固定內存大小的別名
? 數據類型是創建變量的模子

類型的本質
char
short
int
1 byte
2 byte
4 byte
內存空間
char c
short s
int i

變量本質
? 變量是一段實際連續存儲空間的別名
? 程序中通過變量來申請並命名存儲空間
? 通過變量的名字可以使用存儲空間

auto,register,static 分析

auto
? C 語言中的變量可以有自己的屬性
? 在定義變量的時候可以加上“ 屬性” 關鍵字
? “ 屬性” 關鍵字指明變量的特有意義
?auto即C語言中局部變量的默認屬性
?編譯器默認所有的局部變量都是auto的

? static 關鍵字指明變量的“ 靜態” 屬性
? static 關鍵同時具有“ 作用域限定符” 的意義
?static 修飾的局部變量存儲在程序靜態區
?static 的另一個意義是文件作用域標示符
―static 修飾的全局變量作用域只是聲明的文件中
―static 修飾的函數作用域只是聲明的文件中

register
? register 關鍵字指明將變量存儲於寄存器中
? register 只是請求寄存器變量 , 但不一定請求成功
?register 變量的必須是CPU 寄存器可以接受的值
? 不能用& 運算符獲取register 變量的地址

? 小結
? auto 變量存儲在程序的棧中 , 默認屬性
? static 變量存儲在程序靜態區中
? register 變量請求存儲於CPU 寄存器中

if,switch,do,while,for 分析


析 分支語句分析 -- if
?if 語句用於根據條件選擇執行語句
?else 不能獨立存在且總是與它最近的if 相匹配
?else 語句後可以接連其他 if 語句

?if 語句中零值比較的注意點
? bool 型變量應該直接出現於條件中 , 不要進行比較
? 普通變量和 0 值比較時 , 0 值應該出現在比較符號左邊
? float 型變量不能直接進行 0 值比較 , 需要定義精度

析 分支語句分析 -- switch
? switch 語句對應單個條件多個分值的情形
? 每個case 語句分支必須要有 break , 否則會導致分支重疊
? default 語句有必要加上 , 以處理特殊情況
表達式
default 語句1 1 語句2 2 語句3 3
break break break break

?case 語句中的值只能是整型或字符型
?case 語句排列順序分析
? 按字母或數字順序排列各條語句
? 正常情況放在前面 , 異常情況放在後面
? default 語句只用於處理真正的默認情況

分支語句分析
? 小結
? if語句實用於需要“按片”進行判斷的情形中
? switch語句實用於需要對各個離散值進行分別判
斷的情形中
? if語句可以安全從功能上代替switch語句,但
switch語句無法代替if語句
? switch語句對於多分支判斷的情形更加簡潔

循環語句分析
? 循環語句的基本工作方式
? 通過條件表達式判定是否執行循環體
? 條件表達式遵循if 語句表達式的原則


?do,while,for 的區別
? do 語句先執行後判斷 , 循環體至少執行一次
? while 語句先判斷後執行 , 循環體可能不執行
? for 語句先判斷後執行 , 相比while 更簡潔

?break 和continue 的區別
? break 表示終止循環的執行
? continue 表示終止本次循環體 , 進入下次循環執行

注意:swicth語句不能與continue同時用

goto,void,extern,sizeof 分析

遭人遺棄的 goto
? 高手潛規則 : 禁用goto
? 項目經驗 : 程序質量與goto 的出現次數成反比
? 最後的判決 : 將goto 打入冷宮

void 的意義
? void 修飾函數返回值和參數
? 如果函數沒有返回值 , 那麼應該將其聲明為void 型
? 如果函數沒有參數 , 應該聲明其參數為void
void 修飾函數返回值和參數僅為了表示 無

不存在void 變量

C 語言沒有定義void 究竟是多大內存的別名

沒有void 的標尺

無法在內存中裁剪出void 對應的變量

? void 指針的意義
? C 語言規定只有相同類型的指針才可以相互賦值


? void* 指針作為左值用於“ 接收” 任意類型的指針


? void* 指針作為右值賦值給其它指針時需要強制類型轉換


extern 中隱藏的意義
? extern 用於聲明外部定義的變量和函數
? extern 用於“ 告訴” 編譯器用C 方式編譯
C++ 編譯器和一些變種C 編譯器默認會按“ 自己” 的方式編譯
函數和變量 , 通過extern 關鍵可以命令編譯器“ 以標准C 方
式進行編譯” 。

為sizeof 正名
? sizeof 是編譯器的內置指示符 , 不是函數
? sizeof 用於“ 計算” 相應實體所占的內存大小
? sizeof 的值在編譯期就已經確定

const 和volatile 分析

const 修飾變量
? 在C 語言中const 修飾的變量是只讀的 , 其本質還是變量
? const 修飾的變量會在內存占用空間
? 本質上const 只對編譯器有用 , 在運行時無用
原來const 不是真的常量

const 修飾數組
? 在C 語言中const 修飾的數組是只讀的
? const 修飾的數組空間不可被改變

const 修飾指針(判斷const修飾的是p還是*p )
? const int* p; //p 可變 ,p 指向的內容不可變
? int const* p; //p 可變 ,p 指向的內容不可變
? int* const p; //p 不可變 ,p 指向的內容可變
? const int* const p; //p 和p 指向的內容都不可變
口訣 : 左數右指
當const 出現在 * 號左邊時指針指向的數據為常量
當const 出現在* 後右邊時指針本身為常量

const 修飾函數參數和返回值
? const 修飾函數參數表示在函數體內不希望改變參數的值

 

? const 修飾函數返回值表示返回值不可改變 , 多用於返回指針的情況


深藏不漏的volatile
? volatile 可理解為“ 編譯器警告指示字”
? volatile 用於告訴編譯器必須每次去內存中取變量值
? volatile 主要修飾可能被多個線程訪問的變量
? volatile 也可以修飾可能被未知因數更改的變量

struct 和union 分析

由結構體產生柔性數組
? 柔性數組即數組大小待定的數組
? C 語言中結構體的最後一個元素可以是大小未知的數組
? C 語言中可以由結構體產生柔性數組

union 和struct 的區別
? struct 中的每個域在內存中都獨立分配空間
? union 只分配最大域的空間 , 所有域共享這個空間
union 使用的注意事項
? union 的使用受系統大小端的影響

enum 和typedef 分析

枚舉類型的使用方法
? enum 是一種自定義類型
? enum 默認常量在前一個值的基礎上依次加1
? enum 類型的變量只能取定義時的離散值

枚舉類型和#define 的區別
?#define 宏常量只是簡單的進行值替換 , 枚舉常
量是真正意義上的常量
?#define 宏常量無法被調試 , 枚舉常量可以
?#define 宏常量無類型信息 , 枚舉常量是一種特
定類型的常量

面試中……
考官 : 你能說說typedef 具體的意義嗎 ?
應聘者 :typedef 用於定義一種新的類型 。。。
? typedef 用於給一個已經存在的數據類型重命名
? typedef 並沒有產生新的類型
? typedef 重定義的類型不能進行unsigned 和signed 擴展

typedef 和#define 的區別
? typedef 是給已有類型取別名
? #define 為簡單的字符串替換 , 無別名的概念

注釋符號

注釋符號
? 注釋規則小結
? 編譯器會在編譯過程刪除注釋 , 但不是簡單的刪除而是用空
格代替
? 編譯器認為雙引號括起來內容都是字符串 , 雙斜槓也不例外
? “/*……*/” 型注釋不能被嵌套

注釋符號
? 你覺得Y=x/*p 是什麼意思? ?
作者本意 : 把 x 除以*p 的結果賦值給 y
編譯器 : 將/* 作為一段注釋的開始 , 把 /* 後的內
容都當成注釋內容 , 直到*/ 出現為止
在編譯器看來 , 注釋和其它程序元素都是平等的 , 所以 ,
作為程序員也不能看輕注釋 。

? 出色的注釋你來寫
? 注釋應該准確易懂 , 防止二義性 , 錯誤的注釋有害而無利
? 注釋是對代碼的提示 , 避免臃腫和喧賓奪主
? 一目了然的代碼避免加注釋
? 不要用縮寫來注釋代碼 , 這樣可能會產生誤解
? 注釋用於闡述原因而不是用於描述程序的運行過程

接續符和轉義符

? C 語言中的接續符(\) 是指示編譯器行為的利器

? 接續符的使用 :
? 編譯器會將反斜槓剔除 , 跟在反斜槓後面的字符自動解到前
一行
? 在接續單詞時 , 反斜槓之後不能有空格 , 反斜槓的下一行之
前也不能有空格
? 接續符適合在定義宏代碼塊時使用

? C 語言中的轉義符(\) 主要用於表示無回顯字符 , 也可
用於表示常規字符

小結
? C 語言中的反斜槓(\) 同時具有接續符和轉義符的作用
? 當反斜槓作為接續符使用時可直接出現在程序中
? 當反斜槓作為轉義符使用時需出現在字符或字符串中

單引號和雙引號

?C 語言中的 單引號 用來表示 字符常量
?C 語言中的 雙引號 用來表示 字符串常量
‘a’ 表示字符常量
在內存中占1 個字節
’a’+1 表示’a’ 的ASCII 碼加1 , 結果為‘b’
“a” 表示字符串常量
在內存中占2 個字節
“a”+1 表示指針運算 , 結果指向“a” 結束符’\0’

單引號和雙引號
?小結
? 本質上單引號括起來的一個字符代表整數
? 雙引號括起來的字符代表一個指針
? C 編譯器接受字符和字符串的比較 , 可意義是錯誤的
? C 編譯器允許字符串對字符變量賦值 , 其意義是可笑的

邏輯運算符使用分析

? 邏輯運算符&&,|| 和! 真的簡單嗎 ?

短路規則 :
?|| 從左向右開始計算 , 當遇到為真的條件
時停止計算 , 整個表達式為真 ; 所有條件
為假時表達式才為假 。
?&& 從左向右開始計算 , 當遇到為假的條
件時停止計算 , 整個表達式為假 ; 所有條
件為真時表達式才為真。

? “!” 到底是神馬?

C 語言中的邏輯符“!” 只認得0 , 只知道見了0 就返回1 。
因此當其作用的值不是0 時 , 其結果為0 。

? 三目運算符(a?b:c) 可以作為邏輯運算符的載體
? 規則 : 當a 的值為真時 , 返回b 的值 ; 否則返回c 的值

位運算符分析

? 在C 語言中的位運算符
右移 >>
左移 <<
取反 ~
按位異或 ^
按位或 |
按位與 &

? 左移和右移注意點
? 左移運算符<< 將運算數的二進制位左移
? 規則 : 高位丟棄 , 低位補0
? 右移運算符>> 把運算數的二進制位右移
? 規則 : 高位補符號位 , 低位丟棄
注意補齊位的值 !


? 防錯准則 :
? 避免位運算符 , 邏輯運算符和數學運算符同時出現在一個表達式

? 當位運算符 , 邏輯運算符和數學運算符需要同時參與運算時 , 盡
量使用括號() 來表達計算次序
小技巧 :
? 左移n 位相當於乘以2 的n 次方 , 但效率比數學運算符高
? 右移n 位相當於除以2 的n 次方 , 但效率比數學運算符高

++,-- 操作符使用分析


++, -- 操作符使用分析
? 一對令人頭疼的兄弟
你覺得這個表達式的值是多少 ?

? 筆試面試中的++i+++i+++i
a+++b
a++ + b
a + ++b

? 法 貪心法 - -- ++, -- 表達式的閱讀技巧
? 編譯器處理的每個符號應該盡可能多的包含字符
? 編譯器以從左向右的順序一個一個盡可能多的讀入字符
? 當即將讀入的字符不可能和已讀入的字符組成合法符號為止

優先級和類型轉換分析

? C 語言隱式類型轉換
? 算術運算式中 , 低類型轉換為高類型
? 賦值表達式中 , 表達式的值轉換為左邊變量的類型
? 函數調用時 , 實參轉換為形參的類型
? 函數返回值 ,return 表達式轉換為返回值類型
int -> unsigned int -> long -> unsigned long -> double
float
char
short

宏定義與使用分析

? #define 定義宏常量可以出現在代碼的任何地方
? #define 從本行開始 , 之後的代碼都可以使用這個宏常量

? #define 表達式給有函數調用的假象 , 卻不是函數
? #define 表達式可以比函數更強大
? #define 表達式比函數更容易出錯

? 宏表達式在預編譯期被處理 , 編譯器不知道宏表達式
的存在
? 宏表達式用“ 實參” 完全替代形參 , 不進行任何運算
? 宏表達式沒有任何的“ 調用” 開銷
? 宏表達式中不能出現遞歸定義

條件編譯使用分析

? 條件編譯的行為類似於C 語言中的if…else
? 條件編譯是預編譯指示命令 , 用於控制是否編譯某段代碼

? #include 的本質是將已經存在的文件內容嵌入到當前文件中
? #include 的間接包含同樣會產生嵌入文件內容的動作

條件編譯的意義
? 條件編譯使得我們可以按不同的條件編譯不同的代碼段 ,
因而可以產生不同的目標代碼


? #if…#else…#endif 被預編譯器處理 ; 而if…else 語句被
編譯器處理 , 必然被編譯進目標代碼


? 實際工程中條件編譯主要用於一下兩種情況 :
? 不同的產品線共用一份代碼
? 區分編譯產品的調試版和發布版

小結
? 通過編譯器命令行能夠定義預處理器使用的宏
? 條件編譯可以避免重復包含頭同一個頭文件
? 條件編譯是在工程開發中可以區別不同產品線的代碼
? 條件編譯可以定義產品的發布版和調試版

break的用法

問題

break 關鍵字可以用於按條件退出循環。當break的條件滿足時,會直接退出循環,而不再判斷循環條件和進行循環變量的遞增或者遞減。

步驟

實現此案例需要按照如下步驟進行。

步驟一:break的用法代碼如下所示:

#include

int main()
{
for (int i = 0; ; i++)
{
int num;
printf("請輸入一個整數(輸入0退出):");
scanf("%d", &num);

if (num > 0)
printf("%d是一個正數。\n", num);
else if (num < 0)
printf("%d是一個負數。\n", num);
else
break;
}

return 0;
}
  1.  

上述代碼中,以下代碼:

  1. for (int i = 0; ; i++)

設置一個死循環。

上述代碼中,以下代碼:

  1. int num;
  2. printf("請輸入一個整數(輸入0退出):");
  3. scanf("%d", &num);

首先,定義一個整型變量num,用於存儲輸入的一個整數。

然後,使用函數printf提示輸入的一個整數,並提示輸入0退出。

最後,使用函數scanf輸入的一個整數到變量score中。

上述代碼中,以下代碼:

  1. if (num > 0)
  2. printf("%d是一個正數。\n", num);

如果輸入的整數大於0,則打印是一個正數。

上述代碼中,以下代碼:

  1. else if (num < 0)
  2. printf("%d是一個負數。\n", num);

如果輸入的整數小於0,則打印是一個負數。

上述代碼中,以下代碼:

  1. else
  2. break;

如果輸入的整數等於0,則退出循環。如此使用break語句,可以實現不定次數循環。

完整代碼

本案例的完整代碼如下所示:

#include

int main()
{
for (int i = 0; ; i++)
{
int num;
printf("請輸入一個整數(輸入0退出):");
scanf("%d", &num);

if (num > 0)
printf("%d是一個正數。\n", num);
else if (num < 0)
printf("%d是一個負數。\n", num);
else
break;
}

return 0;
}
  1.  

循環次數不確定的循環

問題

實現“猜數字”小游戲。

步驟

實現此案例需要按照如下步驟進行。

步驟一:循環次數不確定的循環

#include
#include
#include

int main()
{
srand(time(0));
int ran = rand() % 100 + 1;

for(;;)
{
int num;
printf("請輸入一個1到100之間的整數:");
scanf("%d", &num);
if (num < 0 || num > 100)
printf("輸入的數不在1到100之間。\n");
else if (num == ran)
break;
else if (num > ran)
printf("大了\n");
else
printf("小了\n");
}
printf("猜對了\n");

return 0;
}
  1.  

上述代碼中,以下代碼:

  1. srand(time(0));

設置隨機的算法種子。要使用隨機數,需要此句。

上述代碼中,以下代碼:

  1. int ran = rand() % 100 + 1;

使用函數rand生成一個1~100之間的隨機數。

上述代碼中,以下代碼:

  1. for(;;)

設置一個死循環。

上述代碼中,以下代碼:

  1. int num;
  2. printf("請輸入一個1到100之間的整數:");
  3. scanf("%d", &num);

首先,定義一個整型變量num,用於存儲輸入一個1~100之間的整數。

然後,使用函數printf提示輸入整數。

最後,使用函數scanf輸入整數到變量score中。

上述代碼中,以下代碼:

  1. if (num < 0 || num > 100)
  2. printf("輸入的數不在1到100之間。\n");

容錯保護,輸入的整數不在1~100之間,則重新輸入。

上述代碼中,以下代碼:

  1. else if (num == ran)
  2. break;

如果輸入的整數正好與隨機數相等,則使用break語句退出。

上述代碼中,以下代碼:

  1. else if (num > ran)
  2. printf("大了\n");

如果輸入的整數大於隨機數,則提示大了。

上述代碼中,以下代碼:

  1. else
  2. printf("小了\n");

如果輸入的整數小於隨機數,則提示小了。

上述代碼中,以下代碼:

  • printf("猜對了\n");

使用break語句退出循環後,提示猜對了。

完整代碼

本案例的完整代碼如下所示:

#include
#include
#include

int main()
{
srand(time(0));
int ran = rand() % 100 + 1;

for(;;)
{
int num;
printf("請輸入一個1到100之間的整數:");
scanf("%d", &num);
if (num < 0 || num > 100)
printf("輸入的數不在1到100之間。\n");
else if (num == ran)
break;
else if (num > ran)
printf("大了\n");
else
printf("小了\n");
}
printf("猜對了\n");

return 0;
}
  1.  

continue用法

問題

continue 關鍵字可以用於按條件中止本次循環,繼續下次循環。break 退出循環,continue不會退出循環,只是不再繼續執行本次循環。

步驟

實現此案例需要按照如下步驟進行。

步驟一:continue用法代碼如下所示:

#include

int main()
{
for (int i = 0; i <= 100; i++)
if(i % 2)
continue;
else
printf("%d ", i);
printf("\n");

return 0;
}

上述代碼中,以下代碼:

  1. for (int i = 0; i <= 100; i++)

設置一個循環,循環100次。

上述代碼中,以下代碼:

  1. if(i % 2)
  2. continue;
  3. else
  4. printf("%d ", i);

判斷循環變量,如果循環變量是奇數,則使用continue語句繼續循環。否則打印這個偶數。

完整代碼

本案例的完整代碼如下所示:

#include

int main()
{
for (int i = 0; i <= 100; i++)
if(i % 2)
continue;
else
printf("%d ", i);
printf("\n");

return 0;
}
  1.  

二重循環的使用

問題

打印如下圖-2所示的圖形:

\

圖-2

步驟

實現此案例需要按照如下步驟進行。

步驟一:二重循環的使用代碼如下所示:

#include

int main()
{
for (int i = 0; i < 5; i++)
{
for (int j = 0; j <= i; j++)
printf("*");
printf("\n");
}

return 0;
}
  1.  

上述代碼中,以下代碼:

  1. for (int i = 0; i < 5; i++)

設置一個外層循環,循環5次,代表打印5行。

上述代碼中,以下代碼:

  1. for (int j = 0; j <= i; j++)
  2. printf("*");

設置一個內層循環,循環的次數由外層循環變量i決定,當i等於0時,打印一個*,當i等於1時,打印兩個*,依次類推。

由此可以看出外層循環負責打印多少行,內層循環負責在一行中打印多少個*。

上述代碼中,以下代碼:

  1. printf("\n");

此句非常重要,因為內層循環退出時,表示某行的*已經打印完畢,此時需要打印一個回車,以換行。

完整代碼

本案例的完整代碼如下所示:

#include

int main()
{
for (int i = 0; i < 5; i++)
{
for (int j = 0; j <= i; j++)
printf("*");
printf("\n");
}

return 0;
}
  1.  

二重循環的使用(續1)

問題

打印如下圖-3所示的圖形:

\

圖-3

步驟

實現此案例需要按照如下步驟進行。

步驟一:switch語句的使用

代碼如下所示:

  1. #include
  2.  
  3. int main()
  4. {
  5. for (int i = 0; i < 5; i++)
  6. {
  7. for (int j = 0; j <= i * 2; j++)
  8. printf("*");
  9. printf("\n");
  10. }
  11.  
  12. return 0;
  13. }

上述代碼中,以下代碼:

  1. for (int i = 0; i < 5; i++)

設置一個外層循環,循環5次,代表打印5行。

上述代碼中,以下代碼:

  1. for (int j = 0; j <= i * 2; j++)
  2. printf("*");

設置一個內層循環,循環的次數由外層循環變量i * 2決定。當i等於0時,條件為j <= 0,所以循環一次,打印一個*。當i等於1時,條件為j <= 1 * 2,所以循環三次,打印三個*,依次類推。

由此可以看出外層循環負責打印多少行,內層循環負責在一行中打印多少個*。

上述代碼中,以下代碼:

  1. printf("\n");

此句非常重要,因為內層循環退出時,表示某行的*已經打印完畢,此時需要打印一個回車,以換行。

完整代碼

本案例的完整代碼如下所示:

#include

int main()
{
for (int i = 0; i < 5; i++)
{
for (int j = 0; j <= i * 2; j++)
printf("*");
printf("\n");
}

return 0;
}
  1.  
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved