LinuxForum.Fans
Тема

Automatic vs static

Автор: Peter 54 сообщений 6191 просмотров
#229619

Говоря "работает", я имел в виду только, что можно присваивать указателю значение без приведения его к void*

text

(short int* )&int + 1

т.к. это уже void*, а как дальше компилятор будет обращаться с этим указателем, зависит и от него
и от программы. А стандарты есть разные, некоторые, например, позволяют вложенные
объявления подпрограмм.

Пётр.

#229630
Fat-Zer написал:
(ЕМНИП предварительной конвертации в void* требуют только плюсы)

да? ну может быть...

Peter написал:
т.к. это уже void*, а как дальше компилятор будет обращаться с этим указателем, зависит и от него

дык я объясняю вам, КАК он обращается - для него void* этот тот-же int*, просто записанный по другому. Само собой (для компилятора) разумеется, что int ну никак не может превратиться ВНЕЗАПНО в short, просто нет в С таких операций. Есть только операция присваивания одного int'а, другому short'у, а это совсем другая история. Т.е. компилятор вполне себе имеет право вовсе и не брать адреса переменной, что-бы потом по этому адресу найти переменную. Переменная вполне себе может лежать в регистре, и даже не знать, что её вдруг стали считать short'ом. Точно также она может лежать и в стеке. Компилятор попросту выкидывает всю лишнюю синтаксическую шелуху, в том числе и приведение типа. Даже без всяких проверок, ибо для него стандарт == Закон. Да, я понимаю, обидно. Но это не ассемблер. В стандарте полно таких мест, где поведение отдаётся на откуп компилятору - это оправдано, ибо позволяет компилировать код оптимальный под заданный CPU. От программиста требуется лишь написать такой код, поведения которого никак не зависит от компилятора. И дело осложняется ещё и тем, что CPU ещё с первопня имеют два ALU, и компилятор этим уже 15 лет пользуется. Вот выражение: X+Y, в каком порядке вычисляются X и Y? Откройте код, и убедитесь, что вычисляются они одновременно - процессор смешивает расчёт X и расчёт Y. На каком этапе произойдёт изменение вашей переменной - сказать невозможно. Также процессор может (и делает) временные копии, которые ещё и непредсказуемым образом переходят из одного в другой поток.

Peter написал:
А стандарты есть разные, некоторые, например, позволяют вложенные объявления подпрограмм.

стандарт один. Есть разные версии - есть ещё не готовая, и есть уже не действующая. Вложенные функции в действующей версии уже много лет. Как я понял, нужны для облегчения портирования с других ЯП. Вопрос много раз обсуждался, в том числе и со скандалами. В том числе и в рунете.

#229668

Что выражения и переменные будут вычисляться в порядке, определяемым компилятором, я знаю.
void* -это никак не int*, это указатель на байт в памяти, его тип не определён и по нему нельзя обращаться,
его можно только преобразовать в указатель на конкретный тип. Как компилятор будет интерпретировать
полученный таким образом указатель, действительно заранее неизвестно, с чем я и столкнулся.

Пётр.

#229696
Peter написал:
void* -это никак не int*

нет. Это как раз int*. Просто он в вашем коде так записан, но это просто синтаксическая шелуха.

Peter написал:
это указатель на байт в памяти, его тип не определён

эх... определён-таки. Ну поймите вы простую вещь - если вы пишете x =7, то у вас x, это НЕ переменная, а константа. А (void*) - это тоже вполне конкретный тип. Т.е. если вы в пределах одной единицы линковки написали int *x =..., потом void *y = x, то ваш y это и есть x. А *y это тоже самое (взаимозаменяемо), что и *x. Математики называют такие вещи "эквивалентными", хотя на первый взгляд, они разные. Но для создателей компиляторов - эквивалентны.Ну а если x это указатель на константу - так это и останется константой, что-бы вы с ней не делали. Если константа равна 7, и вы к ней "прибавили" 1, и потом распечатали, то весь этот мусор будет выкинут, и будет просто распечатана восьмёрка. Ну а если вы будете как угодно извращаться с указателями, то это всё семёркой и останется. Все ваши переходы будут исключены и отброшены.

В вашем конкретном случае есть ещё и преобразование типа. Оно будет выполнено. Но не по стандарту, не по правилам, а так, как удобнее компилятору. (ему удобнее "никак")

#229741

Пусть void* и конкретный тип, но по определению это неопределеённый тип,
по указателю этого типа невозможно обращаться к памяти. Его можно либо присваивать
с приведением к типу данных, либо ему присваивать адрес объекта некоего типа и тоже
с приведением к void*, поэтому с практической точки зрения void* не эквивалентен другому
указателю, даже если в какой-то момент они равны и указывают на один и тот же байт.

Вот как компилятор будет организовывать работу с указателем, относительно которго он
не знает, на что он указывает, то я не знаю, есть ли тут какие-то стандарты. Он вправе счесть
его указателм на константу, что я и наблюдал в Ubuntu при -O3, может и не оптимизировать.

Пётр.

#229762
Peter написал:
Пусть void* и конкретный тип, но по определению это неопределеённый тип,

это всё синтаксическая шелуха. Обычно void* это вполне определённый тип, и его следует понимать как например слово "человек". Понятно, что у этого человека есть имя, фамилия, и прочее. Может это даже женщина. И всё равно, мы используем для его наименования слово "человек". Однако, если void* это параметр нераскрываемой функции, то тогда он становится истинным указателем на НЁХ. В этом случае его даже не стоит и пытаться разименовать - программа рухнет как карточный домик в самый ответственный момент! Но пока это не истинный void*, а лишь псевдоним, мы можем его даже и разименовывать (помня о том, что так вообще-то делать нельзя, однако, если ваша программа таки собралась и работает, то этот бинарник скорее всего тоже будет работоспособен. Правда только в протестированной платформе). Проблема в том, что разименование void* не даёт никакой выгоды, если делать всё по правилам, вы напишете на 3 буквы больше, но это всё тоже будет выкинуто компилятором. И кроме того, вы избавитесь от возможных проблем в будущем.

Вообще, когда вы пишите на C/C++, не стоит боятся длинных и понятных конструкций - помните, компилятор выбросит весь ненужный мусор, и не надо делать это за него. Если уж так неймётся - сделайте ассемблерную вставку, а не надейтесь на UB.

Peter написал:
Вот как компилятор будет организовывать работу с указателем, относительно которго он
не знает, на что он указывает, то я не знаю, есть ли тут какие-то стандарты

стандарт прост - UB. Единственное, что вы можете сделать с истинным указателем void* - это его скопировать в другой void* (ну ещё взять его адрес, получив void**).

Peter написал:
Он вправе счесть его указателм на константу, что я и наблюдал в Ubuntu при -O3

он ещё вправе нарисовать розового слоника, и отправить почтового голубя.

#229789

Я никогда и не думал разименовывать void*.
Вообще исходный вопрос был по поводу работы компилятора с указателем, содержащий
адрес не объекта, а просто адрес в памяти, потом заговорили о правилах приведения
указателей и адресов, а как обращаться с void*, вы уж слишком подробно разъясняете.

Пётр.

#229794
Peter написал:
Я никогда и не думал разименовывать void*.

ИМХО это и есть разименовывание, хотя так сразу и не заметишь...

Peter написал:
вы уж слишком подробно разъясняете.

я надеюсь, что это прочитаете не только вы. Извините, ничего личного. Но другой читатель может быть и менее подкован.

#229797

я тут уже писал про UB и её жуткую коварность, на примере быдлокода x-- - --x; http://linuxforum.ru/viewtopic.php?pid=167373#p167373
Вот что получилось:

text
 |0000040b 8b45f4                         mov         eax, [ebp-0000000c]   // X1 <= x  | 10                                         
│0000040e 8b55f4                         mov         edx, [ebp-0000000c]  //  X2 <= x | 10
│00000411 4a                             dec         edx  // X2 = X2 - 1 | 9
│00000412 8955f4                         mov         [ebp-0000000c], edx  //  x <= X2 | 9
│00000415 8b55f4                         mov         edx, [ebp-0000000c]  //  X2 <= x (sic!)
│00000418 89c1                           mov         ecx, eax  // X3 <= X1 | 10
│0000041a 29d1                           sub         ecx, edx  //  X3 <= X3 - X2 | 10-9==1
│0000041c 89ca                           mov         edx, ecx  //  X2 <= X3 | 1
│0000041e 48                             dec         eax // X1 <= X1 - 1 | 9                                                                    
│0000041f 8945f4                         mov         [ebp-0000000c], eax  // x <= X1 | 9

Как видите, логика компилятора совсем не тривиальная. Да и то, это один CPU, да и тот 80486. Даже в таком простом коде довольно сложно было разобраться (в смысле - понять логику компилятора).

#229926

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

Пётр.

#229953
drBatty написал:
Как видите, логика компилятора совсем не тривиальная. Да и то, это один CPU, да и тот 80486. Даже в таком простом коде довольно сложно было разобраться (в смысле - понять логику компилятора).

количество cpu дел то не поменяет, а вот для пенька с его супер-пупер-скалярно-конвеерностью компилятор может что-то похитрожопей придумать...

#230012
Peter написал:
Я таких конструкций избегаю, тем более, что компилятор может изменять порядок вычислений,
даже заданный скобками.

преобразование void* -> другой тип, это не лучше.

Fat-Zer написал:
а вот для пенька с его супер-пупер-скалярно-конвеерностью компилятор может что-то похитрожопей придумать...

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

#232934
Peter написал:
void* -это никак не int*, это указатель на байт в памяти

И давно это void* - указатель на байт, а не на слово? Про то что он преобразуется в любой тип указателя можете не рассказывать.

#233069
RasenHerz написал:
И давно это void* - указатель на байт, а не на слово?

ещё с тех пор, как C не было. void* это адрес какого-то байта в памяти. В ассемблере все адреса такие. А вот лежать там, начиная с этого байта может всё что угодно.

RasenHerz написал:
Про то что он преобразуется в любой тип указателя можете не рассказывать.

причём тут "преобразуется"? void* указывает на что угодно, на любой байт в памяти, хоть в середину int'а.

Вы таки путаете то, на что void* указывает, с самим указателем. Да, размер указателя - это размер одного адресного слова. Сейчас это 32 или 64 бита. (прошу учесть, что размер int'а от размера void* вообще говоря не зависит, и как я понимаю, мы сейчас опять получим тонны убитого г-но кода, который сделан в предположении, что int x = (int)(void*)y; делать таки можно).

Войдите или зарегистрируйтесь, чтобы ответить.