真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

【輕松掌握C語言】程序環(huán)境和預(yù)處理-創(chuàng)新互聯(lián)

目錄 ? ? ? ? ? 一、程序的翻譯和執(zhí)行環(huán)境 ? ? ? ? ? ? ? 1、翻譯環(huán)境 ? ? ? ? ? ? ? 2、執(zhí)行環(huán)境 ? ? ? ? ? 二、預(yù)處理詳解 ? ? ? ? ? ? ? 1、預(yù)定義符號(hào) ? ? ? ? ? ? ? 2、#define ? ? ? ? ? ? ? 3、#undef ? ? ? ? ? ? ? 4、命令行定義 ? ? ? ? ? ? ? 5、條件編譯 ? ? ? ? ? ? ? 6、文件包含

成都創(chuàng)新互聯(lián)公司-專業(yè)網(wǎng)站定制、快速模板網(wǎng)站建設(shè)、高性價(jià)比津南網(wǎng)站開發(fā)、企業(yè)建站全套包干低至880元,成熟完善的模板庫(kù),直接使用。一站式津南網(wǎng)站制作公司更省心,省錢,快速模板網(wǎng)站建設(shè)找我們,業(yè)務(wù)覆蓋津南地區(qū)。費(fèi)用合理售后完善,十年實(shí)體公司更值得信賴。

一、程序的翻譯和執(zhí)行環(huán)境

1、翻譯環(huán)境,在這個(gè)環(huán)境中源代碼被轉(zhuǎn)換為可執(zhí)行的機(jī)器指令。

2、執(zhí)行環(huán)境,它用于實(shí)際執(zhí)行代碼。

1、翻譯環(huán)境

翻譯環(huán)境的主要目標(biāo)是把 test.c 轉(zhuǎn)化為 test.exe,其中 test.c 需要先經(jīng)過預(yù)編譯轉(zhuǎn)為 test.i ,然后再經(jīng)過編譯轉(zhuǎn)為 test.s ,接著經(jīng)過匯編翻譯為 test.o ,最后再由鏈接器將目標(biāo)文件 test.o 與其他目標(biāo)文件、庫(kù)函數(shù)文件等鏈接后生成可執(zhí)行程序 test.exe。其中前三步由編譯器完成,最后一步由鏈接器完成(這兩個(gè)工具已經(jīng)集成于VS中了),每個(gè)不同的源文件都需要分開編譯,最后由鏈接器合并。

1、組成一個(gè)程序的每個(gè)源文件通過編譯過程分別轉(zhuǎn)換成目標(biāo)代碼(object code)。

2、每個(gè)目標(biāo)文件由鏈接器(linker)捆綁在一起,形成一個(gè)單一而完整的可執(zhí)行程序。

3、鏈接器同時(shí)也會(huì)引入標(biāo)準(zhǔn)C函數(shù)庫(kù)中任何被該程序所用到的函數(shù),而且它可以搜索程序員個(gè)人 的程序庫(kù),將其需要的函數(shù)也鏈接到程序中。

2、執(zhí)行環(huán)境

程序執(zhí)行的過程:

1、程序必須載入內(nèi)存中。在有操作系統(tǒng)的環(huán)境中:一般這個(gè)由操作系統(tǒng)完成。在獨(dú)立的環(huán)境中,程序 的載入必須由手工安排,也可能是通過可執(zhí)行代碼置入只讀內(nèi)存來完成。

2、程序的執(zhí)行便開始。接著便調(diào)用main函數(shù)。

3、開始執(zhí)行程序代碼。這個(gè)時(shí)候程序?qū)⑹褂靡粋€(gè)運(yùn)行時(shí)堆棧(stack),存儲(chǔ)函數(shù)的局部變量和返回 地址。程序同時(shí)也可以使用靜態(tài)(static)內(nèi)存,存儲(chǔ)于靜態(tài)內(nèi)存中的變量在程序的整個(gè)執(zhí)行過程 一直保留他們的值。

4、終止程序。正常終止main函數(shù);也有可能是意外終止。

二、預(yù)處理詳解 ? 1、預(yù)定義符號(hào)

1、__FILE__? ? ? ?//進(jìn)行編譯的源文件

2、__LINE__? ? ? //文件當(dāng)前的行號(hào)

3、__DATE__? ? ?//文件被編譯的日期

4、__TIME__? ? ? //文件被編譯的時(shí)間

5、__STDC__? ? ?//如果編譯器遵循ANSI C,其值為1,否則未定義

使用方法如下:

int main()
{
	printf("%s\n", __FILE__);   //進(jìn)行編譯的源文件
	printf("%d\n", __LINE__);   //當(dāng)前的語句所在行數(shù)
	printf("%s\n", __DATE__);   //當(dāng)前編譯運(yùn)行的日期
	printf("%s\n", __TIME__);   //當(dāng)前編譯運(yùn)行的時(shí)間
	return 0;
}

運(yùn)行如下:

2、#define

?(1)#define定義標(biāo)識(shí)符

語法:

#define name stuff

#define MAX 1000

#define reg register? ? ? ? ? ? ? //為 register這個(gè)關(guān)鍵字,創(chuàng)建一個(gè)簡(jiǎn)短的名字

#define do_forever for(;;) ? ? //用更形象的符號(hào)來替換一種實(shí)現(xiàn)

#define CASE break;case ? ? ? ?//在寫case語句的時(shí)候自動(dòng)把 break寫上。

// 如果定義的 stuff過長(zhǎng),可以分成幾行寫,除了最后一行外,每行的后面都加一個(gè)反斜杠(續(xù)行符)。

#define DEBUG_PRINT printf("file:%s\tline:%d\t \ ? ? ? ? ? ? ? ? ? ? ? ? ?

date:%s\ttime:%s\n" ,\

__FILE__,__LINE__ , ? ? ? \

__DATE__,__TIME__ )

用法如下:

?(2)#define定義宏

#define 機(jī)制包括了一個(gè)規(guī)定,允許把參數(shù)替換到文本中,這種實(shí)現(xiàn)通常稱為宏(macro)或定義 宏(define macro)。

語法:

#define name( parament-list ) stuff

name是指定義宏的名稱,后面括號(hào)內(nèi)的符號(hào)表是在定義內(nèi)容過程中所使用的符號(hào)。stuff是宏背后所定義的內(nèi)容。

注意:

參數(shù)列表的左括號(hào)必須與name緊鄰。

如果兩者之間有任何空白存在,參數(shù)列表就會(huì)被解釋為stuff的一部分。

用法如下:

注意:?我們?cè)诙x宏的過程中,對(duì)數(shù)值表達(dá)式進(jìn)行求值的宏定義都應(yīng)該用這種方式加上括號(hào),避免在使用宏時(shí)由于參數(shù)中的操作符或鄰近操作符之間不可預(yù)料的相互作用。

?(3)#define替換規(guī)則

在程序中擴(kuò)展#define定義符號(hào)和宏時(shí),需要涉及幾個(gè)步驟。

1、在調(diào)用宏時(shí),首先對(duì)參數(shù)進(jìn)行檢查,看看是否包含任何由#define定義的符號(hào)。如果是,它們首先被替換。

2、替換文本隨后被插入到程序中原來文本的位置。對(duì)于宏,參數(shù)名被他們的值替換。

3、最后,再次對(duì)結(jié)果文件進(jìn)行掃描,看看它是否包含任何由#define定義的符號(hào)。如果是,就重復(fù)上 述處理過程。

注意:?

1、宏參數(shù)和#define 定義中可以出現(xiàn)其他#define定義的變量。但是對(duì)于宏,不能出現(xiàn)遞歸。

2、當(dāng)預(yù)處理器搜索#define定義的符號(hào)的時(shí)候,字符串常量的內(nèi)容并不被搜索。

?(4)#和##

#可以把一個(gè)宏參數(shù)變成對(duì)應(yīng)的字符串。

首先我們看看這樣的代碼:

char* p = "hello ""world\n";
printf("hello", " world\n");
printf("%s", p);

這里輸出的是hello world,所以我們發(fā)現(xiàn)字符串是有自動(dòng)連接的特點(diǎn)。

下面看一段代碼:

#define PRINT(FORMAT,VALUE) printf("the value is "#FORMAT"\n",VALUE)
int main()
{
	PRINT("%d", 10);
	return 0;
}

運(yùn)行結(jié)果如下:

我們發(fā)現(xiàn),將#和FORMAT連在一起,轉(zhuǎn)換成了一個(gè)字符串,上面的代碼就能轉(zhuǎn)換成下面:

printf("the value is ""FORMAT"" \n",VALUE)

下面舉個(gè)例子能更好的理解

int i = 10;
#define PRINT(FORMAT,VALUE) printf("the value of "#VALUE" is "FORMAT"\n", VALUE)
int main()
{
	PRINT("%d", i + 3);
	return 0;
}

運(yùn)行結(jié)果如下:

首先進(jìn)行宏參數(shù)替換為printf("the value of "#i+3" is ""%d""\n",10+2)?

根據(jù)#號(hào)的作用將i+3這個(gè)宏參數(shù)變成字符串"i+3"

最后輸出結(jié)果就為the value of? i+3? is? 13。

## 的作用是可以把位于它兩邊的符號(hào)合成一個(gè)符號(hào)。

它允許宏定義從分離的文本片段創(chuàng)建標(biāo)識(shí)符。

舉個(gè)例子:

#define ADD_TO_SUM(n, value) sum##n += value;
int main()
{
	int sum5 = 3;
	ADD_TO_SUM(5, 3);
	printf("%d ", sum5);//輸出結(jié)果為什么
	return 0;
}

運(yùn)行如下:

首先進(jìn)行宏參數(shù)替換為sum##5 += 3 就等同于sum##5 = sum##5 + 3

根據(jù)##的作用將兩邊的符號(hào)合成一個(gè)符號(hào),為sum5 = sum5 + 3

所以結(jié)果為6。

?(5)帶副作用的宏參數(shù)

當(dāng)宏參數(shù)在宏的定義中出現(xiàn)超過一次的時(shí)候,如果參數(shù)帶有副作用,那么你在使用這個(gè)宏的時(shí)候就可能 出現(xiàn)危險(xiǎn),導(dǎo)致不可預(yù)測(cè)的后果。副作用就是表達(dá)式求值的時(shí)候出現(xiàn)的永久性效果。

例如:

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

x++;? ? ? ? ? //帶有副作用

舉個(gè)例子:

#define MAX(a, b) ( (a) >(b) ? (a) : (b) )
int main()
{
	int x = 5;
	int y = 8;
	int z = MAX(x++, y++);
	printf("x=%d y=%d z=%d\n", x, y, z);//輸出的結(jié)果是什么?
	return 0;
}

運(yùn)行結(jié)果:

首先進(jìn)行宏參數(shù)的替換為z = ((x++) >(y++) ? (x++) : (y++));

即在使用宏的過程中,由于宏是完全替換的,所以出現(xiàn)了多次++的現(xiàn)象。所以在使用宏的時(shí)候,我們需要謹(jǐn)慎地考慮。

?(6)宏和函數(shù)對(duì)比

宏通常被應(yīng)用于執(zhí)行簡(jiǎn)單的運(yùn)算。比如在兩個(gè)數(shù)中找出較大的一個(gè)。

#define MAX(a, b) ((a)>(b)?(a):(b))

為什么不用函數(shù)來完成這個(gè)任務(wù)?

原因有兩點(diǎn):

1、用于調(diào)用函數(shù)和從函數(shù)返回的代碼可能比實(shí)際執(zhí)行這個(gè)小型計(jì)算工作所需要的時(shí)間更多。所以宏比函數(shù)在程序的規(guī)模和速度方面更勝一籌。

2、更為重要的是函數(shù)的參數(shù)必須聲明為特定的類型。所以函數(shù)只能在類型合適的表達(dá)式上使用。反之 這個(gè)宏怎可以適用于整形、長(zhǎng)整型、浮點(diǎn)型等可以用于>來比較的類型。宏是類型無關(guān)的。

但是宏與函數(shù)比也有自己的缺點(diǎn):?

1、每次使用宏的時(shí)候,一份宏定義的代碼將插入到程序中。除非宏比較短,否則可能大幅度增加程序的長(zhǎng)度。

2、宏是沒法調(diào)試的。

3、宏由于類型無關(guān),也就不夠嚴(yán)謹(jǐn)。

4、宏可能會(huì)帶來運(yùn)算符優(yōu)先級(jí)的問題,導(dǎo)致程容易出現(xiàn)錯(cuò)。

宏有時(shí)候可以做函數(shù)做不到的事情。比如:宏的參數(shù)可以出現(xiàn)類型,但是函數(shù)做不到。

#define MALLOC(num, type) (type *)malloc(num * sizeof(type))
...
//使用
MALLOC(10, int);//類型作為參數(shù)
//預(yù)處理器替換之后:
(int*)malloc(10 * sizeof(int));  //而函數(shù)做不到這樣

在這里我將宏和函數(shù)做了一個(gè)鮮明的對(duì)比:

屬性#define定義宏函數(shù)
代碼長(zhǎng)度每次使用時(shí),宏代碼都會(huì)被插入到程序中。除了非常 小的宏之外,程序的長(zhǎng)度會(huì)大幅度增長(zhǎng)函數(shù)代碼只出現(xiàn)于一個(gè)地方;每 次使用這個(gè)函數(shù)時(shí),都調(diào)用那個(gè) 地方的同一份代碼
執(zhí)行速度更快存在函數(shù)的調(diào)用和返回的額外開 銷,所以相對(duì)慢一些
操作符優(yōu)先級(jí)宏參數(shù)的求值是在所有周圍表達(dá)式的上下文環(huán)境里, 除非加上括號(hào),否則鄰近操作符的優(yōu)先級(jí)可能會(huì)產(chǎn)生 不可預(yù)料的后果,所以建議宏在書寫的時(shí)候多些括 號(hào)函數(shù)參數(shù)只在函數(shù)調(diào)用的時(shí)候求 值一次,它的結(jié)果值傳遞給函 數(shù)。表達(dá)式的求值結(jié)果更容易預(yù) 測(cè)。
帶有副作用的參數(shù)參數(shù)可能被替換到宏體中的多個(gè)位置,所以帶有副作 用的參數(shù)求值可能會(huì)產(chǎn)生不可預(yù)料的結(jié)果。函數(shù)參數(shù)只在傳參的時(shí)候求值一 次,結(jié)果更容易控制。
參數(shù)類型宏的參數(shù)與類型無關(guān),只要對(duì)參數(shù)的操作是合法的, 它就可以使用于任何參數(shù)類型。函數(shù)的參數(shù)是與類型有關(guān)的,如 果參數(shù)的類型不同,就需要不同 的函數(shù),即使他們執(zhí)行的任務(wù)是 不同的。
調(diào)試宏是不方便調(diào)試的函數(shù)是可以逐語句調(diào)試的
遞歸宏是不能遞歸的函數(shù)是可以遞歸的

命名約定:一般來講函數(shù)和宏的使用語法很相似。所以語言本身沒法幫我們區(qū)分二者。 那我們平時(shí)的一個(gè)習(xí)慣是:

把宏名全部大寫

函數(shù)名不要全部大寫

3、#undef

這條指令用于移除一個(gè)宏定義。

#undef NAME? ?

//如果現(xiàn)存的一個(gè)名字需要被重新定義,那么它的舊名字首先要被移除。

4、命令行定義

許多C 的編譯器提供了一種能力,允許在命令行中定義符號(hào)。用于啟動(dòng)編譯過程。

例如:當(dāng)我們根據(jù)同一個(gè)源文件要編譯出不同的一個(gè)程序的不同版本的時(shí)候,這個(gè)特性有點(diǎn)用處。(假定某個(gè)程序中聲明了一個(gè)某個(gè)長(zhǎng)度的數(shù)組,如果機(jī)器內(nèi)存有限,我們需要一個(gè)很小的數(shù)組,但是另外一 個(gè)機(jī)器內(nèi)存大寫,我們需要一個(gè)數(shù)組能夠大寫。)

#includeint main()
{
    int array[ARRAY_SIZE];
    int i = 0;
    for (i = 0; i< ARRAY_SIZE; i++)
    {
        array[i] = i;
    }
    for (i = 0; i< ARRAY_SIZE; i++)
    {
        printf("%d ", array[i]);
    }
    printf("\n");
    return 0;
}

VS2022中不好觀察,大家可以在Linux平臺(tái)下去觀察,

編譯指令:gcc? -D? ARRAY_SIZE=10? programe.c?

5、條件編譯

在編譯一個(gè)程序的時(shí)候我們?nèi)绻獙⒁粭l語句(一組語句)編譯或者放棄是很方便的。因?yàn)槲覀冇袟l件 編譯指令。

比如說:

調(diào)試性的代碼,刪除可惜,保留又礙事,所以我們可以選擇性的編譯。

舉例說明一下:

#include#define __DEBUG__
int main()
{
	int i = 0;
	int arr[10] = { 0 };
	for (i = 0; i< 10; i++)
	{
		arr[i] = i;
#ifdef __DEBUG__
		printf("%d\n", arr[i]);//為了觀察數(shù)組是否賦值成功。
#endif //__DEBUG__
	}
	return 0;
}

上面的意思就是如果__DEBUG__定義了,就執(zhí)行printf("%d\n",arr[i])語句,如果沒有定義就不執(zhí)行。

常見的條件編譯指令:

1.
#if 常量表達(dá)式
//...
#endif
//常量表達(dá)式由預(yù)處理器求值。
如:
#define __DEBUG__ 1
#if __DEBUG__
//..
#endif

2.多個(gè)分支的條件編譯
#if 常量表達(dá)式
//...
#elif 常量表達(dá)式
//...
#else
//...
#endif

3.判斷是否被定義
#if defined(symbol)
#ifdef symbol
#if !defined(symbol)
#ifndef symbol

4.嵌套指令
#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
6、文件包含

我們已經(jīng)知道, #include 指令可以使另外一個(gè)文件被編譯。就像它實(shí)際出現(xiàn)于 #include 指令的地方 一樣。

這種替換的方式很簡(jiǎn)單: 預(yù)處理器先刪除這條指令,并用包含文件的內(nèi)容替換。 這樣一個(gè)源文件被包含10次,那就實(shí)際被編譯10次。

? (1)頭文件被包含的方式

本地文件包含:

#include "filename"

查找策略:先在源文件所在目錄下查找,如果該頭文件未找到,編譯器就像查找?guī)旌瘮?shù)頭文件一樣在標(biāo)準(zhǔn)位置查找頭文件。 如果找不到就提示編譯錯(cuò)誤。

Linux環(huán)境的標(biāo)準(zhǔn)頭文件的路徑:

/usr/include

VS環(huán)境的標(biāo)準(zhǔn)頭文件的路徑:

C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include

庫(kù)文件包含:

#include?

查找頭文件直接去標(biāo)準(zhǔn)路徑下去查找,如果找不到就提示編譯錯(cuò)誤。對(duì)于庫(kù)文件也可以使用 “ ” 的形式包含,?但是這樣做查找的效率就低些,當(dāng)然這樣也不容易區(qū)分是庫(kù)文件還是本地文件了。?

? (2)嵌套文件包含

如果出現(xiàn)一下場(chǎng)景:

如何解決這個(gè)問題呢?

答案是:條件編譯。

每個(gè)頭文件的開頭寫:

#ifndef __TEST_H__

#define __TEST_H__

//頭文件的內(nèi)容

#endif ? //__TEST_H__

或者是:

#pragma once

這樣就可以避免頭文件的重復(fù)引入。


本文要是有不足的地方,歡迎大家在下面評(píng)論,我會(huì)在第一時(shí)間更正。

老鐵們,記著點(diǎn)贊加關(guān)注哦!!!

你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機(jī)房具備T級(jí)流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級(jí)服務(wù)器適合批量采購(gòu),新人活動(dòng)首月15元起,快前往官網(wǎng)查看詳情吧


網(wǎng)頁(yè)標(biāo)題:【輕松掌握C語言】程序環(huán)境和預(yù)處理-創(chuàng)新互聯(lián)
當(dāng)前鏈接:http://weahome.cn/article/pispd.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部