tft每日頭條

 > 生活

 > c語言函數的形參和實參

c語言函數的形參和實參

生活 更新时间:2024-12-27 10:56:28

我們知道,數組作為函數參數時退化為一個指針,再加上一個數組長度的參數,便可以處理數組:

void(int n, int arr[]);

其實這就是告訴編譯器,以arr為基準地址,處理n個int類型的數據。

我們也知道,對于C函數,函數參數的壓棧操作是從右向左進行的(字長内存對齊),從最後一個參數開始壓棧,直到将第一個參數壓棧為止。

由此,如果有一個基準地址及數據長度,似乎可以對不确定函數參數個數的函數進行處理了。

在C中,正是以符号“…”作為占位符來進行變參(函數參數數量可變)處理的。

#include <stdio.h> void print(int n, ...) { int *p,i; p = &n 1; for (i=0;i<n;i ) printf("%d\t",p[i]); printf("\n"); return ; } int main() { print(4,12,34,56,78); return 0; }

從上面的代碼可以看到,首先在print()函數中使用了占位符“…”,因此該函數在編譯的時候被當成變參函數來處理,對該函數調用中的參數一一 進行壓棧處理。如下圖所示為函數參數在棧中的存儲結構。上述代碼定義了一個int型指針變最P,由于函數參數的壓棧順序是從右向左,由髙地址到低地址,所以在函數中通過“p=&n 1;”得到的是第一個可變參數的地址,接下來通過一個for循環一一取出函數中的參數。在此使用第一個函數參數表示傳遞可變參數的個數。在使用變參函數的時候,必須知道參數什麼時候結束。如果沒有給出變參函數的個數,直接給出第一個參數,那麼必須約定一個參數作為結束标志。

c語言函數的形參和實參(輕松從底參理解變參函數的實現)1

在stdarg.h中,通過宏來實現變參函數的泛型化:

#include <stdio.h> #include <stdarg.h> void print(int n,...) { int arg,i; va_list p; va_start(p,n); for(i=0;i<n;i ) { arg = va_arg(p,int); printf("%d\t",arg); } printf("\n"); va_end(p); return ; } int main() { print(3,21,32,54); return 0; }

宏定義如下:

typedef char * va_list; #define _INTSIZEOF(n) ( (sizeof(n) sizeof(int) - 1) & ~(sizeof(int) - 1) ) #define va_start(ap,v) ( ap = (va_list)&v _INTSIZEOF(v) ) #define va_arg(ap,t) ( *(t *)((ap = _INTSIZEOF(t)) - _INTSIZEOF(t)) ) #define va_end(ap) ( ap = (va_list)0 )

宏va_start(ap,v)可以取得第二個參數的地址,存儲到char *類型的ap。

宏_INTSIZEOF(v)用于于内存對齊(編譯器都有默認的内存對齊,通常是一個字長)。

宏va_arg(ap,t)用于實現ap的的類型轉換,并通過 =實現ap的副作用,指向下一下參數的地址。

宏va_end(ap)用于将指針ap置0。

~(sizeof(int) - 1)的值對于32位系統是-4,二進制為:

1 1111111 11111111 11111111 11111100

宏_INTSIZEOF(v)的表達式能夠實現類型v按4個字節(32位系統)進行類型對齊(編譯器會默認按一個字長來對齊内存,int一般也是定義為一個字長的長度)。

以下實例是實現n個int類型做函數參數的實例:

#include <stdio.h> typedef char * va_list; #define va_start(ap,v) ( ap = (va_list)&v sizeof(v) ) #define va_arg(ap,t) ( *(t *)((ap = sizeof(t)) - sizeof(t)) ) #define va_end(ap) ( ap = (va_list)0 ) void print(int n,...) { int arg,i; va_list p; va_start(p,n); for(i=0;i<n;i ) { arg = va_arg(p,int); printf("%d\t",arg); } printf("\n"); va_end(p); return ; } int main() { print(4,12,34,56,78); return 0; }

因為int類型的長度與編譯器默認内存對齊的長度一緻,所以不需要内存對齊的宏_INTSIZEOF(v)。

如果實現n個字符類型的函數參數,就需要添加内存對齊的宏了:

#include <stdio.h> typedef char * va_list; #define va_start(ap,v) ( ap = (va_list)&v sizeof(v) ) #define va_arg(ap,t) ( *(t *)((ap = sizeof(t)) - sizeof(t)) ) #define va_end(ap) ( ap = (va_list)0 ) void print(int n,...) { int arg,i; va_list p; va_start(p,n); for(i=0;i<n;i ) { arg = va_arg(p,char); printf("%c\t",arg); } printf("\n"); va_end(p); return ; } int main() { print(4,'A','B','C','D'); return 0; }

如果是不同類型的n個函數參數呢?則需要逐一分析類型來實現數據解析和地址的偏轉了:

#include <stdio.h> #include <stdlib.h> typedef char * va_list; #define _INTSIZEOF(n) ( (sizeof(n) sizeof(int) - 1) & ~(sizeof(int) - 1) ) #define va_start(ap,v) ( ap = (va_list)&v _INTSIZEOF(v) ) #define va_arg(ap,t) ( *(t *)((ap = _INTSIZEOF(t)) - _INTSIZEOF(t)) ) #define va_end(ap) ( ap = (va_list)0 ) void myprintf(char* fmt, ...) { va_list p; char c; va_start(p,fmt); do { c =*fmt; if (c != '%') { putchar(c); } else { switch(* fmt) { case 'd': printf("%d",*((int*)p)); break; case 'c': printf("%c",*((int*)p)); break; case 'f': printf("%3.2f",*((double*)p)); va_arg(p,int); default: break; } va_arg(p,int); } fmt; }while (*fmt != '\0'); va_end(p); return; } void main() { int a = 12; short b = 56; char c = 'A'; double f = 123.2; myprintf("a = %d\t b = %d\t c = %c\t f = %f\n",a,b,c,f); return ; }

-End-

,

更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!

查看全部

相关生活资讯推荐

热门生活资讯推荐

网友关注

Copyright 2023-2024 - www.tftnews.com All Rights Reserved