int val;
short int *pv = (short int*)(void*)&val + 1;
либо для проверки можно так, результат одинаковый
int val,*p=&val;
short int *pv=(short int*)p + 1;
да что вы говорите :lol:
[console]doc@dt:~/Documents$ gcc -Wall -O0 -o p.elf p.c
doc@dt:~/Documents$ ./p.elf
0
doc@dt:~/Documents$ gcc -Wall -O3 -o p.elf p.c
doc@dt:~/Documents$ ./p.elf
-18549
doc@dt:~/Documents$ cat p.c
#include <stdio.h>
int main(int argc, char *argv[])
{
int val = argc;
short int *pv = (short int*)(void*)&val + 1;
printf("%d\n", *pv);
return 0;
}
[/console]
типично UB. :O
и это ещё не всё!
Peter написал:
int val,*p=&val;
а кто вам сказал, что val существует на момент взятия адреса?
я что-то не припомню такого места в стандарте.
[quote=Peter]int val;
short int *pv = (short int*)(void*)&val + 1;
либо для проверки можно так, результат одинаковый
int val,*p=&val;
short int *pv=(short int*)p + 1;
да что вы говорите :lol:
[/quote]
Сейчас и в тестовой и в рабочей подпрограммах сделано так
text
int val;
sort int *pv=(short int*)(void*)&val+1;
работают обе без "костылей", компилируются с "-O3".
Peter написал:
int val,*p=&val;
а кто вам сказал, что val существует на момент взятия адреса?
я что-то не припомню такого места в стандарте.
Самого стандарта я не читал, а в книге когда-то прочитал, что так можно.
Во всяком случае, я не помню случаев, чтобы компилятор жаловался
на такую конструкцию или чтобы она не работала.
[quote=Peter]short int *pv = (short int*)((void*)pval +2);
Peter написал:
Использование переменной static не подходит, т.к. с ней работа замедляется,
а мне всё это надо, чтобы ускорить.
компилятор может оптимизировать данную конструкцию, что и делает. В случае static он этого сделать не может. Однако, оптимизация обязана правильно работать только если данные данные действительно short. А вот в случае int оптимизация работать правильно не обязана. Что вы и наблюдаете.[/quote]
text
(short int*)((void*)&val+2)
и
text
(short int*)(void*)&val+1
это тождественные конструкции.
Вот логика компилятора мне и непонятна. Есть указатель, указатель - это адрес,
а не константа и не переменная, которая не изменяется в цикле.
Действительно, это указатель не на реальную переменную, это указатель
определённого типа неизвестно на что, но компилятор не должен об этом беспокоиться.
В данном случае это ссылка на область памяти, не являющейся read only,
поэтому нет логических оснований эту ссылку оптимизировать.
Почему в Ubuntu 11.04 компилятор всё же это делает при "-O3",
а в Debian Wheezy нет? Это или баг, или неестественный интеллект.
Вообще такими приёмами с указателями пользуются и хакеры.
Вчера сообразил, что для доступа к произвольным байтам переменной можно использовать также union,
в моём случае так
text
union int2sh {
int mr;
short int ns[2]; } int2sh;
тогда в ns[1] будут 3-й и 4-й байты mr.
Мне надо было некоторое выражение типа int сдвигать на 19 разрядов вправо и я ожидал, что,
если его присвоить mr и потом двухбайтовую переменную со старшими разрядами сдвигать на 3 разряда,
то программа будет работать быстрее, но оказалось наоборот: и этот вариант и вариант с доступом
к старшим байтам обычной переменной через указатель на short int работает медленнее,
т.е. подпрограмма в целом выполняется на 5% дольше.
Ещё оказалось, что на одном и том же компьютере, AMD Athlon X2 2.8GHz, в 32-х разрядной Fedora 10
подпрограмма работает дольше, чем в 64-х разрядном Debian testing, разница в 1/6.
На другом с Intel Core 2 Quad 2.4GHz и 32-х разрядной Ubuntu 11.04, если учесть соотношение частот,
выполняется так же, как на 1-м в 64-х разрядном Debian, или на доли процента быстрее.
Я думаю, что она 32-х разрядная, т.к. "uname -i" возвращает i386.
В данном случае это ссылка на область памяти, не являющейся read only,
поэтому нет логических оснований эту ссылку оптимизировать.
это может быть и регистр. Берите дизассемблер, и проверяйте.
Peter написал:
Вообще такими приёмами с указателями пользуются и хакеры.
не такими.
Peter написал:
Мне надо было некоторое выражение типа int сдвигать на 19 разрядов вправо и я ожидал, что,
если его присвоить mr и потом двухбайтовую переменную со старшими разрядами сдвигать на 3 разряда,
то программа будет работать быстрее,
возможно быстрее всего будет сдвигать на 19 разрядов. Есть для этого специальная команда.
В моём случае указатель устанавливался на 3-й байт существующей переменной и я не знаю,
как можно такой адрес счесть за регистр.
И такими приёмами тоже, например, указатель на константную переменную можно преобразовать
в указатель на обычную и тогда её изменить, но сам я этого не пробовал.
Сейчас я использую оператор >>, действительно получается быстрее, чем через указатель
на short int, установленный на 3-й байт или на совмещённую с ним переменную этого типа.
Как я уже говорил, в Debian Wheezy компилятор этот указатель не оптимизирует
и в цикле при обращении по нему возвращаются изменившиеся значения старших
байтов переменной, а в Ubuntu 11.04 для такого поведения надо компилировать с "-O1".
Как я уже говорил, в Debian Wheezy компилятор этот указатель не оптимизирует
и в цикле при обращении по нему возвращаются изменившиеся значения старших
байтов переменной, а в Ubuntu 11.04 для такого поведения надо компилировать с "-O1".
дело не в том, где дебиан, а где бубунта... дело в версии компилятора и (хотя ИМХО маловероятно - наложенных патчах)...
Fat-Zer, так вы согласны, что int* -> void* -> short* это вообще говоря UB?
Я думаю, это зависит от того, как это используется. Если человек понимает, что
и для чего делает, то всё допустимо. В даном случае преобразование к void* излишне,
можно сразу (short int* )&int + 1.
[quote=Peter]В даном случае преобразование к void* излишне,
можно сразу (short int* )&int + 1.
насколько я помню, стандартом это попросту запрещено. (хотя может и ошибаюсь. Это вы что-то новенькое придумали)[/quote]
Я уже говорил, что стандарта не читал, а сама операция синтаксически правильна
и работает, при условии, что используется little-endian.
Я уже говорил, что стандарта не читал, а сама операция синтаксически правильна
ох... поймите вы простую вещь - С это очень древний и очень глючный язык. Тут синтаксически очень много чего разрешено. А на практике - то работает, а то не работает. Особенно опасно приведение к типу void*, это такая же НЁХ, как и деление на ноль. Точнее, когда вы преобразуете к void* вы умножаете на ноль - информация о типе исчезает, а вот обратно - вы делите. В отличие от математики, в С это разрешено, И это действительно нужно для функций, которые работают с любыми объектами. Но надо всегда выполнять простое правило - из void* нужно преобразовывать в точности в тот-же тип, что вы преобразовали в void *. Тогда всё нормально, и это позволяет например написать
которой всё равно что сортировать. И оно даже по стандарту, и даже работает.
НО!
Если вы преобразуете как-то иначе, то никто ничего вам не гарантирует, мало того, с -O3 это почти всегда НЕ работает. Причина очень проста - компилятор вовсе не обязан брать адрес переменной, работать с адресом, а потом его разименовывать. Он вообще-то вправе работать и с самой переменной (ну сами подумайте, зачем делать лишние действия?). Это всё работает, но только пока тип переменной не меняется. Мало того, при этом изменении НЕ работают стандартные способы преобразования типов - стандарт просто не предусматривает таких ситуаций, а значит создателям компиляторов достаётся ещё одна лазейка для оптимизации. Вот так у вас -1 может вполне легко превратится в +32767, и виноваты будете исключительно вы. Ибо такое преобразование для данного конкретного процессора в данном случае быстрее, а вопрос правильности никого не волнует, ибо если-бы не вы, то получилось-бы 4294967295, что минус единица и есть (в целых, 32х битных знаковых). Но вы-же ещё и тип не тот выбрали - об этом никто не договаривался, и в стандарте такой ситуации не прописано. ССЗБ.
Peter написал:
сама операция синтаксически правильна
и работает
вы в этом уверены? ЕМНИП нельзя прямо преобразовывать тип указателей, кроме как через void*.
Благодарю за подробные разъяснения, но приведённая мной операция всё же работает,
т.к. разрешается алгебраическое сложение указателя с числом. Он же при этом просто
будет указывать на другой адрес со сдвигом, кратным длине объекта, к типу которого
относится указатель. Кроме того, иногда, если удобно, вместо указателя на структуру
используют указатель на её 1-й член, т.к. в языке C структура всегда выровнена
по её 1-му члену. Вот в jpeglib это широко используется.
В том, что некий адрес какого-то объекта присваивается указателю на произвольный тип,
ничего недопустимого нет, т.к. адрес и есть указатель на байт в памяти. При этом
требуется только, чтобы не нарушались правила выравнивания для этого типа.
не работает. пруф выше. "иногда работает, иногда не работает" работой не считается. ИМХО.
Peter написал:
Вот в jpeglib это широко используется.
я и сам знаю, что многие либы не компилляться с -O3. :(
Peter написал:
В том, что некий адрес какого-то объекта присваивается указателю на произвольный тип,
ничего недопустимого нет, т.к. адрес и есть указатель на байт в памяти.
эх... Останемся каждый при своём мнении - стандарт для вас не аргумент, а больше мне и крыть-то нечем... Заработало? Ну значит всё хорошо. Я что, против? Это ваш код...
Fat-Zer, так вы согласны, что int* -> void* -> short* это вообще говоря UB?
согласился с тем, что int* -> short* + изменение указателя - это точно UB. (ЕМНИП предварительной конвертации в void* требуют только плюсы).
по логике приведение через void* - тоже UB (тут можно порассуждать, что будет на BE-архитектурах)
при простом приведении int* -> short* пока не уверен, что будет.