百度錄入網(wǎng)站怎樣建立網(wǎng)站平臺
1.預(yù)定義符號
C語言設(shè)置了一些預(yù)定義符號,可以直接使用,預(yù)定義符號也是在預(yù)處理期間處理的
__FILE__ //進(jìn)?編譯的源?件
__LINE__ //?件當(dāng)前的?號
__DATE__ //?件被編譯的?期
__TIME__ //?件被編譯的時間
__STDC__ //如果編譯器遵循ANSI C,其值為1,否則未定義
例子:
printf("file:%s line:%d\n", __FILE__, __LINE__);
2.define定義常量
基本語法:
#define name stuff
例子:
#define MAX 1000
#define reg register //為 register這個關(guān)鍵字,創(chuàng)建?個簡短的名字
#define do_forever for(;;) //?更形象的符號來替換?種實現(xiàn)
#define CASE break;case //在寫case語句的時候?動把 break寫上。
// 如果定義的 stuff過?,可以分成??寫,除了最后??外,每?的后?都加?個反斜杠(續(xù)?符)。
#define DEBUG_PRINT printf("file:%s\tline:%d\t \date:%s\ttime:%s\n" ,\__FILE__,__LINE__ , \__DATE__,__TIME__ )
思考:在define定義標(biāo)識符的時候,要不要在最后加上;
?
#define MAX 1000;
#define MAX 1000
建議不要加上;,這樣容易導(dǎo)致問題
比如:
if(condition)max = MAX;
elsemax = 0;
如果加了分號的情況,等替換之后,ij和else之間就是2條語句,而沒有大括號的時候,if后邊只能有一條語句,這里會出現(xiàn)語法錯誤
3.#define定義宏
#define機(jī)制包括了一個規(guī)定,允許把參數(shù)替換到文本中,這種實現(xiàn)通常稱為宏(macro)或者定義宏(define macro)
申明方式
#define name( parament-list ) stuff
其中parament-list
是一個由逗號隔開的符號表,它們可能出現(xiàn)在stuff中
- 注:參數(shù)列表的左括號必須與name緊鄰,如果兩者之間有任何空白存在,參數(shù)列表就會被解釋為stuff的一部分
舉例:
#define SQUARE( x ) x * x
這個宏接受收一個參數(shù)x
,如果在上述聲明之后,把SQUARE(5);
置于程序走中,預(yù)處理器就會用下面的表達(dá)式替換前5*5
警告:
這個宏存在一個問題
例子1:
//觀察下?的代碼段:
int a = 5;
printf("%d\n" ,SQUARE( a + 1) );//這里的參數(shù)x會被替換成a+1,所以這條語句實際上變成了
printf ("%d\n",a + 1 * a + 1 );//替換產(chǎn)生的表達(dá)式?jīng)]有按照預(yù)想的次序進(jìn)行求值
//在宏定義上加上兩個括號,這個問題便輕松的解決了:
#define SQUARE(x) (x) * (x)//這樣預(yù)處理之后就產(chǎn)?了預(yù)期的效果:
printf ("%d\n",(a + 1) * (a + 1) );
例子2:
//2
#define DOUBLE(x) (x) + (x)//定義中我們使?了括號,想避免之前的問題,但是這個宏可能會出現(xiàn)新的錯誤。
int a = 5;
printf("%d\n" ,10 * DOUBLE(a));//看上去,好像打印100,但事實上打印的是55.我們發(fā)現(xiàn)替換之后:
printf ("%d\n",10 * (5) + (5));//乘法運算先于宏定義的加法,所以出現(xiàn)了 55 .
//這個問題,的解決辦法是在宏定義表達(dá)式兩邊加上?對括號就可以了。
#define DOUBLE( x) ( ( x ) + ( x ) )
提示:用于對數(shù)值表達(dá)式進(jìn)行求值的宏定義應(yīng)該加上合適的括號,避免在使用宏時由于參數(shù)中的操作符或者鄰近操作符之間不可預(yù)料的相互作用
4.帶有副作用的宏參數(shù)
當(dāng)宏參數(shù)在宏的定義中出現(xiàn)超過一次的時候,如果參數(shù)帶有副作用,在使用這個宏時會出現(xiàn)危險我,導(dǎo)致不可預(yù)測的后果,副作用就是表達(dá)式求值時出現(xiàn)的永久性效果
例如:
x+1;//不帶副作?
x++;//帶有副作?
MAX宏可以證明具有副作用參數(shù)引起的問題
#define MAX(a, b) ( (a) > (b) ? (a) : (b) )
...
x = 5;
y = 8;
z = MAX(x++, y++);
printf("x=%d y=%d z=%d\n", x, y, z);//輸出的結(jié)果是什么?
//z = ( (x++) > (y++) ? (x++) : (y++))
//所以輸出的結(jié)果是:x=6 y=10 z=9
5.宏替換的規(guī)則
在程序中擴(kuò)展#define定義符號和宏時,需要涉及幾個步驟
1.調(diào)用宏時,首先對參數(shù)進(jìn)行檢查,看看是否包含任何由#define定義的符號,如果是,首先被替換
2.替換文本隨后被插入到程序中原來文本的位置,對于宏,參數(shù)名被他們的值所替換
3.最后,再次對結(jié)果文件掃描,看看它是否包含任何由#define定義的符號,如果是,重復(fù)上面過程
注意:
1.宏參數(shù)和#define定義中可以出現(xiàn)其他#define定義的符號,但是對于宏,不能出現(xiàn)遞歸
2.當(dāng)預(yù)處理器搜索#define定義的符號時,字符串常量的內(nèi)容并不被搜索
6.宏函數(shù)的對比
宏通常被應(yīng)用于執(zhí)行簡單的運算
?如在兩個數(shù)中找出較?的?個時,寫成下?的宏,更有優(yōu)勢?些。
#define MAX(a, b) ((a)>(b)?(a):(b))
為什么不用函數(shù)來完成?
1.用于調(diào)用函數(shù)的和從函數(shù)返回的代碼可能比實際這個小型計算工作所需要的時間更多,所以宏比函數(shù)在程序的規(guī)模和速度方面更勝一籌
2.更為重要的是函數(shù)的參數(shù)必須聲明為特定的類型,所以函數(shù)只能在類型合適的表達(dá)式上使用,反之這個宏可以適用于整形型,長整形型,浮點型等可以用>
來比較的類型,宏是類型無關(guān)的
和函數(shù)相比宏的優(yōu)勢:
1.每次使用宏的時候,一份宏定義的代碼被插入到程序中,除非宏比較短,否則可能大幅度增加程序的長度
2.宏是沒法調(diào)試的
3.宏由于類型無關(guān),不夠嚴(yán)謹(jǐn)
4.宏可能會帶來運算符優(yōu)先級問題,導(dǎo)致程序容易出錯
宏有時可以做到函數(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ù)的?個對?
7.#和##
7.1#運算符
#運算符將宏的一個參數(shù)轉(zhuǎn)換為字符串字面量,它僅允許出現(xiàn)在帶參數(shù)的宏替換列表中
#運算符所執(zhí)行的操作可以理解為“字符串化”
有變量int a=10的時候,想打印the value of a is 10
#define PRINT(n) printf("the value of "#n " is %d", n);
當(dāng)我們按照下?的?式調(diào)?的時候:
PRINT(a);//當(dāng)我們把a(bǔ)替換到宏的體內(nèi)時,就出現(xiàn)了#a,?#a就是轉(zhuǎn)換為"a",時?個字符串
代碼就會被預(yù)處理為:
printf("the value of ""a" " is %d", a);
運?代碼就能在屏幕上打印:
the value of a is 10
7.2##運算符
##
可以把位于它兩邊的符號合成一個符號,它允許宏定義從分離的文本片段創(chuàng)建標(biāo)識符,##被稱為記號粘合
這樣的連接必須產(chǎn)生一個合法的標(biāo)識符,否則結(jié)果是未定義
這?我們想想,寫?個函數(shù)求2個數(shù)的較?值的時候,不同的數(shù)據(jù)類型就得寫不同的函數(shù)。
?如:
int int_max(int x, int y)
{return x>y?x:y;
}
float float_max(float x, float y)
{return x>yx:y;
}
但是這樣寫起來太繁瑣了,現(xiàn)在我們這樣寫代碼試試:
//宏定義
#define GENERIC_MAX(type) \
type type##_max(type x, type y)\
{ \return (x>y?x:y); \
}
使?宏,定義不同函數(shù)
GENERIC_MAX(int) //替換到宏體內(nèi)后int##_max ?成了新的符號 int_max做函數(shù)名
GENERIC_MAX(float) //替換到宏體內(nèi)后float##_max ?成了新的符號 float_max做函數(shù)名
int main()
{//調(diào)?函數(shù)int m = int_max(2, 3);printf("%d\n", m);float fm = float_max(3.5f, 4.5f);printf("%f\n", fm);return 0;
}
輸出:
3
4.500000
在實際開發(fā)過程中##使?的很少,很難取出?常貼切的例?。
8.命名約定
?般來講函數(shù)的宏的使?語法很相似。所以語?本?沒法幫我們區(qū)分?者。
那我們平時的?個習(xí)慣是:
- 把宏名全部?寫
- 函數(shù)名不要全部?寫
9.#undef
這條指令用于移除一個宏定義
#undef NAME
//如果現(xiàn)存的?個名字需要被重新定義,那么它的舊名字?先要被移除。
10.命令行定義
許多C 的編譯器提供了?種能?,允許在命令?中定義符號。?于啟動編譯過程。
例如:當(dāng)我們根據(jù)同?個源?件要編譯出?個程序的不同版本的時候,這個特性有點?處。(假定某個程序中聲明了?個某個?度的數(shù)組,如果機(jī)器內(nèi)存有限,我們需要?個很?的數(shù)組,但是另外?個機(jī)器內(nèi)存?些,我們需要?個數(shù)組能夠?些。)
#include <stdio.h>
int 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;
}
編譯指令
//linux 環(huán)境演?
gcc -D ARRAY_SIZE=10 programe.c
11.條件編譯
在編譯?個程序的時候我們?nèi)绻獙?條語句(?組語句)編譯或者放棄是很?便的。因為我們有條
件編譯指令。
?如說:
- 調(diào)試性的代碼,刪除可惜,保留?礙事,所以我們可以選擇性的編譯。
#include <stdio.h>
#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;
}
常見的條件編譯指令
1.
#if 常量表達(dá)式//...
#endif
//常量表達(dá)式由預(yù)處理器求值。
如:
#define __DEBUG__ 1
#if __DEBUG__//..
#endif
2.多個分?的條件編譯
#if 常量表達(dá)式//...
#elif 常量表達(dá)式//...
#else//...
#endif
3.判斷是否被定義
#if defined(symbol)
#ifdef symbol
#if !defined(symbol)
#ifndef symbol
4.嵌套指令
#if defined(OS_UNIX)#ifdef OPTION1unix_version_option1();#endif#ifdef OPTION2unix_version_option2();#endif
#elif defined(OS_MSDOS)#ifdef OPTION2msdos_version_option2();#endif
#endif
12.頭文件的包含
12.1頭文件被包含的方式
12.1.1本地文件包含
#include "filename"
查找策略:先在源?件所在?錄下查找,如果該頭?件未找到,編譯器就像查找?guī)旌瘮?shù)頭?件?樣在
標(biāo)準(zhǔn)位置查找頭?件。
如果找不到就提?編譯錯誤。
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
//這是VS2013的默認(rèn)路徑
注意按照自己的安裝路徑查找
12.1.2庫文件包含
#include <filename.h>
查找頭?件直接去標(biāo)準(zhǔn)路徑下去查找,如果找不到就提?編譯錯誤。
這樣是不是可以說,對于庫?件也可以使? “” 的形式包含?
答案是肯定的,可以,但是這樣做查找的效率就低些,當(dāng)然這樣也不容易區(qū)分是庫?件還是本地?件了。
12.2嵌套文件包含
#include 指令可以使另外?個?件被編譯。就像它實際出現(xiàn)于 #include 指令的地??樣。
這種替換的?式很簡單:預(yù)處理器先刪除這條指令,并?包含?件的內(nèi)容替換。
?個頭?件被包含10次,那就實際被編譯10次,如果重復(fù)包含,對編譯的壓?就?較?。
test.c
#include "test.h"
#include "test.h"
#include "test.h"
#include "test.h"
#include "test.h"
int main()
{return 0;
}
test.h
void test();
struct Stu
{int id;char name[20];
};
如果直接這樣寫,test.c?件中將test.h包含5次,那么test.h?件的內(nèi)容將會被拷?5份在test.c中。
如果test.h ?件?較?,這樣預(yù)處理后代碼量會劇增。如果?程?較?,有公共使?的頭?件,被?家都能使?,?不做任何的處理,那么后果真的不堪設(shè)想。
如何解決頭?件被重復(fù)引?的問題?答案:條件編譯。
每個頭?件的開頭寫:
#ifndef __TEST_H__
#define __TEST_H__
//頭?件的內(nèi)容
#endif //__TEST_H__
或者
#pragma once
就可以避免頭?件的重復(fù)引?。
注:
推薦《?質(zhì)量C/C++編程指南》中附錄的考試試卷(很重要)。
思考題:
- 頭?件中的 ifndef/define/endif是?什么?的?
- #include <filename.h> 和 #include "filename.h"有什么區(qū)別?
13.其他預(yù)處理指令
#error
#pragma
#line
...
不做介紹,??去了解。
#pragma pack()在結(jié)構(gòu)體部分介紹
參考《C語?深度解剖》學(xué)習(xí)