目錄
可變引數串列
1. 求兩個資料中的最大值
2. 如果將引數改成char型別,求char型別變數中的最大值,代碼會有問題嗎?
3. 結果并未受影響,可是,我們決議的時候,是按照va_arg(arg, int)來決議的,這是為什么?
4. 注意事項
5. 原理
6. 先看看這幾個宏的含義
7. 原理圖
本章節文章是作者通過觀看《C語言深度剖析》等各種資料總結的精華,基礎部分省略了不少,是為了讓大家能夠更加深入了解C語言的魅力!因為為了避免與之前的文章發生贅述,所以就直接講作者認為的精華部分哈!現在正文開始!
誰都不能阻擋你成為更優秀的人,
在這里《C語言深度剖析》就結束了哈,作者后面可能會把前面的各個文章再總結一下,讓大家更好地去查閱,接下來就慢慢踏入資料結構了哈!作者也期待很久了,讓我們一起進步哈!
可變引數串列
1. 求兩個資料中的最大值
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <windows.h>
int FindMax(int x, int y)
{
if (x > y){
return x;
}
return y;
}
int main()
{
int x = 0;
int y = 0;
printf("Please Eneter Two Data# ");
scanf("%d %d", &x, &y);
int max = FindMax(x, y);
printf("max = %d\n", max);
system("pause");
return 0;
}
求任意多個資料中的最大值 ( 至少一個 ) ,要求不能使用陣列因為目前引數個數不確定,那么函式撰寫的時候,引數個數也無法確定,換句話說,函式也就沒法撰寫不過, C 提供了滿足該場景的解決方案:可變引數串列
#include <stdio.h>
#include <windows.h>
//num:表示傳入引數的個數
int FindMax(int num, ...)
{
va_list arg; //定義可以訪問可變引數部分的變數,其實是一個char*型別
va_start(arg, num); //使arg指向可變引數部分
int max = va_arg(arg, int); //根據型別,獲取可變引數串列中的第一個資料
for (int i = 0; i < num - 1; i++){//獲取并比較其他的
int curr = va_arg(arg, int);
if (max < curr){
max = curr;
}
}
va_end(arg); //arg使用完畢,收尾作業,本質就是講arg指向NULL
return max;
}
int main()
{
int max = FindMax(5,11,22,33,44,55);
printf("max = %d\n", max);
system("pause");
return 0;
}
1. 如果將引數改成char型別,求char型別變數中的最大值,代碼會有問題嗎?
#include <stdio.h>#include <windows.h>//num: 表示傳入引數的個數int FindMax ( int num , ...){va_list arg ; // 定義可以訪問可變引數部分的變數,其實是一個 char* 型別va_start ( arg , num ); // 使 arg 指向可變引數部分int max = va_arg ( arg , int ); // 根據型別,獲取可變引數串列中的第一個資料for ( int i = 0 ; i < num - 1 ; i ++ ){ // 獲取并比較其他的int curr = va_arg ( arg , int );if ( max < curr ){max = curr ;}}va_end ( arg ); //arg 使用完畢,收尾作業,本質就是講 arg 指向 NULLreturn max ;}int main (){char a = '1' ; //ascii 值 : 49char b = '2' ; //ascii 值 : 50char c = '3' ; //ascii 值 : 51char d = '4' ; //ascii 值 : 52char e = '5' ; //ascii 值 : 53int max = FindMax ( 5 , a , b , c , d , e );printf ( "max = %d\n" , max );system ( "pause" );return 0 ;}
2. 結果并未受影響,可是,我們決議的時候,是按照va_arg(arg, int)來決議的,這是為什么?

通過查看匯編,我們看到,在可變引數場景下:1. 實際傳入的引數如果是char,short,float,編譯器在編譯的時候,會自動進行提升(通過查看匯編,我們都能看到)2. 函式內部使用的時候,根據型別提取資料,更多的是通過int或者double來進行
2. 注意事項
1.可變引數必須從頭到尾逐個訪問,如果你在訪問了幾個可變引數之后想半途終止,這是可以的,但是,如果你想一開始就訪問引數串列中間的引數,那是不行的,2.引數串列中至少有一個命名引數,如果連一個命名引數都沒有,就無法使用 va_start ,3.這些宏是無法直接判斷實際存在引數的數量,4.這些宏無法判斷每個引數的是型別,5.如果在 va_arg 中指定了錯誤的型別,那么其后果是不可預測的,
3. 原理
1. 可變引數串列對應的函式,最終呼叫也是函式呼叫,也要形成堆疊幀2. 堆疊幀形成前,臨時變數是要先入堆疊的,根據之前所學,引數之間位置關系是固定的3. 通過上面匯編的學習,發現了短整型在可變引數部分,會默認進行整形提升,那么函式內部在提取該資料的時候,就要考慮提升之后的值,如果不加考慮,獲取資料可能會報錯或者結果不正確
4. 先看看這幾個宏的含義
//va_list其實就是char*型別,方便后續按照位元組進行指標移動typedef char * va_list;#define va_start _crt_va_start#define va_arg _crt_va_arg#define va_end _crt_va_end//這個宏特別好理解,結合堆疊幀中臨時引數的壓入位置#define _crt_va_start(ap,v) ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )//這個設計特別巧妙,先讓ap指向下個元素,然后使用相對位置-偏移量,訪問當前元素,//訪問了當前資料的同時,還讓ap指向了后續元素,一舉兩得,#define _crt_va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )//這個宏特別好理解,將ap指標設定為NULL#define _crt_va_end(ap) ( ap = (va_list)0 )//取引數的地址,也很好理解#define _ADDRESSOF(v) ( &(v) )//難點是這個,不太好理解#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )前提:為了后面方便表述,我們假設sizeof(n)的值是n(char 1,short 2, int 4)我們在32位平臺,vs2013下測驗,sizeof(int)大小是4,其他情況我們不考慮_INTSIZEOF(n)的意思:計算一個最小數字x,滿足 x>=n && x%4==0,其實就是一種4位元組對齊的方式是什么:比如n是:1,2,3,4 對n進行向 sizeof(int) 的最小整數倍取整的問題 就是 4比如n是:5,6,7,8 對n進行向 sizeof(int) 的最小整數倍取整的問題 就是 8為什么:要有這個4位元組對齊:結合之前堆疊幀的學習和上面代碼的測驗結果怎么辦到的:第一步理解:4的倍數既然是4的最小整數倍取整,那么本質是:x=4*m,m是具體幾倍,對7來講,m就是2,對齊的結果就是8而m具體是多少,取決于n是多少如果n能整除4,那么m就是n/4如果n不能整除4,那么m就是n/4+1上面是兩種情況,如何合并成為一種寫法呢?常見做法是 ( n+sizeof(int)-1) )/sizeof(int) -> (n+4-1)/4如果n能整除4,那么m就是(n+4-1)/4->(n+3)/4, +3的值無意義,會因取整自動消除,等價于 n/4如果n不能整除4,那么n=最大能整除4部分+r,1<=r<4 那么m就是 (n+4-1)/4->(能整除4部分+r+3)/4,其中4<=r+3<7 -> 能整除4部分/4 + (r+3)/4 -> n/4+1第二步理解:最小4位元組對齊數搞清楚了滿足條件最小是幾倍問題,那么,計算一個最小數字x,滿足 x>=n && x%4==0,就變成了((n+sizeof(int)-1)/sizeof(int))[最小幾倍] * sizeof(int)[單位大小] -> ((n+4-1)/4)*4這樣就能求出來4位元組對齊的資料了,其實上面的寫法,在功能上,已經和源代碼中的宏等價了,第三步理解:理解源代碼中的宏拿出簡潔寫法:((n+4-1)/4)* 4,設w=n+4-1, 那么運算式可以變化成為 (w/4)*4,而4就是2^2,w/4,不就相當于右移兩位嗎?,再次*4不就相當左移兩位嗎?先右移兩位,在左移兩位,最終結果就是,最后2個位元位被清空為0!需要這么費勁嗎?w & ~3 不香嗎?所以,簡潔版:(n+4-1) & ~(4-1)原碼版:( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) ),無需先/,再*
5 . 原理圖
#include <stdio.h>#include <windows.h>//num: 表示傳入引數的個數int FindMax ( int num , ...){va_list arg ; // 定義可以訪問可變引數部分的變數,其實是一個 char* 型別va_start ( arg , num ); // 使 arg 指向可變引數部分int max = va_arg ( arg , int ); // 根據型別,獲取可變引數串列中的第一個資料for ( int i = 0 ; i < num - 1 ; i ++ ){ // 獲取并比較其他的int curr = va_arg ( arg , int );if ( max < curr ){max = curr ;}}va_end ( arg ); //arg 使用完畢,收尾作業,本質就是講 arg 指向 NULLreturn max ;}int main (){//為了方便查看,我們引數換成直觀的引數int max = FindMax ( 5 , 0x11 , 0x22 , 0x33 , 0x44 , 0x55 );printf ( "max = %d\n" , max );system ( "pause" );return 0 ;}

今天的內容就到這里了哈!!!
要是認為作者有一點幫助你的話!
就來一個點贊加關注吧!!!當然訂閱是更是求之不得!
最后的最后謝謝大家的觀看!!!
你們的支持是作者寫作的最大動力!!!
下期見哈!!!
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/397424.html
標籤:其他
上一篇:R語言資料格式轉換函式、資料型別判斷函式(numeric、character、vector、matrix、data.frame、factor、logical)、R語言資料格式型別轉換
