Я знаю на некоторых языках следующее:
a += b
более эффективен, чем:
a = a + b
поскольку он устраняет необходимость создания временной переменной. Это в C? Эффективно ли использовать + = (и, следовательно, также -=
*=
и т.д.)
Я знаю на некоторых языках следующее:
a += b
более эффективен, чем:
a = a + b
поскольку он устраняет необходимость создания временной переменной. Это в C? Эффективно ли использовать + = (и, следовательно, также -=
*=
и т.д.)
Итак, вот окончательный ответ...
$ cat junk1.c
#include <stdio.h>
int main()
{
long a, s = 0;
for (a = 0; a < 1000000000; a++)
{
s = s + a * a;
}
printf("Final sum: %ld\n", s);
}
[email protected]:~/junk$ cat junk2.c
#include <stdio.h>
int main()
{
long a, s = 0;
for (a = 0; a < 1000000000; a++)
{
s += a * a;
}
printf("Final sum: %ld\n", s);
}
[email protected]:~/junk$ for a in *.c ; do gcc -O3 -o ${a%.c} $a ; done
[email protected]:~/junk$ time ./junk1
Final sum: 3338615082255021824
real 0m2.188s
user 0m2.120s
sys 0m0.000s
[email protected]:~/junk$ time ./junk2
Final sum: 3338615082255021824
real 0m2.179s
user 0m2.120s
sys 0m0.000s
... для компьютера my и my, работающего в моей операционной системе. Ваши результаты могут варьироваться или не меняться. Однако в системе my время идентично: время пользователя 2.120s.
Теперь, чтобы показать вам, насколько впечатляющими могут быть современные компиляторы, вы заметите, что я использовал выражение a * a
в задании. Это связано с этой небольшой проблемой:
$ cat junk.c
#include <stdio.h>
int main()
{
long a, s = 0;
for (a = 0; a < 1000000000; a++)
{
s = s + a;
}
printf("Final sum: %ld\n", s);
}
[email protected]:~/junk$ gcc -O3 -S junk.c
[email protected]:~/junk$ cat junk.s
.file "junk.c"
.section .rodata.str1.1,"aMS",@progbits,1
.LC0:
.string "Final sum: %ld\n"
.text
.p2align 4,,15
.globl main
.type main, @function
main:
.LFB22:
.cfi_startproc
movabsq $499999999500000000, %rdx
movl $.LC0, %esi
movl $1, %edi
xorl %eax, %eax
jmp __printf_chk
.cfi_endproc
.LFE22:
.size main, .-main
.ident "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"
.section .note.GNU-stack,"",@progbits
Компилятор вычислил мой цикл и развернул его до точки вычисления суммарной суммы и просто ввел ее как константу, которую он начал распечатывать, полностью пропуская любую конструкцию цикла. Перед лицом оптимизаторов, которые умны, вы действительно думаете, что собираетесь найти какой-либо значимый край в различии между s = s + a
и s += a
?!
Это конкретный вопрос для компилятора, но я ожидаю, что все современные компиляторы дадут тот же результат. Использование Visual Studio 2008:
int main() {
int a = 10;
int b = 30;
a = a + b;
int c = 10;
int d = 50;
c += d;
}
Линия a = a + b имеет разборку
0014139C mov eax,dword ptr [a]
0014139F add eax,dword ptr [b]
001413A2 mov dword ptr [a],eax
Линия c + = d имеет разборку
001413B3 mov eax,dword ptr [c]
001413B6 add eax,dword ptr [d]
001413B9 mov dword ptr [c],eax
То же самое. Они скомпилированы в один и тот же код.
Это зависит от того, что a
.
a += b
в C по определению эквивалентен a = a + b
, за исключением того, что из абстрактной точки зрения a
оценивается только один раз в первом варианте. Если a
является "чистым" значением, т.е. Если оценивать его один раз и оценивать его много раз, это не влияет на поведение программ, тогда a += b
строго эквивалентно a = a + b
во всех отношениях, включая эффективность.
Другими словами, в ситуациях, когда у вас действительно есть свободный выбор между a += b
и a = a + b
(что означает, что вы знаете, что они делают то же самое), они, как правило, будут иметь одинаковую эффективность. Некоторые компиляторы могут испытывать трудности, когда a
означает вызов функции (например, не то, что вы имели в виду), но когда a
является энергонезависимой переменной, машинный код, сгенерированный для обоих выражений, будет таким же.
В другом примере, если a
является изменчивой переменной, то a += b
и a = a + b
имеют разное поведение и, следовательно, различную эффективность. Однако, поскольку они не эквивалентны, ваш вопрос просто не применяется в таких случаях.
В простых случаях, показанных в вопросе, нет существенной разницы. Если оценки операторов присваивания - это когда у вас есть выражение, например:
s[i]->m[j1].k = s[i]->m[jl].k + 23; // Was that a typo?
против
s[i]->m[j1].k += 23;
Два преимущества - и я не рассчитываю на меньшее количество ввода. Там нет вопроса о том, была ли опечатка, когда первое и второе выражения отличаются; и компилятор не оценивает сложное выражение дважды. Вероятно, в наши дни это не будет иметь большого значения (оптимизация компиляторов намного лучше, чем раньше), но у вас могут быть еще более сложные выражения (оценка функции, определенной в другой единицы перевода, например, как часть subscriptip), где компилятор не сможет избежать оценки выражения дважды:
s[i]->m[somefunc(j1)].k = s[i]->m[somefunc(j1)].k + 23;
s[i]->m[somefunc(j1)].k += 23;
Кроме того, вы можете написать (если вы храбры):
s[i++]->m[j1++].k += 23;
Но вы не можете написать:
s[i++]->m[j1++].k = s[i]->m[j1].k + 23;
s[i]->m[j1].k = s[i++]->m[j1++].k + 23;
(или любая другая перестановка), потому что порядок оценки не определен.
a += b
более эффективен, чем
a = a + b
потому что первый принимает вам 6 нажатий клавиш, а последний выдает 9 нажатий клавиш.
С современным оборудованием, даже если компилятор глупый и использует более медленный код для одного, чем другое, общее время, сохраненное за время жизни программы, может быть меньше, чем время, необходимое для ввода трех дополнительных штрихов.
Однако, как говорили другие, компилятор почти наверняка производит точно такой же код, чтобы первый был более эффективным.
Даже если вы учитываете читаемость, большинство программистов на C, вероятно, мысленно анализируют бывшее быстрее, чем последнее, потому что это такой общий шаблон.
Практически во всех случаях они дают одинаковые результаты.
Помимо оригинального или некомпетентно написанного компилятора, не должно быть разницы, если a
и b
являются нормальными переменными, поэтому они дают эквивалентные результаты.
Если вы имели дело с С++, а не с C, перегрузка оператора могла бы иметь более существенные отличия.