好吧,也许我第一次提出这个问题的尝试太混淆了。所以,我们再来一次......
在一个带有可变数量参数的函数中,比如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
之类的函数时,生成的代码应该只传递一个参数或将所有结构成员推送到它?
我的一位朋友遇到了一些程序问题,这些程序在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版本,但也许我错了,这是一个未定义的行为。
答案 0 :(得分:8)
预期的行为是未定义的行为。 C语言要求您将正确类型的参数(与格式字符串匹配)传递给printf
,并且没有可以匹配struct
类型的格式字符串。
您可以通过以这种方式传递结构来获取用于打印有意义数据的C实现,但它不可移植,取决于调用约定,并且不可靠。不要这样做。只需传递您想要打印的个人成员。
答案 1 :(得分:2)
看,printf
只是一个简单的功能。
它从右到左评估它的参数,并将它们推到堆栈上。
它执行 no 类型检查。
然后格式字符串需要查看某些内容,即整数,双精度或字符指针。 没别了。
所以只需按照预期传递它。
答案 2 :(得分:0)
如果编译器没有关于如何传递参数的特定信息,例如当没有原型或者在原型具有省略号('...'
)的情况下传递的参数时,编译器会遵循某些传递参数的规则:
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()
的特殊处理。