天天看點

C語言list頭檔案_C語言的角落——這些C語言不常用的特性你知道嗎?

變長參數清單

<stdarg.h> 頭檔案定義了一些宏,當函數參數未知時去擷取函數的參數

變量:typedef va_list

宏:

va_start()

va_arg()

va_end()

va_list類型通過stdarg宏定義來通路一個函數的參數表,參數清單的末尾會用省略号省略

(va_list用來儲存va_start,va_end所需資訊的一種類型。為了通路變長參數清單中的參數,必須聲明va_list類型的一個對象 )

我們通過初始化(va_start)類型為va_list的參數表指針,并通過va_arg來擷取下一個參數。

【例子:】

求任意個整數的最大值:

C語言list頭檔案_C語言的角落——這些C語言不常用的特性你知道嗎?
可變長數組

曆史上,C語言隻支援在編譯時就能确定大小的數組。程式員需要變長數組時,不得不用malloc或calloc這樣的函數為這些數組配置設定存儲空間,且涉及到多元數組時,不得不顯示地編碼,用行優先索引将多元數組映射到一維的數組。

ISOC99引入了一種能力,允許數組的次元是表達式,在數組被配置設定的時候才計算出來。
C語言list頭檔案_C語言的角落——這些C語言不常用的特性你知道嗎?

注意:如果你需要有着變長大小的臨時存儲,并且其生命周期在變量内部時,可考慮VLA(Variable Length Array,變長數組)。

但這有個限制:每個函數的空間不能超過數百位元組。

因為C99指出邊長數組能自動存儲,它們像其他自動變量一樣受限于同一作用域。即便标準未明确規定,VLA的實作都是把記憶體資料放到棧中。VLA的最大長度為SIZE_MAX位元組。考慮到目标平台的棧大小,我們必須更加謹慎小心,以保證程式不會面臨棧溢出、下個記憶體段的資料損壞的尴尬局面。

case支援範圍取值(gcc擴充特性) MinGW編譯通過
C語言list頭檔案_C語言的角落——這些C語言不常用的特性你知道嗎?
非局部跳轉setjmp和longjmp

在C中,goto語句是不能跨越函數的,而執行這類跳轉功能的是setjmp和longjmp宏。這兩個宏對于處理發生在深層嵌套函數調用中的出錯情況是非常有用的。

此即為:非局部跳轉。非局部指的是,這不是由普通C語言goto語句在一個函數内實施的跳轉,而是在棧上跳過若幹調用幀,傳回到目前函數調用路徑的某個函數中。

#include <setjmp.h>

int setjmp(jmp_buf env) ;

void longjmp(jmp_bufenv, int val) ;

setjmp參數env的類型是一個特殊類型jmp_buf。這一資料類型是某種形式的數組,其中存放 在調用longjmp時能用來恢複棧狀态的所有資訊。因為需在另一個函數中引用env變量,是以應該将env變量定義為全局變量。

longjmp參數val,它将成為從setjmp處傳回的值。(很神奇吧。setjmp根據傳回值可知道是哪個longjmp傳回來的)

C語言list頭檔案_C語言的角落——這些C語言不常用的特性你知道嗎?
volatile屬性

如果你有一個自動變量,而又不想它被編譯器優化進寄存器,則可定義其為有volatile屬性。這樣,就明确地把這個值放在存儲器中,而不會被優化進寄存器。

setjmp會儲存目前棧狀态資訊,也會儲存此時寄存器中的值。(longjmp會復原寄存器中的值)

【如果要編寫一個使用非局部跳轉的可移植程式,則必須使用volatile屬性】 · IO緩沖問題 緩沖輸出和記憶體配置設定

當一個程式産生輸出時,能夠立即看到它有多重要?這取決于程式。

例如,終端上顯示輸出并要求人們坐在終端前面回答一個問題,人們能夠看到輸出以知道該輸入什麼就顯得至關重要了。另一方面,如果輸出到一個檔案中,并最終被發送到一個行式列印機,隻有所有的輸出最終能夠到達那裡是重要的。

立即安排輸出的顯示通常比将其暫時儲存在一大塊一起輸出要昂貴得多。是以,C實作通常允許程式員控制産生多少輸出後在實際地寫出它們。

這個控制通常約定為一個稱為setbuf()的庫函數。如果buf是一個具有适當大小的字元數組,則

setbuf(stdout, buf);

将告訴I/O庫寫入到stdout中的輸出要以buf作為一個輸出緩沖,并且等到buf滿了或程式員直接調用fflush()再實際寫出。緩沖區的合适的大小在中定義為BUFSIZ。

是以,下面的程式解釋了通過使用setbuf()來講标準輸入複制到标準輸出:

C語言list頭檔案_C語言的角落——這些C語言不常用的特性你知道嗎?

不幸的是,這個程式是錯誤的,因為一個細微的原因。

要知道毛病出在哪,我們需要知道緩沖區最後一次重新整理是在什麼時候。答案:主程式完成之後,庫将控制交回到作業系統之前所執行的清理的一部分。在這一時刻,緩沖區已經被釋放了! (即main函數棧清空之後)

有兩種方法可以避免這一問題。

首先,使用靜态緩沖區,或者将其顯式地聲明為靜态:

static char buf[BUFSIZ];

或者将整個聲明移到主函數之外。

另一種可能的方法是動态地配置設定緩沖區并且從不釋放它:

char *malloc();

setbuf(stdout, malloc(BUFSIZ));

注意在後一種情況中,不必檢查malloc()的傳回值,因為如果它失敗了,會傳回一個空指針。而setbuf()可以接受一個空指針作為其第二個參數,

這将使得stdout變成非緩沖的。這會運作得很慢,但它是可以運作的。 預編譯和宏定義

C/C++中幾個罕見卻有用的預編譯和宏定義

1:# error

文法格式如下:

#error token-sequence

其主要的作用是在編譯的時候輸出編譯錯誤資訊token-sequence,從友善程式員檢查程式中出現的錯誤。例如下面的程式

C語言list頭檔案_C語言的角落——這些C語言不常用的特性你知道嗎?

在編譯的時候輸出如編譯資訊

fatal error C1189: #error : No definedConstant Symbol CONST_NAME1

2:#pragma

其文法格式如下:

# pragma token-sequence

此指令的作用是觸發所定義的動作。如果token-sequence存在,則觸發相應的動作,否則忽略。此指令一般為編譯系統所使用。例如在Visual C++.Net 中利用# pragma once 防止同一代碼被包含多次。

3:#line

此指令主要是為強制編譯器按指定的行号,開始對源程式的代碼重新編号,在調試的時候,可以按此規定輸出錯誤代碼的準确位置。

形式1

文法格式如下:

# line constant “filename”

其作用是使得其後的源代碼從指定的行号constant重新開始編号,并将目前檔案的名命名為filename。例如下面的程式如下:

C語言list頭檔案_C語言的角落——這些C語言不常用的特性你知道嗎?

提示如下的編譯資訊:

Hello.c(15) : error C2065: 'CONST_NAME1' :undeclared identifier

表示目前檔案的名稱被認為是Hello.c, #line 10 "Hello.c"所在的行被認為是第10行,是以提示第15行出錯。

形式2

文法格式如下:

# line constant

其作用在于編譯的時候,準确輸出出錯代碼所在的位置(行号),而在源程式中并不出現行号,進而友善程式員準确定位。

4:運算符#和##

在ANSI C中為預編譯指令定義了兩個運算符——#和##。

# 的作用是實作文本替換(字元串化),例如

#define HI(x)printf("Hi,"#x"n");

void main()

{

HI(John);

}

程式的運作結果

Hi,John

在預編譯處理的時候, #x的作用是将x替換為所代表的字元序列。(即把x宏變量字元串化)在本程式中x為John,是以建構新串“Hi,John”。

##的作用是串連接配接。

例如

#define CONNECT(x,y) x##y

void main()

{

int a1,a2,a3;

CONNECT(a,1)=0;

CONNECT(a,2)=12;

a3=4;

printf("a1=%dta2=%dta3=%d",a1,a2,a3);

}

程式的運作結果為

a1=0 a2=12 a3=4

在編譯之前, CONNECT(a,1)被翻譯為a1, CONNECT(a,2)被翻譯為a2。

資料類型對應位元組數

程式運作平台

不同的平台上對不同資料類型配置設定的位元組數是不同的。

個人對平台的了解是CPU+OS+Compiler,是因為:

1、64位機器也可以裝32位系統(x64裝XP);

2、32位機器上可以有16/32位的編譯器(XP上有tc是16位的,其他常見的是32位的);

3、即使是32位的編譯器也可以弄出64位的integer來(int64)。

以上這些是基于常見的wintel平台,加上我們可能很少機會接觸的其它平台(其它的CPU和OS),是以個人認為所謂平台的概念是三者的組合。

雖然三者的長度可以不一樣,但顯然互相配合(即長度相等,32位的CPU+32位的OS+32位的Compiler)發揮的能量最大。

理論上來講 我覺得資料類型的位元組數應該是由CPU決定的,但是實際上主要由編譯器決定(占多少位由編譯器在編譯期間說了算)。

常用資料類型對應位元組數可用如sizeof(char),sizeof(char*)等得出

32位編譯器:

char :1個位元組

char*(即指針變量): 4個位元組(32位的尋址空間是2^32, 即32個bit,也就是4個位元組。同理64位編譯器)

short int : 2個位元組

int: 4個位元組

unsigned int : 4個位元組

float: 4個位元組

double: 8個位元組

long: 4個位元組

long long: 8個位元組

unsigned long: 4個位元組

64位編譯器:

char :1個位元組

char*(即指針變量): 8個位元組

short int : 2個位元組

int: 4個位元組

unsigned int : 4個位元組

float: 4個位元組

double: 8個位元組

long: 8個位元組

long long: 8個位元組

unsigned long: 8個位元組

繼續閱讀