網(wǎng)站首頁(yè) 編程語(yǔ)言 正文
一、程序的翻譯環(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)過(guò)編譯器的處理,最后又各自生成一個(gè)叫目標(biāo)文件的東西(windows底下目標(biāo)文件就叫做obj)
2.每個(gè)目標(biāo)文件由鏈接器(linker)捆綁在一起,形成一個(gè)單一而完整的可執(zhí)行程序。
3.鏈接器同時(shí)也會(huì)引入標(biāo)準(zhǔn)C函數(shù)庫(kù)中任何被該程序所用到的函數(shù),而且它可以搜索程序員個(gè)人 的程序庫(kù),將其需要的函數(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語(yǔ)言代碼轉(zhuǎn)換成匯編代碼,1:語(yǔ)法分析,2:詞法分析,3:語(yǔ)義分析,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)文件和鏈接庫(kù)進(jìn)行鏈接。
這個(gè)階段,1:合并段表,2:符號(hào)表的合并和重定位。
執(zhí)行環(huán)境(運(yùn)行環(huán)境)
程序的執(zhí)行過(guò)程:
1. 程序必須載入內(nèi)存中。在有操作系統(tǒng)的環(huán)境中:一般這個(gè)由操作系統(tǒng)完成。在獨(dú)立的環(huán)境中,程序 的載入必須由手工安排,也可能是通過(guò)可執(zhí)行代碼置入只讀內(nèi)存來(lái)完成。
2. 程序的執(zhí)行便開(kāi)始。接著便調(diào)用main函數(shù)。
3.開(kāi)始執(zhí)行程序代碼。這個(gè)時(shí)候程序?qū)⑹褂靡粋€(gè)運(yùn)行時(shí)堆棧(stack),存儲(chǔ)函數(shù)的局部變量和返回 地址。程序同時(shí)也可以使用靜態(tài)(static)內(nèi)存,存儲(chǔ)于靜態(tài)內(nèi)存中的變量在程序的整個(gè)執(zhí)行過(guò)程 一直保留他們的值。
4. 終止程序。正常終止main函數(shù);也有可能是意外終止。
二、預(yù)處理詳解
預(yù)定義符號(hào)
__FILE__ ? ? ?//進(jìn)行編譯的源文件
__LINE__ ? ? //文件當(dāng)前的行號(hào)
__DATE__ ? ?//文件被編譯的日期
__TIME__ ? ?//文件被編譯的時(shí)間
__STDC__ ? ?//如果編譯器遵循ANSI C,其值為1,否則未定義
舉例:寫(xiě)一個(gè)日志文件
#define _CRT_SECURE_NO_WARNINGS 1 #includeint 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)通常稱(chēng)為宏(macro)或定義 宏(define macro)。
這個(gè)問(wèn)題,的解決辦法是在宏定義表達(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. 替換文本隨后被插入到程序中原來(lái)文本的位置。對(duì)于宏,參數(shù)名被他們的值所替換。
3. 最后,再次對(duì)結(jié)果文件進(jìn)行掃描,看看它是否包含任何由#define定義的符號(hào)。如果是,就重復(fù)上 述處理過(guò)程。
注意:
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)超過(guò)一次的時(shí)候,如果參數(shù)帶有副作用,那么你在使用這個(gè)宏的時(shí)候就可能 出現(xiàn)危險(xiǎn),導(dǎo)致不可預(yù)測(cè)的后果。副作用就是表達(dá)式求值的時(shí)候出現(xiàn)的永久性效果。
例如:
x+1;//不帶副作用
x++;//帶有副作用
宏和函數(shù)對(duì)比?
宏通常被應(yīng)用于執(zhí)行簡(jiǎn)單的運(yùn)算。 比如在兩個(gè)數(shù)中找出較大的一個(gè)。
#define MAX(a, b) ((a)>(b)?(a):(b))
?那為什么不用函數(shù)來(lái)完成這個(gè)任務(wù)?
原因有二:
1. 用于調(diào)用函數(shù)和從函數(shù)返回的代碼可能比實(shí)際執(zhí)行這個(gè)小型計(jì)算工作所需要的時(shí)間更多。 所以宏比函數(shù)在程序的規(guī)模和速度方面更勝一籌。
2. 更為重要的是函數(shù)的參數(shù)必須聲明為特定的類(lèi)型。 所以函數(shù)只能在類(lèi)型合適的表達(dá)式上使用。反之這個(gè)宏怎可以適用于整形、長(zhǎng)整型、浮點(diǎn)型等可以 用于>來(lái)比較的類(lèi)型。
宏是類(lèi)型無(wú)關(guān)的。
宏的缺點(diǎn):
當(dāng)然和函數(shù)相比宏也有劣勢(shì)的地方:
1. 每次使用宏的時(shí)候,一份宏定義的代碼將插入到程序中。除非宏比較短,否則可能大幅度增加程序 的長(zhǎng)度。
2. 宏是沒(méi)法調(diào)試的?
3. 宏由于類(lèi)型無(wú)關(guān),也就不夠嚴(yán)謹(jǐn)。
4. 宏可能會(huì)帶來(lái)運(yùn)算符優(yōu)先級(jí)的問(wèn)題,導(dǎo)致程容易出現(xiàn)錯(cuò)。
#undef移除宏
?這條指令用于移除一個(gè)宏定義。
#undef NAME //如果現(xiàn)存的一個(gè)名字需要被重新定義,那么它的舊名字首先要被移除。?
代碼如下:
命令行定義
許多C 的編譯器提供了一種能力,允許在命令行中定義符號(hào)。用于啟動(dòng)編譯過(guò)程。 例如:當(dāng)我們根據(jù)同一個(gè)源文件要編譯出不同的一個(gè)程序的不同版本的時(shí)候,這個(gè)特性有點(diǎn)用處。(假 定某個(gè)程序中聲明了一個(gè)某個(gè)長(zhǎng)度的數(shù)組,如果機(jī)器內(nèi)存有限,我們需要一個(gè)很小的數(shù)組,但是另外一 個(gè)機(jī)器內(nèi)存大寫(xiě),我們需要一個(gè)數(shù)組能夠大寫(xiě)。)
代碼如下:
在linux底下,通過(guò)gcc test.c -D M=100,下面的代碼就可以正常運(yùn)行了
條件編譯?
滿(mǎn)足條件就編譯,不滿(mǎn)足條件就不參與編譯。
代碼如下:
?并不打印結(jié)果。?
#if 1為真,后面就編譯,如果為0就不編譯。?
頭文件包含?
‘頭文件包含的兩種方式:
#include"add.h" 本地文件包含,自定義的函數(shù)的頭文件用""
#include 庫(kù)文件包含,C語(yǔ)言庫(kù)中提供的函數(shù)的頭文件用<>
?查找策略:先在源文件所在目錄下查找,如果該頭文件未找到,編譯器就像查找?guī)旌瘮?shù)頭文件一樣在標(biāo) 準(zhǔn)位置查找頭文件。 如果找不到就提示編譯錯(cuò)誤
查找頭文件直接去標(biāo)準(zhǔn)路徑下去查找,如果找不到就提示編譯錯(cuò)誤。 這樣是不是可以說(shuō),對(duì)于庫(kù)文件也可以使用 “” 的形式包含? 答案是肯定的,可以。 但是這樣做查找的效率就低些,當(dāng)然這樣也不容易區(qū)分是庫(kù)文件還是本地文件了。?
嵌套文件包含?
每個(gè)頭文件的開(kāi)頭寫(xiě):
#ifndef __TEST_H__
#define __TEST_H__
//頭文件的內(nèi)容
#endif ? //__TEST_H__
或者:
#pragma once
就可以避免頭文件的重復(fù)引入。?
總結(jié)
題主太累了,總結(jié)就不寫(xiě)了,已經(jīng)過(guò)了零點(diǎn)了,要睡覺(jué)了。實(shí)在是感覺(jué)頂不住了,明天還得早起呢,希望大家也能不忘初心,堅(jiān)持學(xué)下去,大家一起努力!!!!!!?
原文鏈接:https://blog.csdn.net/m0_64397675/article/details/123034584
相關(guān)推薦
- 2022-11-14 Python實(shí)現(xiàn)腳本轉(zhuǎn)換為命令行程序_python
- 2022-07-15 SQL?Server數(shù)據(jù)表壓縮_MsSql
- 2022-09-18 詳解React?hooks組件通信方法_React
- 2022-10-13 react-router?v6實(shí)現(xiàn)動(dòng)態(tài)路由實(shí)例_React
- 2022-11-10 一文詳解Redis中的持久化_Redis
- 2023-04-12 在redis中防止消息丟失的機(jī)制_Redis
- 2022-10-17 Net?core中使用System.Drawing對(duì)上傳的圖片流進(jìn)行壓縮(示例代碼)_實(shí)用技巧
- 2022-12-06 python中的代碼運(yùn)行時(shí)間獲取方式_python
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過(guò)濾器
- Spring Security概述快速入門(mén)
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支