++ nc против nc = nc + 1 - PullRequest
       24

++ nc против nc = nc + 1

4 голосов
/ 23 декабря 2011

В K & R Ch 1:

Оператор ++nc представляет новый оператор, ++, что означает увеличение на единицу.Вместо этого вы могли бы написать nc = nc + 1, но ++nc является более кратким и часто более эффективным.

Когда предварительное увеличение будет более эффективным, чем альтернатива?По крайней мере, для большинства вещей ассемблер для обоих - это инструкция add (edit: или inc).Когда они отличаются?

Ответы [ 5 ]

10 голосов
/ 23 декабря 2011

Этот текст давно устарел. Возможно, в 70-х годах было верно, что компиляторы будут производить более эффективный вывод для ++ n, но не больше. Все современные компиляторы будут выдавать идентичный код.

3 голосов
/ 23 декабря 2011

Для большинства вещей, по крайней мере, сборка для обоих является инструкцией добавления.

Это не совсем так: часто есть отдельная инструкция «увеличение на единицу» .Однако это не имеет значения, поскольку любой полуприличный компилятор будет выдавать идентичный машинный код для ++nc и nc = nc + 1.

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

2 голосов
/ 23 декабря 2011

Я не знаю точно, я просто размышляю вслух (возможно, я не должен): Возможно, во времена K & R, ++nc был скомпилирован в нечто более эффективное, чем nc = nc + 1 (например, инструкция приращения), а не дополнение).В настоящее время, однако, компиляторы, вероятно, оптимизируют это автоматически.

1 голос
/ 23 декабря 2011

Для «нормальных» переменных не должно быть никакой разницы, как предполагают другие ответы. Только если nc соответствует voloatile, результат может быть другим. Для такой переменной +1 форма должна сначала вычислить выражение nc, то есть load nc, а затем выполнить сложение. Для формы ++ компилятор все еще может использовать ярлыки и увеличивать переменную на месте.

1 голос
/ 23 декабря 2011

Это то, что я мог видеть с gcc -S <filename>. Я позволю вам получить то, что вы хотите!

>
> cat 1.c
    #include <stdio.h>

    int main()
    {
        int i=0;
        ++i;

        return 0;
    }

>
> cat 2.c
#include <stdio.h>

int main()
{
    int i=0;
    i++;

    return 0;
}

>
> cat 3.c
#include <stdio.h>

int main(void)
{
    int i=0;
    i = i + 1;

    return 0;
}
>
> gcc -S 1.c 2.c 3.c
>
>
> diff 1.s 2.s
1c1
<       .file   "1.c"
---
>       .file   "2.c"
>
> diff 2.s 3.s
1c1
<       .file   "2.c"
---
>       .file   "3.c"
>
> diff 3.s 1.s
1c1
<       .file   "3.c"
---
>       .file   "1.c"
>
>

Ниже приведено содержимое файла .s для 1.c, и инструкции идентичны по сравнению с 2.s и 3.s!

> cat 1.s
        .file   "1.c"
        .text
.globl main
        .type   main, @function
main:
.LFB2:
        pushq   %rbp
.LCFI0:
        movq    %rsp, %rbp
.LCFI1:
        movl    $0, -4(%rbp)
        addl    $1, -4(%rbp)
        movl    $0, %eax
        leave
        ret
.LFE2:
        .size   main, .-main
        .section        .eh_frame,"a",@progbits
.Lframe1:
        .long   .LECIE1-.LSCIE1
.LSCIE1:
        .long   0x0
        .byte   0x1
        .string "zR"
        .uleb128 0x1
        .sleb128 -8
        .byte   0x10
        .uleb128 0x1
        .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
>
...