【问题标题】:Conversion from size_t to int从 size_t 到 int 的转换
【发布时间】:2013-05-14 11:21:12
【问题描述】:

关注这个thread ...

对于这段代码:

#include <stdio.h>

int main(void)
{
    int i;
    size_t u;

    for (i = 0; i < 10; i++) {
        u = (size_t)i;
        printf("i = %d, u = %zu\n", i, u);
    }
    return 0;
}

汇编中的输出是:

编辑:使用 -O2 编译

    .file   "demo.c"
    .section    .rodata.str1.1,"aMS",@progbits,1
.LC0:
    .string "i = %d, u = %zu\n"
    .section    .text.startup,"ax",@progbits
    .p2align 4,,15
    .globl  main
    .type   main, @function
main:
.LFB3:
    .cfi_startproc
    pushq   %rbx
    .cfi_def_cfa_offset 16
    .cfi_offset 3, -16
    xorl    %ebx, %ebx
    .p2align 4,,10
    .p2align 3
.L2:
    movq    %rbx, %rdx
    movl    %ebx, %esi
    xorl    %eax, %eax
    movl    $.LC0, %edi
    addq    $1, %rbx
    call    printf
    cmpq    $10, %rbx
    jne .L2
    xorl    %eax, %eax
    popq    %rbx
    .cfi_def_cfa_offset 8
    ret
    .cfi_endproc
.LFE3:
    .size   main, .-main
    .ident  "GCC: (Debian 4.7.2-5) 4.7.2"
    .section    .note.GNU-stack,"",@progbits

转换u = (size_t)i; 是否消耗额外的周期?

【问题讨论】:

  • 看起来你没有打开优化器,这让它毫无意义。
  • Tnak 你汉斯,见编辑

标签: c assembly type-conversion


【解决方案1】:

是的,因为代码已经发布,当然。您的转化在这里:

movl    -4(%rbp), %eax
cltq
movq    %rax, -16(%rbp)

当然,这段代码是未经优化的,所以这不是一个很公平的比较。如果您通过优化对其进行编译,编译器可能会意识到这些值始终为正,并且只需从包含i 的任何寄存器移动到包含第三个参数的%rdx

编辑:

正如怀疑的那样,优化后的代码基本上没有开销。在这种情况下,编译器将循环转换为向上计数u,并从u 派生i,而不是相反,因此%rbx 用于循环,i 的值只是使用%ebx,它是%rbx 的低32 位——所以在这个例子中没有开销。我强调这一点,因为很可能其他情况int 转换为size_t 会受到惩罚。这完全取决于情况。

【讨论】:

  • 谢谢 Mats,现在编译时使用了 -O2 标志(见编辑)
【解决方案2】:

是的,确实如此,因为它将内部表示从 32 位更改为 64 位。 具体来说,

.L3:
    movl    -4(%rbp), %eax
    cltq
    movq    %rax, -16(%rbp)
    movq    -16(%rbp), %rdx

读取i,执行符号扩展并复制到%rdx。我不确定为什么这个值必须通过堆栈 - 正如 mats 指出的那样,这看起来像是来自非优化编译器运行的代码。

编辑

在优化的汇编代码中,循环计数器被维护为更广泛的数据类型。公平地说,寄存器之间的movs 在运行时周期上与 quad 或 dword 没有区别(实际上它们没有区别:参见表 C-16 in intels pertinent doc,由 this SO post 引用。

【讨论】:

  • 谢谢,我明白了
【解决方案3】:

不确定这是否是为您消耗周期的实际分配 我相信这是消耗周期的任务

例如 看看这个 t1.c

#include <stdio.h>

int main(void)
{
    int i;
    size_t u;

    for (i = 0; i < 10; i++) {
        printf("i = %d, u = %zu\n", i, u);
    }
    return 0;
}

以及 t1.c 的组件

        .file   "t1.c"
        .section        .rodata
.LC0:
        .string "i = %d, u = %zu\n"
        .text
.globl main
        .type   main, @function
main:
        pushl   %ebp
        movl    %esp, %ebp
        andl    $-16, %esp
        subl    $32, %esp
        movl    $0, 24(%esp)
        jmp     .L2
.L3:
        movl    $.LC0, %eax
        movl    28(%esp), %edx
        movl    %edx, 8(%esp)
        movl    24(%esp), %edx
        movl    %edx, 4(%esp)
        movl    %eax, (%esp)
        call    printf
        addl    $1, 24(%esp)
.L2:
        cmpl    $9, 24(%esp)
        jle     .L3
        movl    $0, %eax
        leave
        ret
        .size   main, .-main
        .ident  "GCC: (GNU) 4.4.6 20110731 (Red Hat 4.4.6-3)"
        .section        .note.GNU-stack,"",@progbits

在上述情况下,暂时没有任何任务

第二种情况t2.c

#include <stdio.h>

int main(void)
{
    int i;
    size_t u;

    for (i = 0; i < 10; i++) {
        i = (size_t) u;
        printf("i = %d, u = %zu\n", i, u);
    }
    return 0;
}

以及随后的组装

        .file   "t2.c"
        .section        .rodata
.LC0:
        .string "i = %d, u = %zu\n"
        .text
.globl main
        .type   main, @function
main:
        pushl   %ebp
        movl    %esp, %ebp
        andl    $-16, %esp
        subl    $32, %esp
        movl    $0, 24(%esp)
        jmp     .L2
.L3:
        movl    28(%esp), %eax
        movl    %eax, 24(%esp)
        movl    $.LC0, %eax
        movl    28(%esp), %edx
        movl    %edx, 8(%esp)
        movl    24(%esp), %edx
        movl    %edx, 4(%esp)
        movl    %eax, (%esp)
        call    printf
        addl    $1, 24(%esp)
.L2:
        cmpl    $9, 24(%esp)
        jle     .L3
        movl    $0, %eax
        leave
        ret
        .size   main, .-main
        .ident  "GCC: (GNU) 4.4.6 20110731 (Red Hat 4.4.6-3)"
        .section        .note.GNU-stack,"",@progbits

检查上面的陈述

movl    28(%esp), %eax
movl    %eax, 24(%esp)

现在是最后一个例子 t3.c

#include <stdio.h>

int main(void)
{
    int i;
    int u;

    for (i = 0; i < 10; i++) {
        i = u;
        printf("i = %d, u = %zu\n", i, u);
    }
    return 0;
}

以及随后的组装

        .file   "t3.c"
        .section        .rodata
.LC0:
        .string "i = %d, u = %zu\n"
        .text
.globl main
        .type   main, @function
main:
        pushl   %ebp
        movl    %esp, %ebp
        andl    $-16, %esp
        subl    $32, %esp
        movl    $0, 24(%esp)
        jmp     .L2
.L3:
        movl    28(%esp), %eax
        movl    %eax, 24(%esp)
        movl    $.LC0, %eax
        movl    28(%esp), %edx
        movl    %edx, 8(%esp)
        movl    24(%esp), %edx
        movl    %edx, 4(%esp)
        movl    %eax, (%esp)
        call    printf
        addl    $1, 24(%esp)
.L2:
        cmpl    $9, 24(%esp)
        jle     .L3
        movl    $0, %eax
        leave
        ret
        .size   main, .-main
        .ident  "GCC: (GNU) 4.4.6 20110731 (Red Hat 4.4.6-3)"
        .section        .note.GNU-stack,"",@progbits

现在您可以观察 t2 和 t3 并在此处看到差异,但实际上因拱门而异

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-15
    • 2021-05-03
    • 2016-03-09
    • 2019-12-15
    • 2015-08-16
    • 2013-04-14
    相关资源
    最近更新 更多