- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
#include <stdio.h>
int main(void) {
int a, b;
int *p = &a;
int *q = &b + 1;
printf("%p %p %d\n", (void *)p, (void *)q, p == q);
return 0;
}
Нашли или выдавили из себя код, который нельзя назвать нормальным, на который без улыбки не взглянешь? Не торопитесь его удалять или рефакторить, — запостите его на говнокод.ру, посмеёмся вместе!
0
#include <stdio.h>
int main(void) {
int a, b;
int *p = &a;
int *q = &b + 1;
printf("%p %p %d\n", (void *)p, (void *)q, p == q);
return 0;
}
https://habr.com/company/pvs-studio/blog/418023/
0x7fff4a35b19c 0x7fff4a35b19c 0
кто понимает почему?
guest # 0
Elvenfighter # 0
https://wandbox.org/permlink/MF1L8CRAkYsZbuZg
https://wandbox.org/permlink/WYDI2qedK3TRvGSj <--
https://wandbox.org/permlink/NiuxKgk23SR2f37N
https://wandbox.org/permlink/AFcT9cU6Rn8W9bMC
admin # 0
codemonkey # 0 ⇈
guest # 0 ⇈
KOHTPArEHTTBOEuMAMKu # 0
codemonkey # 0
roskomgovno # 0
%p -- writes an implementation defined character sequence defining a pointer. Там можно написать "pituz" и это будет по стандарту.
Стандарт вообще ничего не говорит про то какие там числа.
Он лишь говорит что у поинтеров бывает арифметрика внутри массивов. Все.
guest # 0 ⇈
Это вообще зависит от реализации. Если локальные переменные последовательно пушатся в стек, то &b + 1 == & a. Но ведь они не обязаны пушиться. Компилятор может сделать сразу add rsp, some_size и расположить их в том порядке, в котором ему удобнее. Он может произвести выравнивание данных, и между переменными будут дырки. Он может хранить локальные переменные в регистрах, и тогда у них вообще не будет адреса (тут мы взяли адрес, и оптимизатору пришлось оставить переменные в стеке).
В общем, нужно потестировать на разных компиляторах.
guest # 0 ⇈
guest # 0 ⇈
C'est encore moi.
Проверил.
В gcc &b + 1 == &a. Такой же порядок у шланга, у Borland C, у Pelles C.
В Watcom C &b - 1 == &a, т. е. Watcom кладёт переменные в противоположном порядке (чем позже объявлена, тем выше, совсем как глобальные переменные). Такой же порядок у Digital Mars C.
У LCC дырки между переменными. У 32-битного компилятора почему-то &b - 2 == &a, а расстояние между первыми байтами смежных переменных 8 байт вместо 4.
У MSVC такие же дырки, но другой порядок: &b + 2 == &a.
roskomgovno # 0 ⇈
Порядок -- личное дело компилятора, не надо закладываться на то, что не написано в штандарте
guest # 0 ⇈
roskomgovno # 0 ⇈
Кстати, x86 умеет невыровненный доступ, но с пенальти (делая за программиста всю грязную работу). некоторые CPU не умеют и дают exception.
Некоторые умеют менять поведение в зависимости от регистра (кажется чуть-ли не в x86 когда-то такое или было или хотели сделать).
Тяжелое наследие 8088 в котором шЫрина всех шЫн была 1 байт, и можно было ничо не выравнивать
Но вот уже 286 читал 0,1 куда лучше чем 1,2 бо в ему приходилось делать ДВА чтения и половину выкидывать.
Тут бы и дать пизды программистам, включить exception, но intel пожалел обратную совместимость
guest # 0 ⇈
— При выделении памяти компилятор обычно разместит a и b сразу друг за другом.
— Это ж не куча, а стек, тут все предсказуемо.
Наивные... Им бы подбросить Watcom, Digital Mars или LCC. Сразу бы перестали верить в сказки.
roskomgovno # 0 ⇈
А мне кажется что в стандарте Си нету понятия "стек". Там есть "автоматические переменные"
guest # 0 ⇈
roskomgovno # 0 ⇈
Ну и адресация разная видимо. Вот как у PC есть две дорожки (IO и MEM) так и у них есть MEM-DATA и MEM-CODE.
Или я что-то пута?
Локальные переменные вроде должны лежать в области данных, не?
guest # 0 ⇈
roskomgovno # 0 ⇈
ну в любом случае если писнуть 123 в IP и в DX то там будут адреса разных ячеек
guest # 0 ⇈
KOHTPArEHTTBOEuMAMKu # 0 ⇈
bormand # 0 ⇈
> раздельная адресация
> разные шины
Это 3 ортогональных хуйни, не надо их смешивать.
Вот у того же stm32f4 от ядра уходят 3 шины (I, D, S) и авторы его относят к гарвардской архитектуре. Но все эти шины заведены в один bus matrix и ядро вполне может читать инструкции через I-bus из того же самого блока оперативки, в которую пишет данные через S-bus (но с разными будет быстрее т.к. нет конфликтов). При этом адреса у всех железок (блоков оперативки, флешки, периферии) на всех шинах одинаковы, хотя и не все шины bus matrix роутит ко всем железкам (через I-bus, к примеру, не видно USB контроллер и один из блоков оперативки).
roskomgovno # 0 ⇈
Вот тебе картинка
https://en.wikipedia.org/wiki/Harvard_architecture#/media/File:Harvard_architecture.svg
... with physically separate storage and signal pathways for instructions and data
То-есть у нас и устройства и шина разные.
А теперь скажи, как программист может явно НЕ указать какие данные ему нужны: код или дата? Как CPU поймет на какую шыну выставлять адрес?
А если программист явно указывает, то у нас и адресация разная.
>> Но все эти шины заведены в один bus matrix
деталь реализации. В классическом PC тоже одна шина была и на память и на io и на адрес.
>>на всех шинах одинаковы,
нахуй тогда три шины?
bormand # 0 ⇈
bus matrix - не шина. Это кроссбар. Через него все мастера могут работать параллельно, лишь бы с разными слейвами.
> нахуй тогда три шины?
Пирфоманс же. Если ты работаешь в "гарвардском" режиме (юзаешь разные блоки оперативки для кода и данных) -- всё работает быстро. А конфликты возникают только во время перезаливки кода в блок с кодом. Ну и даже если ты работаешь в "фон-неймановском" режиме (с одним блоком на код и данные), то хотя бы обращения к периферии не мешают фетчить новые инструкции. Когда у тебя нет ебических кешей как у интела -- вполне актуально.
Короче это почти труъ гарвардская архитектура с возможностью поюзать её как фон-неймановскую если пирфоманс не поджимает.
bormand # 0 ⇈
Вот интересно, а как на труъ гарварде читать или писать память кода на сишке? Скорее всего никак, только через асм отдельной инструкцией... Ну либо проц по какому-нибудь биту в адресе данных должен выбирать шину (что не особо приятно для пайплайна).
roskomgovno # 0 ⇈
Ну и как пишут и читают в IO на PC? outb какой-нить может быть. Под капотом там конечно отдельная инструкция:)
>>проц по какому-нибудь биту в адресе
Тоже так подумал, в конце концов контроллер памяти же по биту может выбрать канал например, но мне не нравится мысль делить линейное прострванство на две части по биту.
Звучит как ад для программиста
bormand # 0 ⇈
Скорее как ад для разраба проца. От каждой записи/чтения через шину кода в любом случае конвейер пидорасит... Хоть с инструкцией хоть с битом в адресе.
roskomgovno # 0 ⇈
Но ты представь себе программиста у которого foo это адрес памяти, а (например) "foo & 0xF7" это адрес в I/O)
bormand # 0 ⇈
roskomgovno # 0 ⇈
извините
ты прав: даже если у меня верхний гигабайт замаплен на видеокарту и внизу немного биоса и VGA то все равно есть такой бит в моем 64 битном указателе, повернув который я получу адрес устройства
bormand # 0 ⇈
roskomgovno # 0 ⇈
но как, если у переменной на след. строчке берут адрес?
bormand # 0 ⇈
З.Ы. А ещё у avr вроде все регистры через память видно, так что можно и адрес регистра положить если ненадолго 🙂