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