[英]In C, what is the expected behavior of passing a struct as an argument to printf?
Ok, maybe my first attempt to ask the question was too confuse. 好吧,也许我第一次尝试提问这个问题太混乱了。 So, here we go again...
所以,我们再来一次......
In a function that takes a variable number of arguments, like printf
, when you pass a struct what is the kind of code generated you should expected? 在一个带有可变数量参数的函数中,比如
printf
,当你传递一个struct时,你应该期望生成什么样的代码?
I'm asking this, based on this code: 基于这段代码,我问这个问题:
#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;
}
When compiling this on my windows box, it pass 2 integer arguments: 在我的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
But the code generated by my linux box, just pass one memory address to it: 但是我的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
So this is my question, when you pass only one struct to a function like printf
, the code generated should pass just one argument or push all struct members to it? 所以这是我的问题,当你只将一个结构传递给像
printf
这样的printf
,生成的代码应该只传递一个参数或将所有结构成员推送给它?
A friend of mine was having some problems with a program that wasn't working as expected on Windows. 我的一个朋友在Windows上没有按预期工作的程序遇到一些问题。 So I took a look at the source code and trimmed down the test case to this:
所以我看一下源代码并将测试用例修改为:
#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;
}
So tested on a Windows 7 box with 所以在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.
And the output is this: 输出是这样的:
'5' '5' '5' '5'
'5' '20' '5' '20'
'5' '20' '5' '20'
On a linux box with 在一个带有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.
I got this output: 我得到了这个输出:
'5' '5' '5' '5'
'5' '5' '5' '5'
'5' '5' '229971840' '-641776368'
The output from linux box one, is the one that i expected... linux box one的输出是我预期的...
Then, I took a look at the assembly code generated by each compiler and saw that on my Windows box, the code was passing the whole struct to printf, both v1 and v2 members (and this justifies the results). 然后,我查看了每个编译器生成的汇编代码,并在我的Windows框中看到代码将整个结构传递给printf,v1和v2成员(这证明了结果)。 But on the Linux box, the code generated passed just the first member of the struct, as I expected.
但是在Linux机器上,生成的代码只传递了结构的第一个成员,正如我所期望的那样。
So what should be the behavior in this case? 那么在这种情况下应该是什么行为呢?
For me, the expected one is the Linux one, but maybe I'm wrong and this is a case of undefined behavior. 对我来说,预期的一个是Linux,但也许我错了,这是一个未定义的行为。
The expected behavior is undefined behavior . 预期的行为是未定义的行为 。 The C language requires that you pass arguments of the correct type (matching the format string) to
printf
, and there is no format string which can match struct
types. C语言要求您将正确类型的参数(与格式字符串匹配)传递给
printf
,并且没有可以匹配struct
类型的格式字符串。
You may be able to get the C implementation you're using to print meaningful data by passing structures this way, but it's non-portable, dependent upon calling convention, and unreliable. 您可以通过以这种方式传递结构来获取用于打印有意义数据的C实现,但它不可移植,取决于调用约定,并且不可靠。 Don't do it.
不要这样做。 Just pass the individual members you want to print.
只需传递您要打印的个人成员即可。
Look, printf
is just a simple function. 看,
printf
只是一个简单的功能。 It evaluates its arguments from right-to-left and just pushes them on the stack. 它从右到左评估它的参数,并将它们推到堆栈上。 It does no type checking.
它没有类型检查。
Then the format string expects to see certain things, namely ints, doubles, or character pointers. 然后格式字符串期望看到某些内容,即整数,双精度或字符指针。 Nothing else.
没有其他的。
So just pass it what it expects. 所以只需按照预期传递它。
If the compiler doesn't have specific information about how the argument should be passed, such as when there's no prototype or for arguments that are passed where the protoype have an ellipsis ( '...'
), the compiler follows certain rules for passing the arguments: 如果编译器没有关于如何传递参数的特定信息,例如当没有原型或者在原型具有省略号(
'...'
)的情况下传递的参数时,编译器遵循某些传递规则论点:
float
type it's promoted to double float
类型,它会被提升为double After these default argument promotions are applied, the argument is simply copied to wherever the compiler normally copies arguments (generally, the stack). 应用这些默认参数提升后,只需将参数复制到编译器通常复制参数(通常是堆栈)的任何位置。 So a struct argument would be copied to the stack.
因此,struct参数将被复制到堆栈中。
Try examining the assembly output for the following code, and you'll see that GCC will copy the structures passed to foo()
to the stack: 尝试检查以下代码的程序集输出,您将看到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 might not be applying this rule to your printf()
test because it has special knowledge of printf()
and knows that only certain types are supported by the format string. GCC可能没有将此规则应用于
printf()
测试,因为它具有printf()
特殊知识,并且知道格式字符串仅支持某些类型。 If an argument type doesn't match what's expected by a format specifier then undefined behavior occurs (so the compiler doesn't have to do what you might expect otherwise). 如果参数类型与格式说明符的预期不匹配,则会发生未定义的行为(因此编译器不必执行您可能期望的操作)。 I'm surprised you don't get some sort of warning.
我很惊讶你没有得到某种警告。 If you leave out the
#include <stddio.h>
, you'll probably see a warning something like, "warning: incompatible implicit declaration of built-in function 'printf'", which hints at GCC's special treatment of printf()
. 如果省略
#include <stddio.h>
,你可能会看到类似“警告:内置函数'printf'的不兼容隐式声明”的警告,这暗示了GCC对printf()
的特殊处理。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.