最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C語言程序環(huán)境和預(yù)處理詳解分析

 更新時(shí)間:2022年03月15日 16:47:32   作者:K穩(wěn)重  
大家有沒有想過,在vs2019的編譯器上只要按下Ctrl+F5,一個(gè)test.c的源程序就能變成一個(gè).exe的可執(zhí)行程序,這其中是如何通過編譯產(chǎn)生的呢,本章就和大家一起把其中的知識(shí)和重點(diǎn)的預(yù)處理一起學(xué)習(xí)一下

一、程序的翻譯環(huán)境和運(yùn)行環(huán)境

重點(diǎn):任何ANSI C(標(biāo)準(zhǔn)C的程序)的一種實(shí)現(xiàn),存在兩個(gè)不同的環(huán)境

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

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

程序的翻譯環(huán)境

翻譯環(huán)境就是我們要做的編譯和鏈接的這個(gè)動(dòng)作。

1.每個(gè).c文件都各自獨(dú)立經(jīng)過編譯器的處理,最后又各自生成一個(gè)叫目標(biāo)文件的東西(windows底下目標(biāo)文件就叫做obj)

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

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

 編譯又分為三個(gè)步驟:

預(yù)編譯(預(yù)處理):gcc test.c -E(指令)預(yù)處理后就會(huì)停止,gcc test .c -E >test.i輸出重定向到test.i這個(gè)文件 。這個(gè)階段,

1:完成了頭文件的包含,比如#include這樣指令的預(yù)處理,

2:#define定義的符號(hào)和宏的替換,

3:注釋刪除。這些都屬于文本操作。

編譯:gcc test .i -S生成 test.s文件,這階段把C語言代碼轉(zhuǎn)換成匯編代碼,1:語法分析,2:詞法分析,3:語義分析,4:符號(hào)匯總。 

 匯編:對(duì).s文件進(jìn)行匯編,gcc test .s - c(指令)生成test.o文件(.o文件在windows叫test.obj),這階段把匯編代碼轉(zhuǎn)換成了機(jī)器指令(二進(jìn)制指令),生成符號(hào)表

鏈接階段

把多個(gè)目標(biāo)文件和鏈接庫進(jìn)行鏈接。

這個(gè)階段,1:合并段表,2:符號(hào)表的合并和重定位。

執(zhí)行環(huán)境(運(yùn)行環(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ù)處理詳解

預(yù)定義符號(hào)

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

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

__DATE__    //文件被編譯的日期

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

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

舉例:寫一個(gè)日志文件

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
 
int main()
{
	int i = 0;
	FILE* pf = fopen("log.txt", "a+");
	if (NULL == pf)
	{
		perror("fopen\n");
		return 1;
	}
	for (i = 0; i < 10; i++)
	{
		fprintf(pf, "%s %d %s %s %d\n", __FILE__, __LINE__, __DATE__, __TIME__, i);
	}
	return 0;
 
}

文件內(nèi)容:

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

舉例:

#define定義宏

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

這個(gè)問題,的解決辦法是在宏定義表達(dá)式兩邊加上一對(duì)括號(hào)就可以了。

#define DOUBLE( x)   ( ( x ) + ( x ) )

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

#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定義的符號(hào)。但是對(duì)于宏,不能出現(xiàn)遞歸。 2. 當(dāng)預(yù)處理器搜索#define定義的符號(hào)的時(shí)候,字符串常量的內(nèi)容并不被搜索。

#和##兩個(gè)預(yù)處理的工具

#可以把參數(shù)插入到字符串中 

##可以把位于它兩邊的符號(hào)合成一個(gè)符號(hào)。 它允許宏定義從分離的文本片段創(chuàng)建標(biāo)識(shí)符。 

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

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

例如:

x+1;//不帶副作用

x++;//帶有副作用

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

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

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

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

原因有二:

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

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

宏是類型無關(guān)的。

宏的缺點(diǎn):

當(dāng)然和函數(shù)相比宏也有劣勢(shì)的地方:

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

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

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

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

#undef移除宏

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

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

代碼如下:

命令行定義

許多C 的編譯器提供了一種能力,允許在命令行中定義符號(hào)。用于啟動(dòng)編譯過程。 例如:當(dāng)我們根據(jù)同一個(gè)源文件要編譯出不同的一個(gè)程序的不同版本的時(shí)候,這個(gè)特性有點(diǎn)用處。(假 定某個(gè)程序中聲明了一個(gè)某個(gè)長度的數(shù)組,如果機(jī)器內(nèi)存有限,我們需要一個(gè)很小的數(shù)組,但是另外一 個(gè)機(jī)器內(nèi)存大寫,我們需要一個(gè)數(shù)組能夠大寫。)

代碼如下:

在linux底下,通過gcc test.c -D M=100,下面的代碼就可以正常運(yùn)行了

條件編譯 

滿足條件就編譯,不滿足條件就不參與編譯。

代碼如下:

 并不打印結(jié)果。 

#if 1為真,后面就編譯,如果為0就不編譯。 

頭文件包含 

‘頭文件包含的兩種方式:

#include"add.h" 本地文件包含,自定義的函數(shù)的頭文件用""

#include<add.h> 庫文件包含,C語言庫中提供的函數(shù)的頭文件用<>

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

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

嵌套文件包含 

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

#ifndef __TEST_H__

#define __TEST_H__

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

#endif   //__TEST_H__

或者:

#pragma once

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

總結(jié)

題主太累了,總結(jié)就不寫了,已經(jīng)過了零點(diǎn)了,要睡覺了。實(shí)在是感覺頂不住了,明天還得早起呢,希望大家也能不忘初心,堅(jiān)持學(xué)下去,大家一起努力!?。。。?! 

到此這篇關(guān)于C語言程序環(huán)境和預(yù)處理詳解分析的文章就介紹到這了,更多相關(guān)C語言 預(yù)處理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C++項(xiàng)目求Fibonacci數(shù)列的參考解答

    C++項(xiàng)目求Fibonacci數(shù)列的參考解答

    今天小編就為大家分享一篇關(guān)于C++項(xiàng)目求Fibonacci數(shù)列的參考解答,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧
    2019-02-02
  • C++深入探究list的模擬實(shí)現(xiàn)

    C++深入探究list的模擬實(shí)現(xiàn)

    list相較于vector來說會(huì)顯得復(fù)雜,它的好處是在任意位置插入,刪除都是一個(gè)O(1)的時(shí)間復(fù)雜度,本文主要介紹了C++中List的模擬實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07
  • C語言中QString與QByteArray互相轉(zhuǎn)換的方法

    C語言中QString與QByteArray互相轉(zhuǎn)換的方法

    本文主要介紹了C語言中QString與QByteArray互相轉(zhuǎn)換的方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-05-05
  • 深入淺析 C++ 調(diào)用 Python 模塊

    深入淺析 C++ 調(diào)用 Python 模塊

    Python 提供了 C++ 庫,使得開發(fā)者能很方便地從 C++ 程序中調(diào)用 Python 模塊。接下來通過本文給大家介紹 C++ 調(diào)用 Python 模塊的相關(guān)知識(shí),需要的朋友參考下吧
    2016-03-03
  • 詳解C語言中的函數(shù)、數(shù)組與指針

    詳解C語言中的函數(shù)、數(shù)組與指針

    這篇文章主要介紹了C語言中的函數(shù)、數(shù)組與指針,本文給大家介紹的非常詳細(xì),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2017-02-02
  • 深入理解堆排序及其分析

    深入理解堆排序及其分析

    本篇文章是對(duì)堆排進(jìn)行了詳細(xì)的分析以及介紹,需要的朋友參考下
    2013-05-05
  • C語言二叉排序樹的創(chuàng)建,插入和刪除

    C語言二叉排序樹的創(chuàng)建,插入和刪除

    本文主要介紹了Java實(shí)現(xiàn)二叉排序樹的查找、插入、刪除、遍歷等內(nèi)容。具有很好的參考價(jià)值,下面跟著小編一起來看下吧
    2021-10-10
  • C語言實(shí)現(xiàn)單鏈表的基本操作分享

    C語言實(shí)現(xiàn)單鏈表的基本操作分享

    單鏈表是一種鏈?zhǔn)酱嫒〉臄?shù)據(jù)結(jié)構(gòu),用一組地址任意的存儲(chǔ)單元存放線性表中的數(shù)據(jù)元素。本文將為大家介紹C語言中單鏈表的基本操作,需要的可以參考一下
    2022-10-10
  • Qt通過圖片組繪制動(dòng)態(tài)圖片

    Qt通過圖片組繪制動(dòng)態(tài)圖片

    這篇文章主要為大家詳細(xì)介紹了Qt通過圖片組繪制動(dòng)態(tài)圖片,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-07-07
  • C++異步數(shù)據(jù)交換實(shí)現(xiàn)方法介紹

    C++異步數(shù)據(jù)交換實(shí)現(xiàn)方法介紹

    這篇文章主要介紹了C++異步數(shù)據(jù)交換實(shí)現(xiàn)方法,異步數(shù)據(jù)交換,除了阻塞函數(shù) send() 和 recv() 之外,Boost.MPI 還支持與成員函數(shù) isend() 和 irecv() 的異步數(shù)據(jù)交換
    2022-11-11

最新評(píng)論

黄大仙区| 正宁县| 江华| 灵山县| 洱源县| 内江市| 临高县| 灵石县| 新晃| 聂荣县| 呼图壁县| 棋牌| 宿州市| 苏尼特右旗| 嘉义市| 天峻县| 绵竹市| 定远县| 监利县| 历史| 泗阳县| 庄浪县| 大港区| 夹江县| 马鞍山市| 沽源县| 襄樊市| 东宁县| 刚察县| 凯里市| 宁安市| 望都县| 洛阳市| 淅川县| 木兰县| 遵义市| 新绛县| 东至县| 巴青县| 涿州市| 武隆县|