Здравствуйте, Erop, Вы писали:
E>Здравствуйте, Шахтер, Вы писали:
Ш>>
Ш>> rol ecx, 16 ; 00000010H
Ш>>
E>А это точно быстрее обмена слов?
На большой куче процов будет медленнее...
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[4]: Умный вопрос по C/C++
От:
Аноним
Дата:
24.08.07 07:30
Оценка:
Здравствуйте, Erop, Вы писали:
E>Здравствуйте, remark, Вы писали:
R>>А если sp не указывает на &(*arg), то куда ж ему ещё указывать?! E>Да он скорее всего туда и указывает. E>Просто остальные пользователи *arg могут не заметить твоих последующих усилий и прочитать всё раньше, чем ты запишешь, например
R>>Тут фактически UB идёт только при доступе к объекту через sp, а при создании и инициализации sp UB нет. Т.к. далее я могу привести reinterpret_cast'ом sp обратно к uint32_t и получать доступ только через uint32_t. R>>[абстрагируемся на время от факта, что программа с UB имеет полностью недетерминированное поведение ]
E>1) Насколько я понимаю, то если ты приведёшь туда-сюда, то вообще не будет ничего плохого. Не понятен только смысл этой деятельности E>2) UB вовсе не обозначает полностью недетерминированного поведения. Это всего лишь значит, что реализация может делать что угодно. Часто конкретная реализация имеет вполне известное и детерминированное и даже документированное поведение в случае каких-то UB E>3) В частности, когда ты через хаки лазаешь в "кишки" большого типа, представляя его как коллекцию маленьких, стандарт ничего не гарантирует на тему последствий. Гарантировать может в такой ситуации реализация, железо, например.
Следует ли считать, что reinterpret_cast объявлен deprecated? В силу такого поведения компиляторов, использование reinterpret_cast полностью теряет смысл. Поскольку превращается в гадание на кофейной гуще.
Re: Умный вопрос по C/C++
От:
Аноним
Дата:
24.08.07 07:40
Оценка:
Здравствуйте, McSeem2, Вы писали:
MS>Это не глюк компилятора, компилятор ведет себя совершенно корректно. Просьба к гуру — подождать пару дней с ответом, чтобы любопытные, но менее опытные попытались сами докопаться до истины. А потом я хочу задать дополнительные вопросы, возникшие в связи с этим.
а может кто-нибудь сказать насколько данное использование reinterpret_cast правильно?
static bool is_not_valid_check_sum(const uint8_t* rec, size_t rec_len)
{
if (rec_len < 3)
return true;
const uint8_t* sz_end = rec + rec_len - sizeof(uint16_t);
uint16_t sum = 0;
for (; rec < sz_end; ++rec)
sum += *rec;
return sum != *reinterpret_cast<const uint16_t *>(sz_end);
}
идея в том что в конце записи есть двухбайтовое поле содержащие checksum,
подсчитывается оно протым сложением байт записи.
ЗЫ код скорее всего надо будет портировать на arm, поэтому мнение людей работавших с
arm тоже очень и очень интересно?
Re[6]: Умный вопрос по C/C++
От:
Аноним
Дата:
24.08.07 07:42
Оценка:
Здравствуйте, Awaken, Вы писали:
A>пожалуй стоит привести асмовый код полностью A>там с /O3 эта функция не вызывается — она инлайнится (тогда зачем компилятор оставил ее код?)
A>/O1 A>
A> .file "a.c"
A> .text
A>.globl swap_words
A> .type swap_words, @function
A>swap_words:
A> pushl %ebp
A> movl %esp, %ebp
A> movl 8(%ebp), %eax
A> movzwl 2(%eax), %ecx
A> movzwl (%eax), %edx
A> movw %dx, 2(%eax)
A> movw %cx, (%eax)
A> popl %ebp
A> ret
A> .size swap_words, .-swap_words
A> .section .rodata.str1.1,"aMS",@progbits,1
A>.LC0:
A> .string "%08X %08X\n"
A> .text
A>.globl main
A> .type main, @function
A>main:
A> leal 4(%esp), %ecx
A> andl $-16, %esp
A> pushl -4(%ecx)
A> pushl %ebp
A> movl %esp, %ebp
A> pushl %ecx
A> subl $36, %esp
A> movl $-65536, -8(%ebp)
A> leal -8(%ebp), %eax
A> movl %eax, (%esp)
A> call swap_words
A> movl -8(%ebp), %eax
A> movl %eax, 8(%esp)
A> movl $-65536, 4(%esp)
A> movl $.LC0, (%esp)
A> call printf
A> movl $0, %eax
A> addl $36, %esp
A> popl %ecx
A> popl %ebp
A> leal -4(%ecx), %esp
A> ret
A> .size main, .-main
A> .ident "GCC: (GNU) 4.1.2 20061115 (prerelease) (SUSE Linux)"
A> .section .note.GNU-stack,"",@progbits
A>
A>/O3 A>
A> .file "a.c"
A> .text
A> .p2align 4,,15
A>.globl swap_words
A> .type swap_words, @function
A>swap_words:
A> pushl %ebp
A> movl %esp, %ebp
A> movl 8(%ebp), %eax
A> movzwl 2(%eax), %ecx
A> movzwl (%eax), %edx
A> movw %cx, (%eax)
A> movw %dx, 2(%eax)
A> popl %ebp
A> ret
A> .size swap_words, .-swap_words
A> .section .rodata.str1.1,"aMS",@progbits,1
A>.LC0:
A> .string "%08X %08X\n"
A> .text
A> .p2align 4,,15
A>.globl main
A> .type main, @function
A>main:
A> leal 4(%esp), %ecx
A> andl $-16, %esp
A> pushl -4(%ecx)
A> pushl %ebp
A> movl %esp, %ebp
A> pushl %ecx
A> subl $36, %esp
A> movzwl -6(%ebp), %edx
A> movzwl -8(%ebp), %eax
A> movl $-65536, -8(%ebp)
A> movw %dx, -8(%ebp)
A> movw %ax, -6(%ebp)
A> movl $-65536, 8(%esp)
A> movl $-65536, 4(%esp)
A> movl $.LC0, (%esp)
A> call printf
A> addl $36, %esp
A> xorl %eax, %eax
A> popl %ecx
A> popl %ebp
A> leal -4(%ecx), %esp
A> ret
A> .size main, .-main
A> .ident "GCC: (GNU) 4.1.2 20061115 (prerelease) (SUSE Linux)"
A> .section .note.GNU-stack,"",@progbits
A>
Похоже, что подозрения подтвердились. Компилятор просто не использовал результат вычисления функции, а подставил исходную константу.
И кому только потребовалась такая оптимизация?
Здравствуйте, Alexander Pazdnikov, Вы писали:
AP>Здравствуйте, Аноним, Вы писали:
AP>
А>> return sum != *reinterpret_cast<const uint16_t *>(sz_end);
А>>}
А>>
AP>Если ядро ARM < 9, то часто будет получаться работа с невыровненной памятью. AP>На < ARM9 выравнивание до 4-х байт AP>На ARM9 выравнивание до 2-х байт.
предположительно ARM9,
но вопрос решения остался за рамкой,
надо писать так?
uint16_t valid_sum;
memcpy(&valid_sum, sz_end, sizeof(valid_sum));
return sum != valid_sum;
Re[3]: Умный вопрос по C/C++
От:
Аноним
Дата:
24.08.07 08:25
Оценка:
AP>Если ядро ARM < 9, то часто будет получаться работа с невыровненной памятью. AP>На < ARM9 выравнивание до 4-х байт AP>На ARM9 выравнивание до 2-х байт.
и еще вопросы (простите за наглость),
1)Какой coding style в связи с этим вы используете? типа никаких reinterpert_cast и т.д.
2)а на ARM9 подобных проблем быть не может, ну скажем если поменть тип checksum на какой-нибудь другой,
или складывать не побайтово, а по x-разрядных чисел?
3)а скажем packed структура, мне приходит поток байт
const uint8_t *data, size_t data_len;
а на самом деле это
unsigned some_number;
Foo foo;
где Foo:
struct Foo {
} __attribute__ ((packed));
и я делаю так:
Foo *p = reinterpret_cast<const Foo *>(data + sizeof(unsigned));
Здравствуйте, Аноним, Вы писали: А>1)Какой coding style в связи с этим вы используете? типа никаких reinterpert_cast и т.д. А>2)а на ARM9 подобных проблем быть не может, ну скажем если поменть тип checksum на какой-нибудь другой, А>или складывать не побайтово, а по x-разрядных чисел?
Для надежности и переносимости между разными процами( в том числе и ARM, x86 )
лучше медленнее, но надежнее.
#include <vector>
typedef std::vector<uint8_t> BufferType;
typedef uint32_t CrcType; // обязательно беззнаковый, иначе получим вопросы знакорасширения типов.
CrcType Crc8Mod2CheckSum::calc(BufferType& rBuf, bool crc_included_fl)
{
CrcType cs = 0xFF;
if (crc_included_fl){
BufferType::iterator it_end = rBuf.end() - getCrcLen();
}
for (BufferType::iterator it = rBuf.begin(); it != it_end; ++it){
cs ^= *it;
}
cs &= 0xFF;
return cs;
}
От reinterpert_cast и прочих cast'ов можно отказаться, возможное их применение только для работы с буферами под пакеты приема/передачи.
Как видите, обхожусь без cast'ов
А>или складывать не побайтово, а по x-разрядных чисел?
Безопаснее побайтно. Тем более для телемеханики — надежность на первом месте.
А>а на самом деле это А>unsigned some_number; А>Foo foo;
А>где Foo: А>struct Foo { А>} __attribute__ ((packed));
А>и я делаю так: А>Foo *p = reinterpret_cast<const Foo *>(data + sizeof(unsigned));
А>это тоже будет невалидно на ARM?
Это лучше спросить у людей, которые используют разнообразные хаки от gcc/g++.
Здравствуйте, Аноним, Вы писали:
А>Следует ли считать, что reinterpret_cast объявлен deprecated? В силу такого поведения компиляторов, использование reinterpret_cast полностью теряет смысл. Поскольку превращается в гадание на кофейной гуще.
Нет.
Такое использование незаконно. Но остальные использования reinterpret_cast законны.
Например, ты можешь скастить указатель на объект в int, а потом обратно, и нормально с ним работать дальше.
Здравствуйте, Erop, Вы писали:
E>2) UB вовсе не обозначает полностью недетерминированного поведения. Это всего лишь значит, что реализация может делать что угодно.
А разьве выделенное не то же самое?
E>Часто конкретная реализация имеет вполне известное и детерминированное и даже документированное поведение в случае каких-то UB
Ключевое слово выделено
E>3) В частности, когда ты через хаки лазаешь в "кишки" большого типа, представляя его как коллекцию маленьких, стандарт ничего не гарантирует на тему последствий. Гарантировать может в такой ситуации реализация, железо, например.
Здравствуйте, Аноним, Вы писали:
А>3)а скажем packed структура, мне приходит поток байт А>const uint8_t *data, size_t data_len;
А>а на самом деле это А>unsigned some_number; А>Foo foo;
А>где Foo: А>struct Foo { А>} __attribute__ ((packed));
А>и я делаю так: А>Foo *p = reinterpret_cast<const Foo *>(data + sizeof(unsigned));
А>это тоже будет невалидно на ARM?
Вообще здесь больше проблема переносимости и совместимости разного оборудования, в частности самым выжным обмен данными между little endian и big endian. Ваша структура struct Foo возможно коректно будет обрабатываться между одними типами (le -> le) и (be -> be), но при работе (le -> be) и (be -> le) протокол обмена будет работать некорректно, т.к. порядок слудования байтов на этих устройствах будет разный.
В этом случае необходимо применять __leXX_cpu /__cpu_leXX и __beXX_cpu/__cpu_beXX. Просто определиться с протоколом обмена и порядком следования байт в протоколе обмена (LE или BE). и тогда использовать одну из пар макросов.
Re[4]: Умный вопрос по C/C++
От:
Аноним
Дата:
24.08.07 10:00
Оценка:
Здравствуйте, Аноним, Вы писали:
AP>>Если ядро ARM < 9, то часто будет получаться работа с невыровненной памятью. AP>>На < ARM9 выравнивание до 4-х байт AP>>На ARM9 выравнивание до 2-х байт.
А>и еще вопросы (простите за наглость), А>1)Какой coding style в связи с этим вы используете? типа никаких reinterpert_cast и т.д. А>2)а на ARM9 подобных проблем быть не может, ну скажем если поменть тип checksum на какой-нибудь другой, А>или складывать не побайтово, а по x-разрядных чисел? А>3)а скажем packed структура, мне приходит поток байт А>const uint8_t *data, size_t data_len;
А>а на самом деле это А>unsigned some_number; А>Foo foo;
А>где Foo: А>struct Foo { А>} __attribute__ ((packed));
А>и я делаю так: А>Foo *p = reinterpret_cast<const Foo *>(data + sizeof(unsigned));
А>это тоже будет невалидно на ARM?
Если у вас ARM процессор работает в отдельном от x86 адресном пространстве, то все указатели, адреса буферов и т.п. ARM определяет сам. В таком случае динамическая память выделяется по правильно выравненной границе. В случае размещения буфера в стеке необходимо корректно задать ему тип, т.е. не uint8_t data[xxx];
а
struct {
unsigned some_number;
Foo foo;
} data;
Тогда можно пользоваться тем способом, как вы написали. (вопрос об aliasing-е не рассматриваем).
Что касается __attribute__ ((packed)), то это определяется компилятором. Он может сам сгенерировать код для доступа к невыравненным данным. Тогда действительно будет плотная упаковка. А может и не иметь такой возможности. Тогда структура будет иметь пустоты, т.е. смещения полей в структуре на ARM и x86 будут разными.
Здравствуйте, Андрей Коростелев, Вы писали:
АК>Здравствуйте, McSeem2, Вы писали:
АК>Правило введенное в С99 и запрещающее создание алиаса объекта с типом отличного от типа объекта-оригинала (ISO/IEC 9899 6.5/7) поддерживается в gcc 3.4.1 и выше на уровнях оптимизации начиная со 2-го. В данном случе sp не будет являться корректным алиасом для объекта *arg, потому компилятор вправе сгенерировать код, при котором sp не указывает на &(*arg).
А, простите, как быть, если у меня два указателя показывают на один и тот же адрес, но один char*, а другой нечто вроде
typedef char (*PCHAR20)[20];
Иными словами, я трактую этот адрес как адрес символа, с одной стороны, и строки символов длины 20 — с другой. Классика, в общем,многомерные массивы...
With best regards
Pavel Dvorkin
Re[6]: Умный вопрос по C/C++
От:
Аноним
Дата:
24.08.07 10:25
Оценка:
Здравствуйте, remark, Вы писали:
R>Здравствуйте, Аноним, Вы писали:
А>>Следует ли считать, что reinterpret_cast объявлен deprecated? В силу такого поведения компиляторов, использование reinterpret_cast полностью теряет смысл. Поскольку превращается в гадание на кофейной гуще.
R>Нет. R>Такое использование незаконно. Но остальные использования reinterpret_cast законны. R>Например, ты можешь скастить указатель на объект в int, а потом обратно, и нормально с ним работать дальше.
R>
Польза от такой оптимизации минимальна. Практически во всех случаях изменения типа оптимизатору нельзя отбрасывать это. Поскольку просто так адрес объекта брать никто не будет, пусть даже и используя другой тип указателя.
Ох, чует мое сердце, что это нововведение принесёт больше беды, чем пользы.
Лучшее враг хорошего.
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>А, простите, как быть, если у меня два указателя показывают на один и тот же адрес, но один char*, а другой нечто вроде
PD> typedef char (*PCHAR20)[20];
PD>Иными словами, я трактую этот адрес как адрес символа, с одной стороны, и строки символов длины 20 — с другой. Классика, в общем,многомерные массивы...
Выражение PCHAR20[0] имеет тип char& (char lvalue). И именно через него ты получаешь доступ к памяти. Так что в обоих случаях тип, через который ты получаешь доступ к памяти одинаковое. Так что всё законно.
А>Если у вас ARM процессор работает в отдельном от x86 адресном пространстве, то все указатели, адреса буферов и т.п. ARM определяет сам. В таком случае динамическая память выделяется по правильно выравненной границе. В случае размещения буфера в стеке необходимо корректно задать ему тип, т.е. не uint8_t data[xxx];
Это только в случае выделения памяти в куче или на стеке.
А если эти данные в буфере? Буфер будет выровнен, а начало структуры данных может и небыть.
Здравствуйте, remark, Вы писали:
E>>2) UB вовсе не обозначает полностью недетерминированного поведения. Это всего лишь значит, что реализация может делать что угодно.
R>А разьве выделенное не то же самое?
Нет, как только ты фиксируешь реализацию, так большинство UB становятся вполне детерминированными
Возможно я непонятно выражаюсь. Извини.
E>>Часто конкретная реализация имеет вполне известное и детерминированное и даже документированное поведение в случае каких-то UB R>Ключевое слово выделено
Ну UB-то разные бывают. Просто стандарт снимает с себя ответсвенность за последствия. Но никто не обязывает реализацию тоже снимать с себя ответсвенность...
E>>3) В частности, когда ты через хаки лазаешь в "кишки" большого типа, представляя его как коллекцию маленьких, стандарт ничего не гарантирует на тему последствий. Гарантировать может в такой ситуации реализация, железо, например. R>Здесь тоже
Ну, например, если железо фиксирует endian, то ты, читая int как коллекцию char получаешь уже вполне определённое поведение...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[6]: Умный вопрос по C/C++
От:
Аноним
Дата:
24.08.07 11:05
Оценка:
Здравствуйте, Alexander Pazdnikov, Вы писали:
А>>Если у вас ARM процессор работает в отдельном от x86 адресном пространстве, то все указатели, адреса буферов и т.п. ARM определяет сам. В таком случае динамическая память выделяется по правильно выравненной границе. В случае размещения буфера в стеке необходимо корректно задать ему тип, т.е. не uint8_t data[xxx];
AP>Это только в случае выделения памяти в куче или на стеке. AP>А если эти данные в буфере? Буфер будет выровнен, а начало структуры данных может и небыть.
Может. Но в таком случае не получится описать такую структуру буфера.
Если буфер описан, например, так:
то смещение поля foo зависит от компилятора. Обычно оно будет кратно 4. Заставить разместить foo по смещению 1 не получится. Если все же нужен именно упакованный буфер и переставить местами поля нельзя, то обращаться к невыравненным полям нужно с помощью соответствующих функций или макросов. В таком случае макросы будут разными для разных платформ.