日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

當你敲完Hello World后的第一步——C語言

作者:_奇奇 更新時間: 2022-05-13 編程語言

“江山如畫,一時多少豪杰——時二二年五月六日”

這里是目錄

  • 前言
  • 一、#define指令
    • 1.#define定義宏
    • 2.#define 替換 宏
    • 3.帶副作用的宏參數
    • 4.#undef撤銷宏定義
    • 5.宏和函數對比(重點)
  • 二、條件編譯指令
    • 1.單分支
    • 2.多分支條件編譯
    • 3.判斷某個符號是否被定義
    • 4.嵌套指令
  • 三、#include指令
    • 1.本地文件包含
    • 2.庫文件包含
    • 3.嵌套文件包含

前言

了解敲完hello world后,編譯器是怎么處理代碼的第一步的呢,這是學習C和C++的基礎。
Hello World代碼如下。

在這里插入圖片描述
當你敲完Hello World這串代碼時。編譯器會對這些代碼進行編譯鏈接的操作。

編譯: 又分為 預處理編譯匯編
所以說 當你敲完C代碼后的第一步,編譯器會對C代碼進行預處理.

那么預處理主要做了那些事情呢?

預處理大致做了以下事情
1.定義和替換由 #define指令定義的符號
2.刪除注釋
3.確定代碼部分內容是否應該根據一些 條件編譯指令 進行編譯
4.插入被 #include指令包含的內容

所以本章詳解預處理指令 #define、#include、條件編譯指令

一、#define指令

1.#define定義宏

什么是宏?
宏的定義:#define 允許把參數替換到文本中,這種實現通常稱為定義宏

宏的聲明格式

#define NAME stuff

解釋:沒當有符號name出現在#define NAME stuff這條語句后面時,預處理器就會把它替換為 stuff。
NAME:
1.NAME是宏的名字。在這里 name 相當于變量,或者也可以相當于函數但不等于函數
2.一般NAME都是大寫,因為宏和函數語法很相似,語言本身我們無法區分,所以宏名要全部大寫
stuff:可以是常量。可以是表達式。也可以是一段程序

例如:
以下代碼在預處理后是什么樣子呢?

//定義聲明宏
//定義中我們使用了括號,這是一個好習慣,避免優先級的錯誤
#define SQUARE(x)  (x)*(x)
int main()
{
	printf("%d ", SQUARE(5));
	return 0;
}

預處理后的代碼,以下你看到的代碼是編譯器實實在在的處后的代碼。

#define SQUARE(x)  (x)*(x)
int main()
{
	//將SQUARE(5)替換為(5)*(5)
	printf("%d ", (5)*(5));
	return 0;
}

你是否還對#define 替換迷惑?請繼續往下看

2.#define 替換 宏

到底上面的代碼是怎么替換的 宏?
1.再調用宏時,首先對參數檢查,看是否包含了#define定義的符號,比如SQUARE(5),然后將它的x * x替換為5 * 5.
2.對于宏,參數名被他們的值所替代。
3.最后,再次對文本掃描,看是否包含了熱任何由#define定義的符號。如果是,就重復上述處理過程。

為什么會有第3步的重復呢?
因為有時候#define定義可以包含其他#define定義的符號。但是宏不可以遞歸

3.帶副作用的宏參數

什么是帶副作用的宏參數?
副作用:就是表達式求值的時候出現的永久性效果

例如:

x+1;//不帶副作用
x++;//帶有副作用

下面代碼輸出結果是什么?

#include 
#define ADD(a, b) (a)+(b)
int main()
{
	int x = 2; 
	int y = 3; 

	int z = ADD(x++, y++);
	//輸出的結果是什么?
	//x=3 y=4 z=5
	printf("x=%d y=%d z=%d\n", x, y, z);
	return 0;
}

因為被替換的代碼是int z = ADD(x++, y++);
替換后為:int z = (x++)+(y++);

這樣結果就一目了然。

4.#undef撤銷宏定義

#undef:這條指令用于移除一個宏定義
例如:移除MAX這個宏。
在這里插入圖片描述

5.宏和函數對比(重點)

屬性 #define定義宏 函數
代碼長度 每次使用時,宏代碼都會被插入到程序中。除了非常小的宏之外,程序的長度會大幅度增長 函數代碼只出現于一個地方。每次使用函數時,都調用同一個地方的代碼
執行速度 更快 存在函數的調用和返回 的格外開銷,所以相對慢一些
操作符優先級 宏參數求值需要加上括號,否則容易造成不可以預料的后果 只在函數調用事求值一次,不會帶副作用
帶有副作用的參數 參數可能被替換到宏的多個位置,有的可能帶有副作用 函數參數只在傳參的時候求值一次,結果更容易控制
參數類型 宏的參數與類型無關,可以是任何類型的的參數 函數參數與類型有關,參數類型不同就需要不同的函數,因為C語言沒有C++的重載
調試 宏是不可以調試的,因為在程序運行前就已經替換的宏 可以逐語句調試

二、條件編譯指令

什么是條件編譯?
意思就是我們可以選擇性的編譯。
條件編譯:你可以選擇代碼的一部分是被正常編譯還是完全忽略。用于支持條件編譯的基本結構是#if指令和與其匹配的#endif指令。

1.單分支

常量表達式expression,由預處理器求值。
如果expression為真,那么statements將被執行,否則預處理器就安靜的刪除它們。

#if expression
 statements;
#endif
//常量表達式expression,由預處理器求值。

2.多分支條件編譯

同if else語句,為真則執行。

#if expression
 //...
#elif expression
 //...
#else
 //...
#endif

3.判斷某個符號是否被定義

為了測試一個符號是否已經被定義。在條件編譯中完成這個任務更方便。

以下兩條語句功能想通過。

1.#if defined(symbol)

2.#ifdef symbol

4.嵌套指令

某個程序既要在windows系統下能夠運行,也需要在Linux系統下運行,這就要條件編譯來解決跨平臺問題。這時候嵌套指令很容易解決。

#if defined(OS_UNIX)
	 #ifdef OPTION1
			 unix_version_option1();
	 #endif
	 #ifdef OPTION2
 			unix_version_option2();
	 #endif
#elif defined(OS_MSDOS)
	 #ifdef OPTION2
 			msdos_version_option2();
	 #endif
#endif

三、#include指令

#include在預處理時會被展開。
這種展開的方式很簡單:
1.預處理器先刪除這條指令,并用**#include**所包含文件的內容替換。
2.這樣一個源文件被包含10次,那就實際被編譯10次。

1.本地文件包含

#include "Add.h"

查找方法
1.先在源文件所在目錄下查找
2.如果該頭文件未找到,編譯器就像查找庫函數頭文件一樣在標準位置查找頭文件。

2.庫文件包含

#include 

查找方法:查找頭文件直接去標準路徑下去查找,如果找不到就提示編譯錯誤。

這樣是不是可以說,對于庫文件也可以使用 “” 的形式包含?
答案是肯定的,可以。
但是這樣做查找的效率就低些,當然這樣也不容易區分是庫文件還是本地文件了。

3.嵌套文件包含

有時候會重復包含頭文件,以前為了解決這個方法,人們用了條件編譯。代碼如下
每個頭文件的開頭寫:

例如有個test.h的頭文件。用下劃線分開頭文件。全大寫。

#ifndef __TEST_H__
#define __TEST_H__
//這里面寫頭文件的內容
#endif  

上面這種寫法比較古老。
現在一般用這個寫法

#pragma once

#pragma once也是是用來防止頭文件被包含的。


原文鏈接:https://blog.csdn.net/qq2466200050/article/details/124615042

欄目分類
最近更新