最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C語言可變參數(shù)列表的用法與深度剖析

 更新時間:2022年02月07日 15:37:43   作者:^jhao^  
這篇文章主要給大家介紹了關于C語言可變參數(shù)列表的相關資料,文中通過實例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下

前言

可變參數(shù)列表,使用起來像是數(shù)組,學習過函數(shù)棧幀的話可以發(fā)現(xiàn)實際上他也就是在棧區(qū)定義的一塊空間當中連續(xù)訪問,不過他不支持直接在中間部分訪問。

聲明: 以下所有測試都是在x86,vs2013下完成的。

一、可變參數(shù)列表是什么?

在我們初始C語言的第一節(jié)課的時候我們就已經(jīng)接觸了可變參數(shù)列表,在printf的過程當中我們通常可以傳遞大量要打印的參數(shù),但是我們卻不知道他是如何做到的,今天就帶大家剖析它。

二、怎么用可變參數(shù)列表

首先我們要引入windows.h的頭文件

然后我們先要介紹以下幾個宏。在這里我們先簡述它的功能,在后面會有詳細的講解,這里是為了方便大家入門。

typedef char* va_list;  //類型的重定義

#define _ADDRESSOF(v) (&(v))//一個取地址的宏。

1._ADDRESSOF:取傳入變量的地址。

#define _INTSIZEOF(n) \
 ((sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1))

2._INTSIZEOF:該宏功能是讓n的類型往4的倍數(shù)上取整。

#define _INTSIZEOF(n)\
  ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )

下面一段代碼進行解釋:

#pragma pack(1)//設置默認對其數(shù)為1
struct A
{
	char ch[11];
};

int main()
{
	printf("int : %d\n", _INTSIZEOF(int));
	printf("double: %d\n", _INTSIZEOF(double));
	printf("short: %d\n", _INTSIZEOF(short));
	printf("float: %d\n", _INTSIZEOF(float));
	printf("long long int: %d\n", _INTSIZEOF(long long int));
	printf("struct A:%d\n", _INTSIZEOF(struct A));
	return 0;
}

結果:

3.__crt_va_start_a:取變量v的地址強轉為char*然后向指向v類型對其數(shù)后,即找到第一個可變參數(shù)列表當中的變量!

#define __crt_va_start_a(ap, v) \
((void)(ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v)))

4.__crt_va_arg:將ap提前指向下一個要訪問的位置,并且返回當前訪問的內容。 注意+=后ap指向下一個要訪問的地址,但是返回的內容是當前的。

#define __crt_va_arg(ap, t)   \
      (*(t*)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)))

5.__crt_va_end:將ap置成NULL。

#define __crt_va_end(ap)\
 ((void)(ap = (va_list)0))

緊接著我們看一下以下幾個定義。

#define va_start __crt_va_start
#define va_arg   __crt_va_arg
#define va_end   __crt_va_end

測試:找一組不存放在數(shù)組當中的最大的一個數(shù)據(jù)返回。

int Find_Max(int num, ...)
{
	//定義一個char* 的變量arg
	va_list arg;
	//將arg指向第一個可變參數(shù)
	va_start(arg, num);
	//將max置成第一個可變參數(shù),然后arg指向下一個可變參數(shù)
	int max = va_arg(arg, int);
	//循環(huán)num-1次,訪問完剩下的可變參,找到最大的賦值給max
	for (int i = 1; i < num; ++i)
	{
		int r;
		if (max < (r = va_arg(arg, int)))
		{
			max = r;
		}
	}
	//將arg指針變量置成NULL,避免野指針
	va_end(arg);
	return max;
}
int main()
{
	int ret = Find_Max(5, 0x1, 0x2, 0x3, 0x4, 0x5);
	printf("ret :%d\n", ret);
	return 0;
}

結果:

三、對于宏的深度剖析

雖然在Linux下的進程地址空間是由高到低排列的,但是由于vs下的內存是從低字節(jié)到高字節(jié)的,我們的棧會和linux下畫的不太一樣,但是都是朝著低地址方向擴展的。這是方便大家理解。

Linux的進程地址空間示意圖:

代碼棧幀示意圖:

隱式類型轉換

舉個栗子,當我們執(zhí)行下面的代碼,當我們以char類型傳參,但函數(shù)體依舊以int的步長獲取,此時會出錯嗎?

#include<stdio.h>
#include<windows.h>
int Find_Max(int num, ...)
{
	//定義一個char* 的變量arg
	va_list arg;
	//將arg指向第一個可變參數(shù)
	va_start(arg, num);
	//將max置成第一個可變參數(shù),然后arg指向下一個可變參數(shù)
	int max = va_arg(arg, int);
	//循環(huán)num-1次,訪問完剩下的可變參,找到最大的賦值給max
	for (int i = 1; i < num; ++i)
	{
		int r;
		if (max < (r = va_arg(arg, int)))
		{
			max = r;
		}
	}
	//將arg指針變量置成NULL,避免野指針
	va_end(arg);
	return max;
}

int main()
{
	char a = '1'; //ascii值: 49
	char b = '2'; //ascii值: 50
	char c = '3'; //ascii值: 51
	char d = '4'; //ascii值: 52
	char e = '5'; //ascii值: 53
	int ret = Find_Max(5, a, b, c, d, e);
	//int ret = Find_Max(5, 0x1, 0x2, 0x3, 0x4, 0x5);
	printf("ret :%d\n", ret);
	system("pause");
}

答案:

不會的,由于壓棧的時候是通過寄存器傳參的,32位下的寄存器就是4個字節(jié)。

壓棧時的匯編:其中第一條不是mov,而是movsx,即匯編語言數(shù)據(jù)傳送指令MOV的變體。帶符號擴展,并傳送。也就是整形提升。

同理:用float傳參,用double字長走,也是沒有問題的。

總結

所以我們習慣在函數(shù)體內部(Find_Max)用int/double為長度走,而傳參的時候我們可以用char/short/float等等類型。

注意:

64位下的定義和32位下差異是很大的。

為什么按照4字節(jié)對齊:

先前講到在短整型,在壓棧的過程中會發(fā)生整形提升,那么從棧幀中要拿到對應的數(shù)據(jù)也要按照對應的方法提取。

_INTSIZEOF的數(shù)學理解:

_INTSIZEOF(n)的意思:計算一個最小數(shù)字x,滿足 x>=n && x%4==0,n表示sizeof(n)的值。即該類型的大小要滿足往n的整數(shù)倍對齊,且最小不能小于n。

以4字節(jié)對齊為栗子:

n%4 == 0,則 ret = n;

n %4 != 0 , 則 ret = (n+ 4 - 1)/4 *4;

(n+ 4 - 1)/4 -->假設 n為1到4,那么(n + 4 - 1)/4的結果都是1,再乘上對其數(shù)4就是以4對齊的最小對齊數(shù)了。就能將這4個數(shù)值范圍最小對齊倍數(shù)控制在同一個值。

我們觀察(n+ 4 -1)/4 *4,/4實際上就是將二進制序列往右移,*4就是把二進制序列往左移動,這一來一回實際上就是把最低兩位置成0,那么我們還可以簡化成:
(n+ 4 -1) & ~3 ,也就是源碼當中的定義了?。?/p>

#define _INTSIZEOF(n)\
  ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )

對兩個函數(shù)的重新認知

對printf的理解:

在上述的例子中,宏是無法判斷實際存在參數(shù)的數(shù)量,以及實際參數(shù)的類型的,那么在printf當中,必定有能夠確定參數(shù)數(shù)量以及辨別參數(shù)類型的方法,實際上也就是%c,%d,%lf,其中%的數(shù)量除了%%外的%的數(shù)量實際上就能讓我們得知參數(shù)的數(shù)量,而%c,%d,實際上也就說明了對應的類型。

對exec系列的理解:

進程控制,當時講述了實際上只有一個系統(tǒng)調用execve,其他函數(shù)exec函數(shù)最終都是要調用execve函數(shù),那么是如何實現(xiàn)從參數(shù)l到v這個過程的呢?

答案:

實際上訪問到null為止,傳參的數(shù)量用一個count一直計數(shù)就能拿到,而類型毫無疑問就是char*,我們可以用strlen去計算要走多長。(不過兩個char數(shù)組通常會間隔多8個字節(jié))

總結

到此這篇關于C語言可變參數(shù)列表的文章就介紹到這了,更多相關C語言可變參數(shù)列表內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • QT獲取顯示當前時間和日期的方法(用QTime,QDate和QDateTime)

    QT獲取顯示當前時間和日期的方法(用QTime,QDate和QDateTime)

    獲取當期日期時間在我們日常開發(fā)中經(jīng)常會遇到,下面這篇文章主要給大家介紹了關于QT獲取顯示當前時間和日期的相關資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2022-08-08
  • C++?select模型簡單聊天室的實現(xiàn)示例

    C++?select模型簡單聊天室的實現(xiàn)示例

    本文主要介紹了C++?select模型簡單聊天室的實現(xiàn)示例,使用CMake項目進行開發(fā),文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • C++實例分析講解臨時對象與右值引用的用法

    C++實例分析講解臨時對象與右值引用的用法

    對性能來說,許多的問題都需要和出現(xiàn)頻率及本身執(zhí)行一次的開銷掛鉤,有些問題雖然看似比較開銷較大,但是很少會執(zhí)行到,那也不會對程序有大的影響;同樣一個很小開銷的函數(shù)執(zhí)行很頻繁,同樣會對程序的執(zhí)行效率有很大影響。本章中作者主要根據(jù)臨時對象來闡述這樣一個觀點
    2022-08-08
  • 你真的知道C++對象大小嗎?

    你真的知道C++對象大小嗎?

    這篇文章主要給大家介紹了關于C++對象大小的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-01-01
  • c語言中全局變量的設置方式

    c語言中全局變量的設置方式

    這篇文章主要介紹了c語言中全局變量的設置方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • 深入C++中struct與class的區(qū)別分析

    深入C++中struct與class的區(qū)別分析

    本篇文章是對C++中struct與class的區(qū)別進行了詳細的分析介紹,需要的朋友參考下
    2013-05-05
  • C++實現(xiàn)將s16le的音頻流轉換為float類型

    C++實現(xiàn)將s16le的音頻流轉換為float類型

    這篇文章主要為大家詳細介紹了如何利用C++實現(xiàn)將s16le的音頻流轉換為float類型,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起了解一下
    2023-04-04
  • C++實現(xiàn)學生管理系統(tǒng)

    C++實現(xiàn)學生管理系統(tǒng)

    這篇文章主要為大家詳細介紹了C++實現(xiàn)學生管理系統(tǒng),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-07-07
  • 一文帶你入木三分地理解字符串KMP算法以及C++實現(xiàn)

    一文帶你入木三分地理解字符串KMP算法以及C++實現(xiàn)

    KMP算法是一種改進的字符串匹配算法,KMP算法的核心是利用匹配失敗后的信息,盡量減少模式串與主串的匹配次數(shù)以達到快速匹配的目的。本文就來和大家聊聊KMP算法的原理與實現(xiàn),需要的可以參考一下
    2022-12-12
  • Qt自定義實現(xiàn)一個等待提示Ui控件

    Qt自定義實現(xiàn)一個等待提示Ui控件

    等待樣式控件是我們在做UI時出場率還挺高的控件之一,所以這篇文章主要為大家介紹了Qt如何自定義一個好看的等待提示Ui控件,感興趣的可以了解下
    2024-01-01

最新評論

冕宁县| 湖口县| 泽州县| 互助| 淮南市| 固镇县| 胶南市| 班戈县| 西华县| 儋州市| 朔州市| 新邵县| 根河市| 自贡市| 奉节县| 旌德县| 维西| 东丽区| 安宁市| 原阳县| 天水市| 湘西| 比如县| 峨山| 阿克苏市| 宜宾县| 承德市| 余江县| 仁化县| 北宁市| 隆安县| 彭泽县| 郧西县| 贞丰县| 偃师市| 丹江口市| 五家渠市| 枣庄市| 夹江县| 巴林左旗| 南投县|