0
PVOID trampoline = (PVOID )1; //для примера присвоил рандомное значение
((PBYTE)trampoline)++; //тут ошибка (Выражение должно быть допустимо для изменения левосторонним значением)

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

  • 1
    Так а что вы хотите менять? Если сам указатель - то (*(PBYTE*)&trampoline)++;/ Если значение по этому адресу - (*(PBYTE)trampoline)++;. "По-моему, так." (с) Пух – Harry May 27 '21 at 08:42
  • @Harry а почему такая запись неверна? Ведь это то же самое по сути. Если бы мы указатель объявили как PBYTE, то все без ошибок было – javay haster May 27 '21 at 08:44
  • 1
    Потому что это было бы lvalue - переменная. А (PBYTE)trampoline - это значение указателя, но не он сам. Просто значение, которое не может выступать в качестве ссылки. – Harry May 27 '21 at 09:10
  • "Причем в чисто сишном компиляторе все нормально компилируется" - ничего подобного. – user7860670 May 27 '21 at 10:54
  • @Harry Мне кажется, что в таком случае будет нарушение strict aliasing. – user7860670 May 27 '21 at 10:57
  • @user7860670 Откровенно говоря, именно поэтому и написал свое пресловутое "по-моему, так" :) Не знаю. Это такой темный угол - strict aliasing - что врубиться, что можно, что нет - я никак не могу его понять, только что вызубрить наизусть... а с точки зрения практики мне кажется, что его опасность преувеличена. По крайней мере, ни разу не сталкивался с проблемами из-за этого... – Harry May 27 '21 at 11:08

1 Answers1

2

стандарт :

7.6.3 Explicit type conversion (cast notation)

The result of the expression (T) cast-expression is of type T. The result is an lvalue if T is an lvalue reference type or an rvalue reference to function type and an xvalue if T is an rvalue reference to object type; otherwise the result is a prvalue. [Note 1 : If T is a non-class type that is cv-qualified, the cv-qualifier s are discarded when determining the type of the resulting prvalue; see 7.2. — end note]

перевод :

7.6.3 Явное преобразование типов (приведенная нотация)

Результат приведения выражения (T) имеет тип T. Результатом является значение lvalue, если T является типом ссылки lvalue или ссылкой rvalue на тип функции, и значение xvalue, если T является ссылкой rvalue на тип объекта; в противном случае результатом является значение prvalue. [Примечание 1 : Если T-тип, не относящийся к классу, который квалифицируется cv, квалификатор s cv отбрасывается при определении типа результирующего значения prvalue; см. 7.2. — конец примечания]

Приведение на объект даёт новый временный объект. Временный объект это не lvalue. А ссылка или (*ptr) будет lvalue после приведения типа. И её можно будет менять.

# include <cstdint>
# include <iostream>
typedef void * PVOID ;
typedef uint8_t * PBYTE ;
typedef uint8_t BYTE ;
BYTE arr[]={0,0,0};
PVOID trampoline = (PVOID )(&arr[0]);
int main(){
    // тут была ошибка (Выражение должно быть допустимо для изменения левосторонним значением)
    PBYTE && tmp = ((PBYTE)trampoline);
    tmp ++ ;
    std::cout<<"trampoline = "<<trampoline<<std::endl;
    std::cout<<"tmp = "<<(void*)tmp<<std::endl;
    (*(PBYTE*)&trampoline)++;
    std::cout<<"trampoline = "<<trampoline<<std::endl;
    ((PBYTE&)trampoline)++;
    std::cout<<"trampoline = "<<trampoline<<std::endl;
}

=>

trampoline = 0x404191
tmp = 0x404192
trampoline = 0x404192
trampoline = 0x404193

Справочник по Left-value Right-value :

variable = LValue
& variable = RValue
(* (any pointer)) = LValue
((T)(any value)) = RValue
(*(T*)(any pointer)) = LValue
((T&)(any link)) - LValue
AlexGlebe
  • 17,227
  • Мне кажется, что в (*(PBYTE*)&trampoline)++; будет нарушение strict aliasing. – user7860670 May 27 '21 at 10:57
  • Был указатель на байт 1 , стал указатель на байт 2. Где тут стрикт? @user7860670 – AlexGlebe May 27 '21 at 11:12
  • &trampoline - указатель на на указатель void, (PBYTE*) кастует его в указатель на указатель на byte, разыменование - UB. – user7860670 May 27 '21 at 11:20
  • Стандарт уточняет, что грубая смена типа указателя A* -> B* -> A*. Не приведёт к проблемам. Осталось только спросить у указателя 1 - а правильный ли ты (можешь ли указывать на байт?). В примере эта единица всего-лишь пример. @user7860670 – AlexGlebe May 27 '21 at 11:27
  • Тут совсем не A* -> B* -> A*, тут A* -> B* -> разыменование B* – user7860670 May 27 '21 at 11:28
  • Это у автору вопроса будет проблема. Правильно, ли он приводит типы. Вопрос был про rvalue <-> lvalue . @user7860670 – AlexGlebe May 27 '21 at 11:40
  • У автора код должен выдавать ошибку компиляции, а вот ваш код в ответе - это проблема. – user7860670 May 27 '21 at 11:42
  • Так нет проблем. – user7860670 May 27 '21 at 11:46
  • uint8_t ran = 1 ; PVOID trampoline = (PVOID ) & ran ; - а так нет проблем ? @user7860670 – AlexGlebe May 27 '21 at 11:46
  • Хм, вы случаем предыдущий комментарий не удаляли? А то получается что я сначала ответил, а потом пришел вопрос. – user7860670 May 27 '21 at 11:49
  • Кстати я вспомнил, что был вопрос про strict aliasing, где я приводил похожий пример https://ru.stackoverflow.com/questions/749685/strict-aliasing-%d0%b8-%d1%80%d0%b5%d0%b0%d0%bb%d1%8c%d0%bd%d1%8b%d0%b5-%d0%ba%d0%be%d0%bc%d0%bf%d0%b8%d0%bb%d1%8f%d1%82%d0%be%d1%80%d1%8b – user7860670 May 27 '21 at 11:51
  • Хватит критики, всем всё понятно. @user7860670 – AlexGlebe May 27 '21 at 11:53