1樓:匿名使用者
因為c語言並不內建字串,所以用一個字元指標指出字串的開頭(字串末尾由「/0」指出)。
函式的引數宣告如果是const 指標,就表示該函式不會修改該指標指向的內容,編譯器才敢進行某些優化
c語言中的void printlog(char *format,...)這是什麼意思?
2樓:匿名使用者
表示printlog這個函式,接受任意多個引數,第一個引數必須為char *型別,後面有多少個引數都可以,沒有也可以。
c語言對可變引數的支援通過stdarg.h來實現,原理很簡單。
首先讓編譯器識別...,允許函式呼叫傳入任意數量的引數而不產生警告。然後在函式體裡通過stdarg.h提供的幾個巨集來獲取引數。
這是stdarg.h提供的用於實現變長引數的幾個巨集:
#define va_arg(ap,t) ( *(t *)((ap += _intsizeof(t)) - _intsizeof(t)) )
#define va_end(ap) ( ap = (va_list)0 )
va_start用來初始化,就是將指標指向棧中變長引數的起始位置。va_arg用來取引數,即將指標轉換為指定型別的指標,取值。再將指標進行偏移。
va_end用來完成清理工作,也就是把指標置為null。
例子:int sum(unsigned int n,...)
va_end(args);
return sum;
}這段**實現了對任意數量的整數的求和,整數的數目由n指定。所以說,變長引數的函式一定有一個已知型別的引數,即第一個引數,用來告知函式,一共傳入了多少個引數,每個引數該取多長。
3樓:匿名使用者
是可變引數,是c的一個語法現象,我在電腦上儲存的一些資料,希望對你有用。
一、什麼是可變引數
我們在c語言程式設計中有時會遇到一些引數個數可變的函式,例如printf()函式,其函式原型為:
int printf( const char* format, ...);
它除了有一個引數format固定以外,後面跟的引數的個數和型別是可變的(用三個點"…"做引數佔位符),實際呼叫時可以有以下的形式:
printf("%d",i);
printf("%s",s);
printf("the number is %d ,string is:%s", i, s);
以上這些東西已為大家所熟悉。但是究竟如何寫可變引數的c函式以及這些可變引數的函式編譯器是如何實現,這個問題卻一直困擾了我好久。本文就這個問題進行一些**,希望能對大家有些幫助.
二、可變引數在編譯器中的處理
我們知道va_start,va_arg,va_end是在stdarg.h中被定義成巨集的, 由於1)硬體平臺的不同 2)編譯器的不同,所以定義的巨集也有所不同,下面看一下vc++6.0中stdarg.
h裡的**(檔案的路徑為vc安裝目錄下的\vc98\include\stdarg.h)
typedef char * va_list;
#define _intsizeof(n) ((sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
#define va_arg(ap,t) ( *(t *)((ap += _intsizeof(t)) - _intsizeof(t)) )
#define va_end(ap) ( ap = (va_list)0 )
下面我們解釋這些**的含義:
1、首先把va_list被定義成char*,這是因為在我們目前所用的pc機上,字元指標型別可以用來儲存記憶體單元地址。而在有的機器上va_list是被定義成void*的
2、定義_intsizeof(n)主要是為了某些需要記憶體的對齊的系統.這個巨集的目的是為了得到最後一個固定引數的實際記憶體大小。在我的機器上直接用sizeof運算子來代替,對程式的執行結構也沒有影響。
(後文將看到我自己的實現)。
3、va_start的定義為 &v+_intsizeof(v) ,這裡&v是最後一個固定引數的起始地址,再加上其實際佔用大小後,就得到了第一個可變引數的起始記憶體地址。所以我們執行va_start(ap, v)以後,ap指向第一個可變引數在的記憶體地址,有了這個地址,以後的事情就簡單了。
這裡要知道兩個事情:
⑴在intel+windows的機器上,函式棧的方向是向下的,棧頂指標的記憶體地址低於棧底指標,所以先進棧的資料是存放在記憶體的高地址處。
(2)在vc等絕大多數c編譯器中,預設情況下,引數進棧的順序是由右向左的,因此,引數進棧以後的記憶體模型如下圖所示:最後一個固定引數的地址位於第一個可變引數之下,並且是連續儲存的。
|--------------------------|
| 最後一個可變引數 | ->高記憶體地址處
|--------------------------|
|--------------------------|
| 第n個可變引數 | ->va_arg(arg_ptr,int)後arg_ptr所指的地方,
| | 即第n個可變引數的地址。
|--------------- |
|--------------------------|
| 第一個可變引數 | ->va_start(arg_ptr,start)後arg_ptr所指的地方
| | 即第一個可變引數的地址
|--------------- |
|------------------------ --|
| |
| 最後一個固定引數 | -> start的起始地址
|-------------- -| .................
|-------------------------- |
| |
|--------------- | -> 低記憶體地址處
(4) va_arg():有了va_start的良好基礎,我們取得了第一個可變引數的地址,在va_arg()裡的任務就是根據指定的引數型別取得本引數的值,並且把指標調到下一個引數的起始地址。
因此,現在再來看va_arg()的實現就應該心中有數了:
#define va_arg(ap,t) ( *(t *)((ap += _intsizeof(t)) - _intsizeof(t)) )
這個巨集做了兩個事情,
①用使用者輸入的型別名對引數地址進行強制型別轉換,得到使用者所需要的值
②計算出本引數的實際大小,將指標調到本引數的結尾,也就是下一個引數的首地址,以便後續處理。
(5)va_end巨集的解釋:x86平臺定義為ap=(char*)0;使ap不再 指向堆疊,而是跟null一樣.有些直接定義為((void*)0),這樣編譯器不會為va_end產生**,例如gcc在linux的x86平臺就是這樣定義的.
在這裡大家要注意一個問題:由於引數的地址用於va_start巨集,所以引數不能宣告為暫存器變數或作為函式或陣列型別. 關於va_start, va_arg, va_end的描述就是這些了,我們要注意的 是不同的作業系統和硬體平臺的定義有些不同,但原理卻是相似的.
三、可變引數在程式設計中要注意的問題
因為va_start, va_arg, va_end等定義成巨集,所以它顯得很愚蠢, 可變引數的型別和個數完全在該函式中由程式**控制,它並不能智慧 地識別不同引數的個數和型別. 有人會問:那麼printf中不是實現了智慧識別引數嗎?
那是因為函式 printf是從固定引數format字串來分析出引數的型別,再呼叫va_arg 的來獲取可變引數的.也就是說,你想實現智慧識別可變引數的話是要通過在自己的程式裡作判斷來實現的. 例如,在c的經典教材《the c programming language》的7.
3節中就給出了一個printf的可能實現方式,由於篇幅原因這裡不再敘述。
四、小結:
1、標準c庫的中的三個巨集的作用只是用來確定可變引數列表中每個引數的記憶體地址,編譯器是不知道引數的實際數目的。
2、在實際應用的**中,程式設計師必須自己考慮確定引數數目的辦法,如
⑴在固定引數中設標誌-- printf函式就是用這個辦法。後面也有例子。
⑵在預先設定一個特殊的結束標記,就是說多輸入一個可變引數,呼叫時要將最後一個可變引數的值設定成這個特殊的值,在函式體中根據這個值判斷是否達到引數的結尾。本文前面的**就是採用這個辦法.
無論採用哪種辦法,程式設計師都應該在文件中告訴呼叫者自己的約定。
3、實現可變引數的要點就是想辦法取得每個引數的地址,取得地址的辦法由以下幾個因素決定:
①函式棧的生長方向
②引數的入棧順序
③cpu的對齊方式
④記憶體地址的表達方式
結合源**,我們可以看出va_list的實現是由④決定的,_intsizeof(n)的引入則是由③決定的,他和①②又一起決定了va_start的實現,最後va_end的存在則是良好程式設計風格的體現,將不再使用的指標設為null,這樣可以防止以後的誤操作。
4、取得地址後,再結合引數的型別,程式設計師就可以正確的處理引數了。理解了以上要點,相信稍有經驗的讀者就可以寫出適合於自己機器的實現來。
printf函式的右對齊性
我忘了 10d是右對齊還是左對齊,它是一種格式,指的是佔10個位置,比如i 5,沒10的話輸出525,有10的話輸出九個空格然後是5八個空格然後25。懂了嗎?手機打字真累 可以控制輸出左對齊或右對齊,即在 和字母之間加入一個 號可 說明輸出為左對齊,否則為右對齊。例如 7d 表示輸出7位整數左對齊 ...
excel中if函式及lookup函式的應用問題
lookup 函式可返回一行或一列區域中或者陣列中的某個值。lookup 函式具有兩種語法形式 向量和陣列。向量形式的 lookup 在一行或一列區域 稱為向量 中查詢值,然後返回另一行或一列區域中相同位置處的值。陣列形式的 lookup 在陣列的第一行或列中查詢指定值,然後返回該陣列的最後一行或列...
炮神中楊志華歷史的原型是誰
為什麼拍電視連續劇不直接說是趙章成本人,為什麼要說是楊志華呢?楊志華 1900 2020 解放軍炮兵總司令,1927年考入雲南講武堂,此後先後擔任國民革命軍炮兵排排長,紅軍炮兵營營長,八路軍新七團團長,解放軍炮兵一師師長,炮兵司令部副司令,志願軍炮兵司令員,1955年被授予少將軍銜,1958年炮擊金...