суббота, 5 июля 2008 г.

Операции над указателями...

Долгая дискуссия заставила призадуматься...

С одной стороны в исходниках gcc я явно обнаружил обмен индекса и указателя, в случае операций с массивами (gcc/gcc/c-types.c):
 if (TREE_CODE (TREE_TYPE (array)) != ARRAY_TYPE
&& TREE_CODE (TREE_TYPE (array)) != POINTER_TYPE)
{
tree temp;
if (TREE_CODE (TREE_TYPE (index)) != ARRAY_TYPE
&& TREE_CODE (TREE_TYPE (index)) != POINTER_TYPE)
{
error ("subscripted value is neither array nor pointer");
return error_mark_node;
}
temp = array;
array = index;
index = temp;
swapped = true;
}
Но загадочности на этом не закончились. На самом деле компилятору действительно пофиг что с чем складывать. указатель с индексом или индекс с указателем, результатом всеравно будет указатель на элемент.
char s[] = "hello"; 
int idx = 3;
assert (*(s + idx) == 'l' && *(idx + s) == 'l');
Так вот второе выражение смущает меня больше всего.

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

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

Вообще компилятор во многих случаях молчит. Я вот до сих пор считал, что удаление недостижимого кода - это оптимизация. И при компиляции без оптимизации компилятор должен тупо генерить все что написано. Но это оказалось не так.
int i = 10;
if (1) i += 20;
if (0) i -= 30;
$ gcc -O0 -S ...
        movl    $10, -8(%ebp)
addl $20, -8(%ebp)
add есть, а вот sub не наблюдаю...

Эта ерунда всплыла у меня когда я тшетно пытался посмотреть код, генерируемый выражениями argc + argv - 1, argv + argc - 1 с помощью BOOST_ASSERT(argc + argv - 1 == argv + argc - 1);. Выражение оказывалось истинным, и BOOST_ASSERT вообще не генерировал никакого кода, не смотря на -O0. Дык он и свертку выполнил безо всякого разрешения.

Может быть у gcc есть какой нибудь волшебный ключик, который позволяет разрешить генерацию недостижимого кода? не знаю точно...

8 коммент.:

Yuri Volkov комментирует...

я тоже когда-то с подобной проблемой столкнулся - volatile тогда помог, правда то был случай не с недостижимым кодом...

Андрей Валяев комментирует...

Но почему он вообще оптимизирует??? не знаю как VS, gcc вроде бы такой ерундой стал страдать сравнительно недавно. может быть с gcc4...

Может быть, тривиальные виды оптимизации переросли в нечто естественное, без которого даже O0 не может существовать, а им на смену пришла векторизация... :)

Анонимный комментирует...

Попробуй
int i = 10;
volatile a = 1;
if (a == 1) i += 20;
if (a == 0) i -= 30;

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

Андрей Валяев комментирует...

Свертка выражений и удаление недостижимого кода - это все виды оптимизации.. Значит если я говорю что оптимизация запрещена - надо все эти возможности отключить...

А то получается что
отключай оптимизацию, не отключай, всеравно она оптимизирует... :(

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

Анонимный комментирует...

Удаление недостижимого кода - не обязательно результат оптимизации. Возможно, тут просто некомпиляция. Нет точки входа - нет и кода, если это противоречит логики кодогенератора (на уровне алгоритма кодогенерации).

Андрей Валяев комментирует...

А еще он (gcc) функции инлайнит хотя его об этом не просили...

Хотя надо сказать что при O0 он не осмеливается этого делать :)

Анонимный комментирует...

Так вот второе выражение смущает меня больше всего.
Стандартный Си. На некоторых компиляторах вообще этот код верен:
int i, *s;
i = i[*s];
так как i[*s] автоматом преобразуется в *(i + s), причем эта функциональность явно описывается как фича.

А компилятор, как партизан упорно молчит. Хоть бы варнинг чтоли сделали какой.
С какой стати-то?

И при компиляции без оптимизации компилятор должен тупо генерить все что написано.
Это в MSVC так... чтобы отлаживать было проще, потому как оптимизатор код может сильно ворочать.

Анонимный комментирует...

Тьфу ты!

int i;
int *s;

i = i[s];

Прошу прощения.
Это и сейчас верно, кстати.