В C, каково ожидаемое поведение передачи структуры в качестве аргумента в printf? - PullRequest
2 голосов
/ 26 июня 2011

Вторая попытка

Хорошо, возможно, моя первая попытка задать вопрос была слишком запутанной.Итак, здесь мы снова идем ...

В функции, которая принимает переменное число аргументов, например, printf, когда вы передаете структуру, какой тип сгенерированного кода вы ожидаете?

Я спрашиваю это, основываясь на этом коде:

#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 box с

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, но, возможно, я ошибаюсь, и это случай неопределенного поведения.

Ответы [ 3 ]

8 голосов
/ 26 июня 2011

Ожидаемое поведение: неопределенное поведение . Язык C требует, чтобы вы передавали аргументы правильного типа (соответствующие строке формата) в printf, и не существует строки формата, которая может соответствовать struct типам.

Возможно, вам удастся получить реализацию C, которую вы используете для печати значимых данных, передавая таким образом структуры, но она непереносима, зависит от соглашения о вызовах и ненадежна. Не делай этого. Просто передайте отдельных членов, которых вы хотите напечатать.

2 голосов
/ 26 июня 2011

Смотри, printf - это простая функция.Он оценивает свои аргументы справа налево и просто помещает их в стек.Он не проверяет тип.

Затем строка формата ожидает увидеть определенные вещи, а именно, целые, двойные или символьные указатели.Ничего другого.

Так что просто передайте это, что он ожидает.

0 голосов
/ 26 июня 2011

Если у компилятора нет конкретной информации о том, как должен передаваться аргумент, например, когда нет прототипа или для аргументов, которые передаются, когда у прототипа есть многоточие ('...'), компилятор следует определенным правилам дляпередавая аргументы:

  • применяются целочисленные преобразования (обратите внимание, что это не относится к структурам)
  • , если аргумент имеет тип float, он повышается до двойного

После применения этих расширений аргументов по умолчанию аргумент просто копируется туда, куда компилятор обычно копирует аргументы (как правило, в стек).Поэтому аргумент 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().

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...