LINUX.ORG.RU

type aliasing

 


0

3

Добрый день.
Если прочитать про вопрос здесь (внизу) http://en.cppreference.com/w/cpp/language/reinterpret_cast, то такой код не должен выводить нули:

//1.cpp
#include <iostream>
using namespace std;
void f(short int *s);
int main()
{
    int y = 0;
    for(int i = 0;  i < 10;  ++ i)
    {
        y = i;
        f((short int*)&y);
        cout << y << '\n';
    }
}

//2.cpp
void f(short int *s)
{
    s[0] = 0;
    s[1] = 0;
}

pavlick@pc:~$ g++ 1.cpp 2.cpp -std=c++14 -Wall -O3
pavlick@pc:~$ ./a.out
0
0
0
0
0
0
0
0
0
0
Но как видно выводит, т.е. вызов функции не дал заоптимизировать цикл. Аналогичная история с memcpy, она ведь работает:
void* memcpy( void* dest, const void* src, std::size_t count );
Отсюда вопрос: верно ли предположение о том, что можно кастовать в указатель любого типа и отправлять в функцию без опасений? Вызов функции отменяет оптимизации, а функция запишет всё в память (из регистров) будучи заинлайниной или нет.

★★

Последнее исправление: CYB3R (всего исправлений: 1)

не понял? с чего бы он не должен выводить нули?

Вызов функции отменяет оптимизации

все оптимизации — на усмотрение компилятора

т.е. вызов функции не дал заоптимизировать цикл.

совершенно не факт

next_time ★★★★★
()
Ответ на: комментарий от next_time

Сравните:

#include <iostream>
using namespace std;
void f(short int *ps, int *pi)
{
    for(int i = 0;  i < 10;  ++ i)
    {
        *pi = i;
        ps[0] = 0;
        ps[1] = 0;
        cout << *pi << '\n';
    }
}
int main()
{
    int y;
    short int *s = (short int*)&y;
    f(s, &y);
}

pavlick@pc:~$ g++ 1.cpp -std=c++14 -O3
pavlick@pc:~$ ./a.out 
0
1
2
3
4
5
6
7
8
9

pavlick ★★
() автор топика

Не надо пытаться перехитрить компилятор. Сегодня тебе это удастся, а завтра допилят LTO и прощай, нога.

Зачем тебе нарушать правило строгого алиасинга? Без этого несложно обойтись, особенно в C++.

memcpy работает не потому что функция, а потому что void* может указывать куда хочет.

anonymous
()

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

Во-первых если у тебя функция принимает указатель на char и указатель на int например, то компилятор в соответствии со strict aliasing решит что эти указатели не могут указывать на одну и ту же область памяти, и заоптимизирует(может заоптимизировать) это соответствующим образом, так что опасения все же нужны.

Во-вторых указатели на большие типы должны быть выравнены по определенным границам, и если ты например делаешь char a[5] = {1,2,3,4,5}; uint32_t *ptr = (uint32_t *)&a[1]; то это будет UB. http://spin.atomicobject.com/2014/05/19/c-undefined-behaviors/ см второй пункт. Это про C, но к плюсам думаю тоже относится.

SZT ★★★★★
()
Последнее исправление: SZT (всего исправлений: 2)
Ответ на: комментарий от anonymous

В целом согласен со всем. Выходит так нельзя (возможны ошибки в коде, пишу по памяти):

wifstream f("file");
double val;
f.read((wchar_t*)&val, 1);  // вместо 1 может быть что-то другое, в завис. от ос.
Т.е. кастовать в wchar_t * ?

pavlick ★★
() автор топика
Ответ на: комментарий от SZT

Это довольно спорно. Я не видел чётких разъяснений по этому поводу из стандарта. Если так, то в с++ нет способа получить доступ к отдельному байту фундаментального типа для записи (int, double, ...) (использовать неактивный член union также нельзя).

Эксперименты показывают, что способ работает. Есть такая статья [ulr]http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-ali...[/ulr]. Там предложен такой способ:

 uint32_t 
 swap_words( uint32_t arg )
 {
   char* const cp = (char*)&arg;
   const char  c0 = cp[0];
   const char  c1 = cp[1];
   const char  c2 = cp[2];
   const char  c3 = cp[3];
  
   cp[0] = c2;
   cp[1] = c3;
   cp[2] = c0;
   cp[3] = c1;
 
   return (arg);
 } 

pavlick ★★
() автор топика
Ответ на: комментарий от SZT

Или я неправильно понимаю. Автор статьи пишет:

The converse is not true. Casting a char* to a pointer of any type other than a char* and dereferencing it is usually in volation of the strict aliasing rule.

pavlick ★★
() автор топика
Ответ на: комментарий от pavlick

Если так, то в с++ нет способа получить доступ к отдельному байту фундаментального типа для записи

А memcpy?

использовать неактивный член union также нельзя

Это еще почему?

http://stackoverflow.com/a/11640603

Finally, one of the changes from C90 to C99 was to remove any restriction on accessing one member of a union when the last store was to a different one. The rationale was that the behaviour would then depend on the representations of the values. Since this point is often misunderstood, it might well be worth making it clear in the Standard.

Поправка: это все к С относится, я понятия не имею что они там в стандарте плюсов по этому поводу нагородили.

SZT ★★★★★
()
Ответ на: комментарий от SZT

1. Ведь memcpy надо как-то написать. 2. Да, c union != c++ union. 3. Автор статьи дальше поясняет во всяком случаи как он понимает. Вопрос вообще плохо освещен. В справочнике:

AliasedType is char or unsigned char: this permits examination of the object representation of any object as an array of unsigned char.

Складывается впечатление, что читать другой тип через char * можно, а писать нет.

pavlick ★★
() автор топика
Ответ на: комментарий от pavlick

Складывается впечатление, что читать другой тип через char * можно, а писать нет.

Это вряд ли, из N3797-3.10-10:

If a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined:
...
— a char or unsigned char type.
«access» != «read only».

xaizek ★★★★★
()
Ответ на: комментарий от anonymous

Сегодня тебе это удастся, а завтра допилят LTO и прощай, нога

Уважаемая нулина, если ты нихрена не понимаешь в том, о чем кукарекаешь - зачем ты кукарекаешь?

Никакого «type aliasing» с лто никогда не будет, ибо оно никогда не нужно.

anonymous
()
Ответ на: комментарий от SZT

Опять эта нулина. Если ты не способен читать и понимать, то зачем ты кукарекаешь? Я ведь приду и опять смешаю тебя с говном.

Во-вторых указатели на большие типы должны быть выравнены по определенным границам

Не должны. С какой жопы ты это решил?

и если ты например делаешь char a[5] = {1,2,3,4,5}; uint32_t *ptr = (uint32_t *)&a[1]; то это будет UB.

Не будет.

http://spin.atomicobject.com/2014/05/19/c-undefined-behaviors/ см второй пункт

Ты даже прочитать высер этого убогого нуля не можешь, но давай я тебе помогу.

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

anonymous
()
Ответ на: комментарий от anonymous

Note, that you don’t even have to dereference the pointer to stumble into undefined behavior. The actual conversion is undefined.

SZT ★★★★★
()
Ответ на: комментарий от DELIRIUM

Значит в плюсах остается только извращаться с memcpy

SZT ★★★★★
()
Ответ на: комментарий от pavlick

логично.

for(int i = 0;  i < 10;  ++ i)
    {
 *pi = i;
 cout << *pi << '\n';
}

и

for(int i = 0;  i < 10;  ++ i)
    {
y = 0;
cout << y << '\n';
}

есть разница?

next_time ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.