在 C 中,goto 語(yǔ)句是不能跨越函數(shù)的,而執(zhí)行這類(lèi)跳轉(zhuǎn)功能的是 setjmp 和 longjmp 宏
。這兩個(gè)宏對(duì)于處理發(fā)生在深層嵌套函數(shù)調(diào)用中的出錯(cuò)情況是非常有用的。
此即為:非局部跳轉(zhuǎn)。非局部指的是,這不是由普通 C 語(yǔ)言 goto 語(yǔ)句在一個(gè)函數(shù)內(nèi)實(shí)施的跳轉(zhuǎn),而是在棧上跳過(guò)若干調(diào)用幀,返回到當(dāng)前函數(shù)調(diào)用路徑的某個(gè)函數(shù)中。
#include <setjmp.h>
int setjmp (jmp_buf env) ; /*設(shè)置調(diào)轉(zhuǎn)點(diǎn)*/
void longjmp (jmp_buf env, int val) ; /*跳轉(zhuǎn)*/
setjmp 參數(shù) env 的類(lèi)型是一個(gè)特殊類(lèi)型 jmp_buf。這一數(shù)據(jù)類(lèi)型是某種形式的數(shù)組,其中存放 在調(diào)用 longjmp 時(shí)能用來(lái)恢復(fù)棧狀態(tài)的所有信息。因?yàn)樾柙诹硪粋€(gè)函數(shù)中引用 env 變量,所以應(yīng)該將 env 變量定義為全局變量。
longjmp 參數(shù) val,它將成為從 setjmp 處返回的值。
#include <stdio.h>
#include <setjmp.h>
static jmp_buf buf;
void second(void)
{
printf("second\n");
longjmp(buf,1);
// 跳回setjmp的調(diào)用處使得setjmp返回值為1
}
void first(void)
{
second();
printf("first\n");
// 不可能執(zhí)行到此行
}
int main()
{
if (!setjmp(buf))
{
// 進(jìn)入此行前,setjmp返回0
first();
}
else
{
// 當(dāng)longjmp跳轉(zhuǎn)回,setjmp返回1,因此進(jìn)入此行
printf("main\n");
}
return 0;
}
直接調(diào)用 setjmp 時(shí),返回值為 0,這一般用于初始化(設(shè)置跳轉(zhuǎn)點(diǎn)時(shí))。以后再調(diào)用 longjmp 宏時(shí)用 env 變量進(jìn)行跳轉(zhuǎn)。程序會(huì)自動(dòng)跳轉(zhuǎn)到 setjmp 宏的返回語(yǔ)句處,此時(shí) setjmp 的返回值為非 0,由 longjmp 的第二個(gè)參數(shù)指定。
一般地,宏 setjmp 和 longjmp 是成對(duì)使用的,這樣程序流程可以從一個(gè)深層嵌套的函數(shù)中返回。
\ 頭文件定義了一些宏,當(dāng)函數(shù)參數(shù)未知時(shí)去獲取函數(shù)的參數(shù)變量:typedef va_list
宏:
va_start()
va_arg()
va_end()
va_list 類(lèi)型通過(guò) stdarg 宏定義來(lái)訪問(wèn)一個(gè)函數(shù)的參數(shù)表,參數(shù)列表的末尾會(huì)用省略號(hào)省略
( va_list 用來(lái)保存 va_start , va_end 所需信息的一種類(lèi)型。為了訪問(wèn)變長(zhǎng)參數(shù)列表中的參數(shù),必須聲明 va_list 類(lèi)型的一個(gè)對(duì)象 )
我們通過(guò)初始化( va_start )類(lèi)型為 va_list 的參數(shù)表指針,并通過(guò) va_arg 來(lái)獲取下一個(gè)參數(shù)
。
//求任意個(gè)整數(shù)的最大值
#include <stdio.h>
#include <stdarg.h>
int maxint(int n, ...) /* 參數(shù)數(shù)量由非變長(zhǎng)參數(shù)n直接指定 */
{
va_list ap;
int i, arg, max;
va_start(ap, n); /* ap為參數(shù)指針,首先將其初始化為最后一個(gè)具名參數(shù), 以便va_arg獲取下一個(gè)省略號(hào)內(nèi)參數(shù) */
for (i = 0; i < n; i++) {
arg = va_arg(ap, int); /* 類(lèi)型固定為int, 按照給定類(lèi)型返回下一個(gè)參數(shù) */
if (i == 0)
max = arg;
else {
if (arg > max)
max = arg;
}
}
va_end(ap);
return max;
}
void main()
{
printf("max = %d\n", maxint(5, 2, 6, 8, 11, 7));
}
歷史上,C語(yǔ)言只支持在編譯時(shí)就能確定大小的數(shù)組。程序員需要變長(zhǎng)數(shù)組時(shí),不得不用malloc或calloc這樣的函數(shù)為這些數(shù)組分配存儲(chǔ)空間,且涉及到多維數(shù)組時(shí),不得不顯示地編碼,用行優(yōu)先索引將多維數(shù)組映射到一維的數(shù)組。
ISO C99引入了一種能力,允許數(shù)組的維度是表達(dá)式,在數(shù)組被分配的時(shí)候才計(jì)算出來(lái)。
#include <stdio.h>
int main(void)
{
int n, i ;
scanf("%d", &n) ;
int array[n] ;
for (; i<n; i++)
{
array[i] = i ;
}
for (i=0; i<n; i++)
{
printf("%d,", array[i]) ;
}
return 0;
}
注意:
如果你需要有著變長(zhǎng)大小的臨時(shí)存儲(chǔ),并且其生命周期在變量?jī)?nèi)部時(shí),可考慮VLA(Variable Length Array,變長(zhǎng)數(shù)組)。但這有個(gè)限制:每個(gè)函數(shù)的空間不能超過(guò)數(shù)百字節(jié)。因?yàn)?C99 指出邊長(zhǎng)數(shù)組能自動(dòng)存儲(chǔ),它們像其他自動(dòng)變量一樣受限于同一作用域。即便標(biāo)準(zhǔn)未明確規(guī)定,VLA 的實(shí)現(xiàn)都是把內(nèi)存數(shù)據(jù)放到棧中。VLA 的最大長(zhǎng)度為 SIZE_MAX 字節(jié)??紤]到目標(biāo)平臺(tái)的棧大小,我們必須更加謹(jǐn)慎小心,以保證程序不會(huì)面臨棧溢出、下個(gè)內(nèi)存段的數(shù)據(jù)損壞的尷尬局面。
#include <stdio.h>
int main(void)
{
int i=0;
scanf("%d", &i) ;
switch(i)
{
case 1 ... 9: putchar("0123456789"[i]);
case 'A' ... 'Z': //do something
}
return 0;
}
switch (a)
{
case 1: ;
// ...
if (b==2)
{
case 2:;
// ...
}
else case 3:
{
// ...
for (b=0;b<10;b++)
{
case 5: ;
// ...
}
}
break;
case 4:
}
在C99之前,你只能按順序初始化一個(gè)結(jié)構(gòu)體。在C99中你可以這樣做:
struct Foo {
int x;
int y;
int z;
};
Foo foo = {.z = 3, .x = 5};
這段代碼首先初始化了foo.z,然后初始化了foo.x. foo.y 沒(méi)有被初始化,所以被置為0。
這一語(yǔ)法同樣可以被用在數(shù)組中。以下三行代碼是等價(jià)的:
int a[5] = {[1] = 2, [4] = 5};
int a[] = {[1] = 2, [4] = 5};
int a[5] = {0, 2, 0, 0, 5};
關(guān)鍵字 restrict 僅對(duì)指針有用,修飾指針,表明要修改這個(gè)指針?biāo)赶虻臄?shù)據(jù)區(qū)的內(nèi)容,僅能通過(guò)該指針來(lái)實(shí)現(xiàn),此關(guān)鍵字的作用是使編譯器優(yōu)化代碼,生成更高效的匯編代碼。
int foo (int* x, int* y)
{
*x = 0;
*y = 1;
return *x;
}
很顯然函數(shù)foo()的返回值是0,除非參數(shù)x和y的值相同。可以想象,99%的情況下該函數(shù)都會(huì)返回0而不是1。然而編譯起必須保證生成100%正確的代碼,因此,編譯器不能將原有代碼替換成下面的更優(yōu)版本:
int f (int* x, int* y)
{
*x = 0;
*y = 1;
return 0;
}
現(xiàn)在我們有了 restrict 這個(gè)關(guān)鍵字,就可以利用它來(lái)幫助編譯器安全的進(jìn)行代碼優(yōu)化了,由于指針 x 是修改 x的唯一途徑,編譯起可以確認(rèn) “ y=1; ”這行代碼不會(huì)修改 * x的內(nèi)容,因此可以安全的優(yōu)化。
int f (int *restrict x, int *restrict y)
{
*x = 0;
*y = 1;
return 0;
}
很多C的庫(kù)函數(shù)中用restrict關(guān)鍵字:
void memcpy( void restrict dest ,const void restrict src,sizi_t n)
這是一個(gè)很有用的內(nèi)存復(fù)制函數(shù),由于兩個(gè)參數(shù)都加了 restrict 限定,所以?xún)蓧K區(qū)域不能重疊,即 dest 指針?biāo)傅膮^(qū)域,不能讓別的指針來(lái)修改,即 src 的指針不能修改. 相對(duì)應(yīng)的別一個(gè)函數(shù) memmove(void dest,const void * src,size_t)則可以重疊。
void f(int a[static 10]) {
/* ... */
}
你向編譯器保證,你傳遞給 f 的指針指向一個(gè)具有至少10個(gè) int 類(lèi)型元素的數(shù)組的首個(gè)元素。我猜這也是為了優(yōu)化;例如,編譯器將會(huì)假定 a 非空。編譯器還會(huì)在你嘗試要將一個(gè)可以被靜態(tài)確定為 null 的指針傳入或是一個(gè)數(shù)組太小的時(shí)候發(fā)出警告。
void f(int a[const]) {
/* ... */
}
你不能修改指針 a.,這和說(shuō)明符 int * const a.作用是一樣的。然而,當(dāng)你結(jié)合上一段中提到的 static 使用,比如在int a[static const 10] 中,你可以獲得一些使用指針風(fēng)格無(wú)法得到的東西。
int x = 'ABCD' ;
這會(huì)把 x 的值設(shè)置為 0×41424344(或者0×44434241,取決于大小端)我們一般的小端機(jī)上,低位存在低字節(jié)處,DCBA 依次從低字節(jié)到高字節(jié)排列。
這只是一種看起來(lái)比較炫酷的寫(xiě)法,一般沒(méi)什么用。
更多建議: