[英]In C, what is the expected behavior of passing a struct as an argument to printf?
好吧,也許我第一次嘗試提問這個問題太混亂了。 所以,我們再來一次......
在一個帶有可變數量參數的函數中,比如printf
,當你傳遞一個struct時,你應該期望生成什么樣的代碼?
基於這段代碼,我問這個問題:
#include <stdio.h>
struct edge{
int v1, v2;
};
int main(void){
struct edge edges;
edges.v1=5;
edges.v2=20;
printf("'%d'\n", edges);
return 0;
}
在我的Windows框中編譯時,它傳遞2個整數參數:
.file "simple_test_case2.c"
.def ___main; .scl 2; .type 32; .endef
.section .rdata,"dr"
LC0:
.ascii "'%d'\12\0"
.text
.globl _main
.def _main; .scl 2; .type 32; .endef
_main:
pushl %ebp
movl %esp, %ebp
andl $-16, %esp
subl $32, %esp
call ___main
movl $5, 24(%esp)
movl $20, 28(%esp)
movl 24(%esp), %eax
movl 28(%esp), %edx
movl %eax, 4(%esp)
movl %edx, 8(%esp)
movl $LC0, (%esp)
call _printf
movl $0, %eax
leave
ret
.def _printf; .scl 2; .type 32; .endef
但是我的linux框生成的代碼只是將一個內存地址傳遞給它:
.file "simple_test_case2.c"
.section .rodata
.LC0:
.string "'%d'\n"
.text
.align 2
.globl main
.type main, @function
main:
.LFB2:
pushq %rbp
.LCFI0:
movq %rsp, %rbp
.LCFI1:
subq $16, %rsp
.LCFI2:
movl $5, -16(%rbp)
movl $20, -12(%rbp)
movq -16(%rbp), %rsi
movl $.LC0, %edi
movl $0, %eax
call printf
movl $0, %eax
leave
ret
.LFE2:
.size main, .-main
.globl __gxx_personality_v0
.section .eh_frame,"a",@progbits
.Lframe1:
.long .LECIE1-.LSCIE1
.LSCIE1:
.long 0x0
.byte 0x1
.string "zPR"
.uleb128 0x1
.sleb128 -8
.byte 0x10
.uleb128 0x6
.byte 0x3
.long __gxx_personality_v0
.byte 0x3
.byte 0xc
.uleb128 0x7
.uleb128 0x8
.byte 0x90
.uleb128 0x1
.align 8
.LECIE1:
.LSFDE1:
.long .LEFDE1-.LASFDE1
.LASFDE1:
.long .LASFDE1-.Lframe1
.long .LFB2
.long .LFE2-.LFB2
.uleb128 0x0
.byte 0x4
.long .LCFI0-.LFB2
.byte 0xe
.uleb128 0x10
.byte 0x86
.uleb128 0x2
.byte 0x4
.long .LCFI1-.LCFI0
.byte 0xd
.uleb128 0x6
.align 8
.LEFDE1:
.ident "GCC: (GNU) 4.1.2 20080704 (Red Hat 4.1.2-48)"
.section .note.GNU-stack,"",@progbits
所以這是我的問題,當你只將一個結構傳遞給像printf
這樣的printf
,生成的代碼應該只傳遞一個參數或將所有結構成員推送給它?
我的一個朋友在Windows上沒有按預期工作的程序遇到一些問題。 所以我看一下源代碼並將測試用例修改為:
#include <stdio.h>
struct edge{
int v1, v2;
};
int main(void){
struct edge edges;
edges.v1=5;
edges.v2=20;
//This is the expected behavior for me:
printf("'%d' '%d' '%d' '%d'\n", edges.v1, edges.v1, edges.v1, edges.v1);
//This is supposed to work like this? It should pass the whole struct to printf?
printf("'%d' '%d' '%d' '%d'\n", edges, edges, edges, edges);
printf("'%d' '%d' '%d' '%d'\n", edges, edges);
return 0;
}
所以在Windows 7的盒子上測試過
gcc (tdm-1) 4.5.0
Copyright (C) 2010 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
輸出是這樣的:
'5' '5' '5' '5'
'5' '20' '5' '20'
'5' '20' '5' '20'
在一個帶有Linux的盒子上
gcc (GCC) 4.1.2 20080704 (Red Hat 4.1.2-48)
Copyright (C) 2006 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
我得到了這個輸出:
'5' '5' '5' '5'
'5' '5' '5' '5'
'5' '5' '229971840' '-641776368'
linux box one的輸出是我預期的...
然后,我查看了每個編譯器生成的匯編代碼,並在我的Windows框中看到代碼將整個結構傳遞給printf,v1和v2成員(這證明了結果)。 但是在Linux機器上,生成的代碼只傳遞了結構的第一個成員,正如我所期望的那樣。
那么在這種情況下應該是什么行為呢?
對我來說,預期的一個是Linux,但也許我錯了,這是一個未定義的行為。
預期的行為是未定義的行為 。 C語言要求您將正確類型的參數(與格式字符串匹配)傳遞給printf
,並且沒有可以匹配struct
類型的格式字符串。
您可以通過以這種方式傳遞結構來獲取用於打印有意義數據的C實現,但它不可移植,取決於調用約定,並且不可靠。 不要這樣做。 只需傳遞您要打印的個人成員即可。
看, printf
只是一個簡單的功能。 它從右到左評估它的參數,並將它們推到堆棧上。 它沒有類型檢查。
然后格式字符串期望看到某些內容,即整數,雙精度或字符指針。 沒有其他的。
所以只需按照預期傳遞它。
如果編譯器沒有關於如何傳遞參數的特定信息,例如當沒有原型或者在原型具有省略號( '...'
)的情況下傳遞的參數時,編譯器遵循某些傳遞規則論點:
float
類型,它會被提升為double 應用這些默認參數提升后,只需將參數復制到編譯器通常復制參數(通常是堆棧)的任何位置。 因此,struct參數將被復制到堆棧中。
嘗試檢查以下代碼的程序集輸出,您將看到GCC將傳遞給foo()
的結構復制到堆棧:
#include <stdarg.h>
void foo( size_t siz, ...);
struct x
{
int x;
int y;
};
struct y
{
char a;
};
struct z
{
double x;
double y;
int z;
};
int main()
{
struct x x1;
struct y y1;
struct z z1;
foo( sizeof(x1), x1);
foo( sizeof(y1), y1);
foo( sizeof(z1), z1);
return 0;
}
GCC可能沒有將此規則應用於printf()
測試,因為它具有printf()
特殊知識,並且知道格式字符串僅支持某些類型。 如果參數類型與格式說明符的預期不匹配,則會發生未定義的行為(因此編譯器不必執行您可能期望的操作)。 我很驚訝你沒有得到某種警告。 如果省略#include <stddio.h>
,你可能會看到類似“警告:內置函數'printf'的不兼容隱式聲明”的警告,這暗示了GCC對printf()
的特殊處理。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.