Оценка операнда в размере оператора
Поскольку оператор sizeof оценивает операнд, если это VLA, я попытался проверить его следующим образом:
#include<stdio.h>
int main(void)
{
int sz=20,i=0,j=0;
int arr[sz];
printf("%d\n",sizeof((++i,sz)));
printf("%d\n",sizeof((++j,arr)));
printf("%d\n%d\n",i,j);
}
Я думал, что я не буду увеличивать, поскольку sz не является VLA, но j будет увеличиваться, поскольку arr является VLA.
Но в выводе ни один из i и j не увеличился.
4 ответа
Цитирую мой ответ на другой вопрос:
"Преобразование" происходит из-за оператора вычитания. Вы можете увидеть похожий и, возможно, более удивительный результат с запятой:
printf("%zu\n", sizeof(1, a));
также распечатает
sizeof(int *)
из-за оператора запятой, приводящего к привыканию в контексте значения.
В основном, из-за оператора запятой, тип arr
это указатель, и размер VLA не входит в картину. Смотрите мой связанный ответ для деталей.
Немного объяснений, но я подозреваю, что это некоторая оптимизация компилятора для оператора запятой. Значение операции с запятой - это значение последнего выражения. Поскольку компилятор знает, что sizeof является унарным оператором и представлен операцией с запятой, он не беспокоится о вычислении любого, кроме последнего выражения (независимо от того, было ли последнее ссылкой на VLA или нет).
Я написал несколько тестовых программ (gcc 4.3.3 на Ubuntu 9.04):
$ cat test.C# with sizeof
#include <stdio.h>
int main(void)
{
int x = 0;
printf("%d\n",
sizeof( printf("%s%d\n", "comma!", ++x), x));
}
$ gcc -S test.c
$ cat test.s
.file "test.c"
.section .rodata
.LC0:
.string "%d\n"
.text
.globl main
.type main, @function
main:
leal 4(%esp), %ecx
andl $-16, %esp
pushl -4(%ecx)
pushl %ebp
movl %esp, %ebp
pushl %ecx
subl $36, %esp
movl $0, -8(%ebp)
movl $4, 4(%esp)
movl $.LC0, (%esp)
call printf
addl $36, %esp
popl %ecx
popl %ebp
leal -4(%ecx), %esp
ret
.size main, .-main
.ident "GCC: (Ubuntu 4.3.3-5ubuntu4) 4.3.3"
.section .note.GNU-stack,"",@progbits
Обратите внимание на отсутствие строковых литералов и второй printf()
вызов.
$ cat test-alt.C# без sizeof
#include <stdio.h>
int main(void)
{
int x = 0;
printf("%d\n",
( printf("%s%d\n", "comma!", ++x), x));
}
$ gcc -S test-alt.c
$ cat test-alt.s
.file "test-alt.c"
.section .rodata
.LC0:
.string "comma!"
.LC1:
.string "%s%d\n"
.LC2:
.string "%d\n"
.text
.globl main
.type main, @function
main:
leal 4(%esp), %ecx
andl $-16, %esp
pushl -4(%ecx)
pushl %ebp
movl %esp, %ebp
pushl %ecx
subl $36, %esp
movl $0, -8(%ebp)
addl $1, -8(%ebp)
movl -8(%ebp), %eax
movl %eax, 8(%esp)
movl $.LC0, 4(%esp)
movl $.LC1, (%esp)
call printf
movl -8(%ebp), %eax
movl %eax, 4(%esp)
movl $.LC2, (%esp)
call printf
addl $36, %esp
popl %ecx
popl %ebp
leal -4(%ecx), %esp
ret
.size main, .-main
.ident "GCC: (Ubuntu 4.3.3-5ubuntu4) 4.3.3"
.section .note.GNU-stack,"",@progbits
Это может быть задокументировано где-то, но я не знаю, где искать.
sizeof вычисляет во время компиляции. В C99 для массивов переменной длины он будет ждать до времени выполнения. Проверьте этот ответ на аналогичный вопрос.
Компилятор знает размер массива: он, очевидно, равен 20. Я не думаю, что sz - это VLA. Попробуйте использовать размер массива в качестве параметра функции, например:
Функция void (размер int) { int arr[размер]; ... }
Кстати, чтобы понять, что происходит, рекомендуется прочитать ассемблерный код, созданный компилятором. Проверьте, заменяется ли sizeof константой уже во время компиляции.