最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

C語言C預(yù)處理器和C庫

2023-01-31 11:40 作者:虛云幻仙  | 我要投稿

/*---------以下為commonheader.h文件--------*/

#pragma once //pragma用于設(shè)置編譯器指令,修改編譯器的一些設(shè)置,pragma once使這個文件只會被包含一次,但如果別的文件中有與這個文件相同的代碼塊依然會沖突,這個指令不受C標準支持,不可移植

// 預(yù)處理指令以#開頭

#define CLEANINPUT() while(getchar()!='\n')continue //定義類函數(shù)宏,cleaninput()宏在預(yù)處理階段會替換為while循環(huán)(內(nèi)聯(lián)代碼),實際運行不會發(fā)生函數(shù)調(diào)用,但每個cleaninput()宏的替換都會產(chǎn)生這段相同的代碼,使占用變大,而函數(shù)調(diào)用則只會有一份代碼,但每次調(diào)用函數(shù)更耗時,只有在多次調(diào)用(循環(huán))時類函數(shù)宏和函數(shù)才會有時間、空間區(qū)別

#ifndef _CRT_SECURE_NO_WARNINGS //ifndef如果沒有定義后面跟著的宏/標識符 則執(zhí)行下面直到#else或#endif(先遇到任意一個截止)之間的語句

????#define _CRT_SECURE_NO_WARNINGS //常用方法,為避免重復(fù)宏名,先判斷再定義,可以用縮進表示層級,縮不縮沒有影響

#endif //預(yù)處理器的if必須用endif結(jié)束

#ifdef DEBUG //如果聲明了DEBUG則執(zhí)行下面內(nèi)容

#define INFO() printf("%s %s %s %d",__DATE__, \

__TIME__, \

__FILE__, \

__LINE__\

) //預(yù)定義宏__DATE__替換為預(yù)處理器執(zhí)行到這里時(預(yù)處理時)的日期字符串字面量,__TIME__替換為時分秒字符串字面量"hh:mm:ss",__FILE__替換為當前源代碼文件字符串字面量,__LINE__替換為當前行號 整型 常量,注意這些預(yù)定義宏在每個調(diào)用的地方單獨替換,比如xxx.c第20行使用了__FILE__和__LINE__則替換為xxx.c 20,yyy.c第13行使用則替換為yyy.c 13,由于預(yù)處理器在編譯階段之前,所以不會替換成最終可執(zhí)行文件名以及實際打開文件時間

//預(yù)處理器按行處理,在行末尾使用\加換行(兩者緊挨著)使編譯器在預(yù)處理之前將多行物理行處理為一行邏輯行,上面五行會去掉\\n合并為一行,并且每行要和首行左對齊,如果有縮進會被視為空白符號

#else //if不滿足走else

#define NODEBUG

#endif // DEBUG

#define AVG(X,Y) ( 1/( ( 1/(X)+1/(Y) )/2 ) ) //類函數(shù)宏傳參,傳入的實參會替換表達式中的XY,,因為預(yù)處理器不做計算,如果X為5+3則會直接代入表達式,既不會先計算處理成8也不外套括號,所以需要對每個參數(shù)手動加括號保證語義正確,同理整個替換體也需要括起來保證作為一個整體來執(zhí)行

//宏的參數(shù)不要使用遞增,由于預(yù)處理器不計算直接替換,所以當有多個替換時會產(chǎn)生多個x++,使變量的值不止+1

#define PI (4*atan(1.0)) //C數(shù)學庫中沒有定義PI,角度的180度對應(yīng)弧度PI,arctan(1)是45度即PI/4,格式: #define 宏名 替換體 ,宏名遵循變量命名規(guī)則,替換體可以為空

#define DEGREE_TO_RADIAN (PI/180) //角度除以180乘以PI = 弧度 ,上面定義的PI會替換掉這行的PI,即((4*atan(1.0))/180)

#define STRINGPI "PI" //用雙引號引起來的部分不會被宏替換,要表示PI對應(yīng)的值的字符串需要使用格式化字符串進行轉(zhuǎn)換

#define PR1(X,Y)? printf(#X" = %.2f\n"#Y" = %.2f\n",(X),(Y)) //#X將參數(shù)X轉(zhuǎn)化為字符串,相當于在X兩側(cè)加了雙引號,編譯時多段字符串會合并,如果直接寫"X"會將其看做字符而不是參數(shù),不進行替換

#if XYZ == 1 //后面跟整形常量表達式,非0為真,不能使用上面通過define定義的PI,因為替換體中的atan()函數(shù)需要到程序執(zhí)行階段才能計算值,預(yù)處理階段得不到表達式PI的值

#define Z 1

#elif defined(XXMOD) //elif配合if使用,#if defined(...)等價于#ifdef ... ,好處是#if 可以使用#elif

#include "XX.h"

#endif // XYZ?==1

#ifndef COMMONHEADER_H //通常避免頭文件重復(fù)包含的方法,將整個頭文件包裹在ifndef內(nèi),用頭文件的名字作為符號常量并用_代替. 頭文件內(nèi)定義的宏也可以拼上文件名防止名字沖突比如COMMONHEADER_H_PR()

#define COMMONHEADER_H //使用頭文件名作為符號常量查看是否已被包含,沒有定義過則執(zhí)行包含,并且定義該符號常量

#endif // !COMMONHEADER_H

//預(yù)定義宏__STDC__設(shè)置為1時表明實現(xiàn)遵循C標準,__STDC_HOSTED__本機環(huán)境設(shè)置為1否則設(shè)置為0,__STDC_VERSION__設(shè)置為199901L以支持C99標準,設(shè)置為201112L以支持C11標準

#line 200 //把當前行號重置為200

#line 220 "cool.c" //把當前行號重置為220,文件名重置為cool.c

//#if __STDC_VERSION__ != 201112L

????// #error Not C11 //error指令讓預(yù)處理器發(fā)出一條錯誤消息,消息包含指令中的文本,并應(yīng)該中斷編譯

//#endif

_Pragma("nonstandardtreatmenttypeB on") //等價于#pragma nonstandardtreatmenttypeB on? 好處是沒有使用#可以放在其他宏中

#define SHOWTYPE(X) printf("Type of "#X" is %s",_Generic((X),int:"int",double:"double",long:"long",default:"other")".\n") //在參數(shù)X前面加一個# (#X)將參數(shù)按字面轉(zhuǎn)換為字符串,編譯階段將多個相鄰的字符串串聯(lián)起來

//_Generic()泛型選擇(C11) 判斷參數(shù)1的類型,不計算參數(shù)1的具體值,后面的參數(shù)為 類型名:值 的形式,參數(shù)1的類型匹配某一個類型名標簽時,整個泛型選擇表達式的值為標簽后面的值,值也可以是表達式,該例中char類型會匹配到default,這時printf函數(shù)的參數(shù)2變?yōu)榱?#34;other"".\n"再合并為一個字符串

//_Noreturn void quit(void) //_Noreturn函數(shù)說明符(c11),告訴用戶和編譯器該函數(shù)不會將控制返回主調(diào)函數(shù),exit()是_Noreturn函數(shù)的一個實例

#define XNAME(n) x##n //如果替換體寫作xn則不會替換n為實參,預(yù)處理器會將xn作為一個標記看待,這和&n *n等使用運算符加參數(shù)名不同,預(yù)處理器能夠識別運算符,所以如果想表示變量名x1,則要使用##運算符(預(yù)處理器黏合劑)連接x和參數(shù)n,即將x和n看作兩個標記(使參數(shù)n能夠替換),然后連在一起

inline static void eatline() //inline內(nèi)聯(lián)函數(shù),編譯器可能會用內(nèi)聯(lián)代碼替換函數(shù)調(diào)用以盡可能快地調(diào)用該函數(shù),可能會執(zhí)行一些優(yōu)化也可能不起作用,一般內(nèi)聯(lián)函數(shù)需要定義為static內(nèi)部鏈接(即在同一個翻譯單元內(nèi)使用),內(nèi)聯(lián)函數(shù)不能獲得函數(shù)地址,因為如果得到了函數(shù)地址,編譯器會生成非內(nèi)聯(lián)函數(shù)

{

//將內(nèi)聯(lián)函數(shù)寫在頭文件時應(yīng)寫完整的函數(shù)定義,一般不在頭文件中放置可執(zhí)行代碼,內(nèi)聯(lián)函數(shù)是特例

???? while (getchar() != '\n')

???? continue;

}


/*---------以下為preprocessor.c文件--------*/

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h> //尖括號指示編譯器去標準庫中查找文件

#include <ctype.h>

#include <math.h>

#include <time.h>

#include <stdlib.h>

#include <assert.h> //斷言庫,assert斷言,用于輔助調(diào)試

#include <string.h> //include指令將頭文件的代碼替換至當前位置,但編譯結(jié)果只保留實際調(diào)用的部分

#include <stdarg.h>

#define DEBUG //define用來定義明示常量/符號常量,

#include "commonheader.h" //雙引號指示編譯器從當前目錄/源文件目錄/IDE指定目錄等位置尋找,找不到再去標準庫中查找,雙引號中實際為相對路徑例如"/usr/biff/p.h"表示在當前目錄的usr文件夾的biff文件夾里的p.h文件

//在預(yù)處理之前,編譯器必須對程序進行翻譯處理,首先映射字符集(utf-8 gbk等),第二定位每個反斜杠(\)后面跟著換行符的實例并刪除他們,將兩個物理行轉(zhuǎn)換成一個邏輯行,第三編譯器會將每一條注釋替換為一個空格,將多個連續(xù)空白替換為1個空格(換行符除外)


struct xy //直角坐標

{

???? double x;

???? double y;

};

struct rA //極坐標

{

???? double r;

???? double A;

};

struct xy coordinate(struct rA* ra);

void testclock_t(void);

void random_choice_int(const int arr[], int size, int num);

void test_qsort(void);

int str_comp(const void* p1, const void* p2);

double* new_d_array(int n, ...);

void say_bye(void);

void other(void);

struct xy coordinate(struct rA* pra) //極坐標轉(zhuǎn)換為直角坐標

{

???? struct xy pxy;

???? pxy.x = cos(pra->A) * pra->r;

???? pxy.y = sin(pra->A) * pra->r;

???? // sin cos tan asin acos atan均為接收1個參數(shù),當xy都為負數(shù),atan的參數(shù)是正數(shù)返回值有誤,需要改為使用atan2(x,y)兩個參數(shù)的版本

???? return pxy; //在被調(diào)函數(shù)中創(chuàng)建的變量/數(shù)據(jù)對象會隨被調(diào)函數(shù)結(jié)束而釋放,如需傳給主調(diào)函數(shù)則要傳回變量的值,或從主調(diào)函數(shù)接收一個地址來存儲值,所以傳回pxy的地址是錯誤的,因為pxy的內(nèi)存已經(jīng)釋放了,該地址的值可能已經(jīng)被改變了,而且該地址也已經(jīng)不再注冊為程序使用

}

#define MINUS(X,Y) ((X)-(Y)) //define對定義行往下的區(qū)域有效

void testclock_t(void)

{

???? clock_t cpu_time1 = clock(); //clock_t定義在time.h頭文件中的類型,clock()返回clock_t類型的當前處理器時間(如果不可用返回-1)

???? double d = 3e20;

???? for (int i = 0; i < 100000; i++)

???? {

???????? d = sqrt(d); //square root平方根

???????? d = pow(d,2);

???? }

???? clock_t cpu_time2 = clock();

???? printf("time2-time1=%g", (double)MINUS(cpu_time2, cpu_time1) / CLOCKS_PER_SEC); //time.h中的常量CLOCKS_PER_SEC每秒鐘的處理器時間單位的數(shù)量

}

#undef MINUS(X,Y) //undef 取消指定的宏,該宏在這一行之前有效(上面的函數(shù)可以使用),這一行之后無效(無法調(diào)用),在下面可再次定義該名稱的宏,并可以賦予不同的操作

void random_choice_int(const int arr[], int size, int num) //從數(shù)組中隨機選擇num個元素

{

???? assert(size > num); //斷言數(shù)組長度大于選取元素的數(shù)量,合法性檢查,如果條件為false會調(diào)用abort()函數(shù)(stdlib.h定義的函數(shù))中止程序并顯示錯誤的文件、函數(shù)、行號、該行代碼的內(nèi)容

???? assert(num > 0); //也可以通過if(){abort();}來中止

???? //當使用了#define NDEBUG? 會禁用文件中所有assert()語句,方便調(diào)試

???? _Static_assert(CHAR_BIT == 16, "16-bit char falsely assumed"); //_Static_assert在編譯階段檢查,判斷參數(shù)1整型常量表達式,如果false編譯器會顯示參數(shù)2字符串并且不編譯程序

???? srand(time(0));

???? int choice;

???? int** pa = (int** )malloc(size * sizeof(int*));

???? for (int i = 0; i < size; i++)

???? {

???? ????pa[i] = arr + i;

???? }

???? for (int i = 0; i < num; i++)

???? {

???????? choice = rand()%(size-i);

???????? printf("%d ", *pa[choice]);

???????? pa[choice] = pa[size - 1 - i];

???? }

???? putchar('\n');

???? free(pa);

}

struct names

{

???? char first[20];

???? char last[20];

};

#define LISTSIZE 10 //盡量對每一個有特殊意義的常量使用define以提高可讀性

#define RANDOM_FROM1TO(X) (rand()%(X)+1)

#define GET_RANDOM_LETTER() "qwertyuiopasdfghjklzxcvbnm"[rand()%26]

#define SHOW_LIST() for(int i = 0; i<LISTSIZE;i++)printf("[%s %s] ",list[i].first,list[i].last)

void test_qsort(void)

{

???? srand(time(0));

???? struct names list[LISTSIZE];

???? int len;

???? for (int i = 0; i < LISTSIZE; i++)

???? {

???????? len = RANDOM_FROM1TO(LISTSIZE-1);

???????? for (int j = 0; j < len; j++)

???????? {

???????? ????list[i].first[j] = GET_RANDOM_LETTER();

???????? }

???????? list[i].first[len] = '\0';

???????? len = RANDOM_FROM1TO(LISTSIZE-1);

???????? for (int j = 0; j < len; j++)

???????? {

???????? ????list[i].last[j] = GET_RANDOM_LETTER();

???????? }

???????? list[i].last[len] = '\0';

???? }

???? SHOW_LIST();

???? putchar('\n');

???? qsort(list, LISTSIZE, sizeof(struct names), str_comp); //qsort()快速排序,參數(shù)1是數(shù)組地址,參數(shù)2是元素個數(shù),參數(shù)3是元素大小,參數(shù)4是比較器(函數(shù)),需要程序員自定義比較器/比較規(guī)則,qsort使用比較器比較兩個元素,如果返回正數(shù)則交換位置

???? //qsort根據(jù)參數(shù)3來確定每個元素的大小,或者說每次移動的字節(jié)數(shù),將參數(shù)3指定的大小作為一個數(shù)據(jù)塊,按比較器的結(jié)果從小到大排序

???? SHOW_LIST();

}

int str_comp(const void* ps1, const void* ps2) //自定義比較器,比較器的函數(shù)簽名必須和qsort規(guī)定的比較器簽名一致,所以即便要比較的是結(jié)構(gòu),也要設(shè)定形參為const void*,比較器要返回int,接收兩個const void*參數(shù)

{

???? const struct names* psn1 = (const struct names*)ps1; //對const void*指針進行強轉(zhuǎn),強轉(zhuǎn)也需要帶上const關(guān)鍵字

???? const struct names* psn2 = (const struct names*)ps2; //對const void*指針進行強轉(zhuǎn),強轉(zhuǎn)也需要帶上const關(guān)鍵字

???? int res = strcmp(psn1->first, psn2->first); //借助strcmp完成字符串比較

???? if (res==0)

???? {

???? ????return strcmp(psn1->last, psn2->last);

???? }

???? return res; //返回正數(shù)即告訴qsort函數(shù)交換這兩個元素

}

double* new_d_array(int n, ...) //可變參數(shù)的函數(shù),在參數(shù)列表的最后添加...作為可變參數(shù)的標志 ,在可變參數(shù)前面添加int類型參數(shù)用來接收可變參數(shù)的數(shù)量,即需要從主調(diào)函數(shù)傳遞當前調(diào)用的實際可變參數(shù)的數(shù)量

{

???? va_list ap; //聲明在stdarg.h中的va_list類型代表一種用于儲存形參對應(yīng)的形參列表中省略號...部分的數(shù)據(jù)對象,即使用va_list類型變量接收可變參數(shù)部分

???? va_start(ap, n); //va_start()宏,參數(shù)1為va_list變量,參數(shù)2為可變參數(shù)的數(shù)量(通過主調(diào)函數(shù)獲得)

???? double* dynarr = malloc(n * sizeof(double)); //由于被調(diào)函數(shù)定義數(shù)組會隨函數(shù)結(jié)束而釋放,但動態(tài)分配的內(nèi)存可以在函數(shù)之間傳遞,最終需要在某處使用free(動態(tài)內(nèi)存地址)來釋放,通??梢詮闹髡{(diào)函數(shù)接收一個數(shù)組用來存放元素,這樣該數(shù)組為主調(diào)函數(shù)的自動變量,不需要手動釋放

???? for (int i = 0; i < n; i++)

???? {

???? ????dynarr[i] = va_arg(ap, double); //va_arg()宏從參數(shù)1 va_list變量中逐個取出參數(shù)(按實參從前往后順序),參數(shù)2為該參數(shù)的類型名,類型名必須正確,因為宏不會進行類型轉(zhuǎn)換,類型不正確解讀的結(jié)果可能不正確

???? }

???? va_end(ap); //清理工作,調(diào)用該函數(shù)后只有用va_start重新初始化ap才能使用,可以在初始化ap后使用va_copy(va_list apcopy,va_list ap)拷貝備份,va_copy會拷貝ap當前狀態(tài)

???? //調(diào)用new_d_array(5, 1.0,2.0,3.0,4.0,5.0),省略號前面的int n稱為parmN,需要和后面實際傳參的數(shù)量對應(yīng),不包含前面的普通參數(shù)

???? return dynarr; //return會將dynarr的值返回,變量dynarr會釋放,主調(diào)函數(shù)可以直接使用值(一次性),也可以聲明一個變量接收值(保存多次使用)

}

void say_bye(void)

{

????puts("bye");

}

#define PR(...) printf(__VA_ARGS__) //變參宏,在宏的參數(shù)列表中使用...(前面可以有普通參數(shù),...必須放在最后一個參數(shù)的位置),在替換體中使用__VA_ARGS__表示變參部分的參數(shù)

void other(void)

{

???? atexit(say_bye); //atexit()函數(shù)用于注冊程序退出時(exit())執(zhí)行的動作,接收無返回值無參數(shù)的函數(shù)地址,并且遵循后進先出,后注冊的先執(zhí)行

???? //由于main函數(shù)return 0 即為隱式exit()所以即便沒有顯式exit(),注冊過的動作也會執(zhí)行,C標準規(guī)定注冊列表最小為32個函數(shù)

???? // exit()執(zhí)行完atexit()指定的函數(shù)后會完成一些清理工作:刷新所有輸出流、關(guān)閉所有打開的流和由標準io函數(shù)tmpfile()創(chuàng)建的臨時文件,然后把控制權(quán)返回主機環(huán)境并報告中止狀態(tài)(EXIT_SUCCESS/EXIT_FAILURE)

???? //memcpy(dest,src,size)將src的size大小數(shù)據(jù)塊拷貝至dest位置,由于拷貝不使用緩沖區(qū),要求dest和src不能有重疊,比如從src+1拷貝到src應(yīng)使用memmove(),會將數(shù)據(jù)先拷貝到創(chuàng)建的緩沖區(qū)再拷貝到dest

???? PR("%d\n%s\n", 20, "asd"); //預(yù)處理時變參分別接收一個字符串參數(shù)、一個int參數(shù)和一個字符串參數(shù),將三個參數(shù)替換到__VA_ARGS__所在位置,并且用逗號分隔

???? exp(10); //math.h中的函數(shù),返回e^參數(shù),指數(shù)函數(shù)的值

???? log(20.0); //math.h,返回 參數(shù)的自然對數(shù)值

???? log10(100.0); //math.h,返回以10為底的對數(shù)值

???? //pow冪運算,sqrt平方根,cbrt立方根,fabs絕對值,ceil(x)返回大于等于x的最小整數(shù),floor(x)返回小于等于x的最大整數(shù)

???? //sqrtf為sqrt的float版本,sqrtl為long double版本,在包含tgmath.h(c99)頭文件時會將這三個函數(shù)組合成一個宏sqrt()自動匹配實參類型的函數(shù),但這時宏sqrt()蓋住了函數(shù)sqrt(),如果想要直接使用函數(shù)sqrt()可以寫作(sqrt)(),用括號先運算sqrt,使其表示函數(shù)地址再接括號表示函數(shù)調(diào)用

}


C語言C預(yù)處理器和C庫的評論 (共 條)

分享到微博請遵守國家法律
和平县| 普定县| 台中县| 大连市| 巴楚县| 佛山市| 玛纳斯县| 建平县| 白山市| 曲沃县| 白玉县| 镇沅| 慈溪市| 涪陵区| 鲜城| 汉阴县| 怀仁县| 沙雅县| 虎林市| 广平县| 望谟县| 中宁县| 九龙城区| 资中县| 四子王旗| 清水县| 姚安县| 固镇县| 麦盖提县| 乌拉特中旗| 丰宁| 二连浩特市| 满洲里市| 水富县| 桂阳县| 赣州市| 临洮县| 海南省| 上高县| 江油市| 新泰市|