前言
上一篇文章–《C語言-程序環(huán)境和預處理(1)》講述了程序的翻譯環(huán)境和執(zhí)行環(huán)境,編譯、連接,預定義符號,#define,#符號和##符號的相關(guān)知識。
鏈接: 《C語言-程序環(huán)境和預處理(1)》
本篇文章,講述帶副作用的宏參數(shù),宏與函數(shù)的對比,#undef,條件編譯,文件包含的相關(guān)知識。
1.帶副作用的宏參數(shù)
我們來看一個代碼:
最后輸出的a,b,m分別是多少?
#define MAX(x,y) ((x)>(y)?(x):(y))
int main()
{
int a = 3;
int b = 5;
int m = MAX(a++, b++);
printf("%d\n", m);
printf("%d\n", a);
printf("%d\n", b);
return 0;
}
你第一次做的時候是否和我一樣,最后輸出的m是5,a是4,b是6。如果是這樣的,那么恭喜你和我一樣,做錯了。
正確的解析如下:
int m = ((a++) > (b++) ? (a++) : (b++));
3 > 5
a=4 b=6 no 6
m=6 b=7
正確答案應該是a=4,b=7,m=6.
其運算步驟應該是這個樣子的,不是一開始我們想當然的那樣,這就是帶有副作用的宏參數(shù)。
當宏參數(shù)在宏的定義中出現(xiàn)超過一次的時候,如果參數(shù)帶有副作用,那么你在使用這個宏的時候就可能出現(xiàn)危險,導致不可預測的后果。副作用就是表達式求值的時候出現(xiàn)的永久性效果。
2.宏與函數(shù)的對比
宏和函數(shù)運行時的步驟對比如下:
函數(shù)的缺點:
- 用于調(diào)用函數(shù)和從函數(shù)返回的代碼可能比實際執(zhí)行這個小型計算工作所需要的時間更多。
- 更為重要的是函數(shù)的參數(shù)必須聲明為特定的類型。所以函數(shù)只能在類型合適的表達式上使用。反之宏可以適用于整形、長整型、浮點型等可以用于比較的類型。
宏的缺點:
- 每次使用宏的時候,一份宏定義的代碼將插入到程序中。除非宏比較短,否則可能大幅度增加程序的長度。
- 宏是沒法調(diào)試的。
- 宏由于類型無關(guān),也就不夠嚴謹。
- 宏可能會帶來運算符優(yōu)先級的問題,導致程容易出現(xiàn)錯。
除此之外,宏可以完成一件函數(shù)永遠做不到的事情:
我們?nèi)绻承r候想給某個東西傳類型,那么函數(shù)顯然無法做到,比如:
開辟空間:
int*p = (int*)malloc(10 * sizeof(int));
//我們想要便捷的寫malloc(10,int),這樣函數(shù)顯然做不到,但是宏可以做到
#define MALLOC(num, type) (type*)malloc(num * sizeof(type))
int*p = MALLOC(10, int);//這樣就可以了
提示:宏是不能遞歸的??!
2.1 宏的命名約定
一般來講函數(shù)的宏的使用語法很相似。所以語言本身沒法幫我們區(qū)分二者。
那我們平時的一個習慣是:
把宏名全部大寫
函數(shù)名不要全部大寫
2.2 命令行定義
許多C 的編譯器提供了一種能力,允許在命令行中定義符號。用于啟動編譯過程。
例如:當我們根據(jù)同一個源文件要編譯出不同的一個程序的不同版本的時候,這個特性有點用處。(假定某個程序中聲明了一個某個長度的數(shù)組,如果機器內(nèi)存有限,我們需要一個很小的數(shù)組,但是另外一個機器內(nèi)存大寫,我們需要一個數(shù)組能夠大寫。)
比如在Linux,gcc編譯器中就可以這樣使用。
這個功能很少見,不常使用,只需知道即可!
3.#undef宏講解
這條指令用于移除一個宏定義。
#define M 100
int main()
{
int m = M;
printf("m = %d\n", m);
#undef M
int m = M;//錯誤,宏M已被刪除不可使用
#define M 1000
//刪除后還可以重新定義M
return 0;
}
4.條件編譯
在編譯一個程序的時候我們?nèi)绻獙⒁粭l語句(一組語句)編譯或者放棄是很方便的。因為我們有條件編譯指令。
比如說: 調(diào)試性的代碼,刪除可惜,保留又礙事,所以我們可以選擇性的編譯。
常見的條件編譯有如下:
4.1 #if #endif
int main()
{
#if 1==2
printf("hehe\n");
#endif
return 0;
}
4.2 多個分支的條件編譯
int main()
{
#if 1==1
printf("我是帥哥\n");
#elif 2==1
printf("我是帥人\n");
#elif 3==1
printf("我太帥了\n");
#else
printf("帥不可擋\n");
#endif
return 0;
}
4.3 判斷是否被定義
int main()
{
#if defined(M)
printf("snan");
#endif
return 0;
}
其還有一種寫法:
int main()
{
//#if defined(M)
// printf("snan");
//#endif
#ifdef M
printf("snan");
#endif
return 0;
}
當然還有#if !defined(M)表示如果沒有定義就怎么怎么樣
#ifndef M便是第二種寫法的如果未定義怎么怎么樣
4.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
就是#if套#if,就是稍微復雜,一條一條理清楚是不難的。
5.文件包含
5.1 頭文件的包含
頭文件的包含有兩種形式:
1.包含本地文件(自己的.h文件)
#include “xxx.h”查找策略:先在源文件所在目錄下查找,如果該頭文件未找到,編譯器就像查找?guī)旌瘮?shù)頭文件一樣在標準位置查找頭文件。如果找不到就提示編譯錯誤。
2.包含標準庫的頭文件
#include <stdio.h>查找策略:查找頭文件直接去標準路徑下去查找,如果找不到就提示編譯錯誤。
提示:#include “stdio.h”這樣寫也是沒問題的,但是會拉低代碼的運行速率,降低查找頭文件的效率?。?/code>
5.1 嵌套文件的包含
在工作中往往避免不了這樣一種情況:
comm.h和comm.c是公共模塊。
test1.h和test1.c使用了公共模塊。
test2.h和test2.c使用了公共模塊。
test.h和test.c使用了test1模塊和test2模塊。
這樣最終程序中就會出現(xiàn)兩份comm.h的內(nèi)容。這樣就造成了文件內(nèi)容的重復。
如何避免這樣的問題,提高編譯器的工作效率呢?
每個頭文件的開頭寫:
#ifndef __TEST_H__
#define __TEST_H__
//這部分是頭文件的內(nèi)容
#endif //__TEST_H__
TEST_H是根據(jù)頭文件的名字來的(不是只能寫TEST_H)
或者:
#pragma once
就可以避免頭文件的重復引入。我們在VS上創(chuàng)建一個頭文件時,它會自動輸入這句話。
6.其他預處理指令
1.#error
編譯程序時,只要遇到#error就會生成一個編譯錯誤的提示消息,并停止編譯
2.#pragma
可以設(shè)定編譯程序完成一些特定的動作(可以通過編譯程序的菜單設(shè)定,也可以直接寫在源代碼中),它允許向編譯程序傳送各種指令。例如:編譯程序可能有一種選擇,它支持對程序執(zhí)行的跟蹤,可用#pragma語句指定一個跟蹤選項
3.#line
可以改變當前行數(shù)和文件名稱,他們時在編譯程序中預先定義的標識符命令的基本形式
#line number[“filename”]4.#pragma pack()
C語言預處理指令#pragma pack()用于控制結(jié)構(gòu)體、聯(lián)合體和類成員的對齊方式。在C語言中,編譯器通常會根據(jù)特定的對齊規(guī)則將結(jié)構(gòu)體、聯(lián)合體和類成員對齊到特定的邊界上,以提高內(nèi)存訪問效率。#pragmapack()指令通過設(shè)置對齊邊界值,控制編譯器對結(jié)構(gòu)體、聯(lián)合體和類成員的對齊方式。該指令的參數(shù)是一個非負整數(shù),默認情況下通常是4或8,表示對齊邊界值為4字節(jié)或8字節(jié)。具體來說,當設(shè)置對齊邊界值為n時,編譯器會將結(jié)構(gòu)體、聯(lián)合體和類成員按照n字節(jié)對齊,即每個成員的起始地址必須是n的倍數(shù)。這可以防止因為內(nèi)存對齊不當而導致的性能下降或錯誤,尤其在與硬件交互或與其他系統(tǒng)進行通信時很重要。文章來源:http://www.zghlxwxcb.cn/news/detail-716059.html
當然還有更多的預處理指令,大家可以自己學習。文章來源地址http://www.zghlxwxcb.cn/news/detail-716059.html
到了這里,關(guān)于C語言-程序環(huán)境和預處理(2)--帶副作用的宏參數(shù),宏與函數(shù)的對比,#undef,條件編譯,文件包含的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!