LINUX.ORG.RU

Можно ли в Си управлять размещением переменных как в ObjectPascal?

 ,


0

1

Вот в Delphi (FreePascal) есть такая директива absolute

var
   a:array[1..10] of integer;
   b:String absolute a;

....

В результате компилятор разместит переменные a и b по одному адресу. Это очень удобно для разных нетривиальных преобразований типов, когда они запрещены напрямую.

Другой способ: поля в записях record, вроде такого

type TMyRecord = record
case Boolean of  //Вместо Boolean можно писать любой перечислимый тип, это не контролируется
    1: (i: Int64);
    2: (a1: byte; a2: byte; a3: byte; a4: byte; a5:byte; a6:byte; a7:byte; a8:byte);
end;

var 
   M:TMyRecord;

M.a2 = // Доступ к 2-му байту представления i.

Понятно, что можно завести указатели на переменные и присваивать им различные адреса, но это дополнительная операция, а вот как чтобы сразу компилятор размещал переменные в одном месте. Между прочим, в Borland Pascal, которые еще для MS-DOS, можно было в absolute даже адрес памяти указать, сейчас нельзя так как это было актуально только в реальном режиме.

★★★★★

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

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

grem ★★★★★
()

Прямого переносимого аналога нет, так как strict aliasing позволяет компилятору считать указатели разных типов независимыми.

i-rinat ★★★★★
()
Ответ на: комментарий от dzidzitop

Не, ну если так через адресацию извращаться, то можно и так, но зачем?

int    a;
char * b;

a = 0x0030;
b = (char *)&a;

printf("\"%s\"\n", b);
"0"

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

Обращение при этом к a и b идут через a_t

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

Понятно, что можно завести указатели на переменные и присваивать им различные адреса, но это дополнительная операция

Дорогой мой, absolute это костылище который завезли в паскаль потому что там нельзя явно преобразовать тип.

int a[10];
...
(char*) a

Никаких дополнительных указателей и переменных не нужно.

no-such-file ★★★★★
()
Ответ на: комментарий от dzidzitop

Точно не. В случае absolute две синтаксически разные переменные располагаются по одному адресу в памяти.

praseodim ★★★★★
() автор топика
Ответ на: комментарий от no-such-file

Дорогой мой, absolute это костылище который завезли в паскаль потому что там нельзя явно преобразовать тип.

Не-а, можно преобразовывать. В ObjectPascal имя типа может вызываться как функция преобразования. Но absolute это не костылище, а довольно удобно на практике.

var
   a:array[0..9] of integer;

...

Char(a);
praseodim ★★★★★
() автор топика
Ответ на: комментарий от grimwaken

2) attribute или pragma в зависимости от компилятора. Например

А конкретный пример? Я что-то не нашел варианта __attribute__ с размещением по адресу другой переменной, к тому же вариант по ссылке не поддерживается gcc. Много всякого про виды выравнивания и упаковки, но не это.

Интересно, что есть атрибут alias как синоним для вызова функций, но это только для функций и не совсем то.

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

может вызываться как функция преобразования

Это не работает для произвольных типов, не говоря уже о составных типах. ЕМНИП нельзя преобразовать число в указатель.

Char(a);

И какой же будет результат? Я прям теряюсь в догадках, ведь 'a' это массив... лол.

no-such-file ★★★★★
()
Ответ на: комментарий от PPP328

«ЦАРЬ!.. НЕНАСТОЯЩИЙ!!!»

Пишу по настоящему виртуальному адресу процесса. В реальном режиме это будет физический адрес.

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

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

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

Да хоть 100500 переменных. Фактическое место в памяти будет выделено под одну максимальную по длине.

С абсолютным адресом тоже все ясно. Компилятор просто ссылаться будет на этот адрес. Но это имеет смысл только в режимах процессора без трансляции адресов с защитой памяти. Поэтому оно было в BP, но исчезло в Delphi.

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

ТС хочет. А зачем - я хз. Может, видеопамять редактировать хочет через замапленую память.

Это не замапленная память, это просто фактически алиасы для одного участка памяти. Так удобно с данными работать, особенно при разборе каких-то бинарных форматов.

Очень странно, что такая фича есть в паскале, но не нашел в С/C++ даже в компиляторозависимых директивах.

praseodim ★★★★★
() автор топика
Ответ на: комментарий от no-such-file

Это не работает для произвольных типов, не говоря уже о составных типах. ЕМНИП нельзя преобразовать число в указатель.

Можно. Все можно преобразовать, есть только одно ограничение: размер типа слева должен быть не меньше, чем справа.

И какой же будет результат? Я прям теряюсь в догадках, ведь 'a' это массив... лол.

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

var
   a:array[0..9] of integer;
   b:Char;

....

b=Char(Pointer(@a)^);  //Берется адрес a и разыменовывается как указатель на неопределенный тип

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

Очень странно, что такая фича есть в паскале, но не нашел в С/C++ даже в компиляторозависимых директивах.

Ты объяснишь, наконец, зачем тебе это? И тебе уже сказали, что в с\с++ это тоже есть: b = &a.

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

Очень странно, что такая фича есть в паскале, но не нашел в С/C++ даже в компиляторозависимых директивах.

такое творчество в стандартах C и C++ называется «undefined behaviour». А для остальных случаев есть union.

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

Ты объяснишь, наконец, зачем тебе это?

Для того же, для чего есть union, но с более простым синтаксисом. union добавляет лишнюю синтаксическую сущность.

И тебе уже сказали, что в с\с++ это тоже есть: b = &a.

b=&a создает ДВЕ переменные на стеке (или где там), в одной из них будет лежать адрес другой. А вот b absolute a - это одна переменная с синтаксическим сахаром из-за которого доступ как двум разным.

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

почитай про endianness и не разбирай бинарные форматы так

А вот как раз ентот endianness не противоречит такому подходу, если понимать что делаешь.

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

Ни C ни C++ ничего не гарантируют. А для конкретного набора платформа + ОС + компилятор есть union. В том числе по абсолютному адресу, если зудит заниматься реалмодовым турбопаскализмом на C или на C++.

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

В общем в холивар скатилось по принципу: если нет, то и не надо. И union достаточен.

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

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

b=&a создает ДВЕ переменные на стеке (или где там), в одной из них будет лежать адрес другой. А вот b absolute a - это одна переменная с синтаксическим сахаром из-за которого доступ как двум разным.

int main() {
	auto a = 10;
	auto & b = a;
	auto & c = a;
	++b;
	++c;
	return a;
}
00000000004003c0 <main>:
  4003c0:	c7 44 24 fc 0a 00 00 	movl   $0xa,-0x4(%rsp)
  4003c7:	00 
  4003c8:	8b 44 24 fc          	mov    -0x4(%rsp),%eax
  4003cc:	83 c0 01             	add    $0x1,%eax
  4003cf:	89 44 24 fc          	mov    %eax,-0x4(%rsp)
  4003d3:	8b 44 24 fc          	mov    -0x4(%rsp),%eax
  4003d7:	83 c0 01             	add    $0x1,%eax
  4003da:	89 44 24 fc          	mov    %eax,-0x4(%rsp)
  4003de:	8b 44 24 fc          	mov    -0x4(%rsp),%eax
  4003e2:	c3                   	retq   
  4003e3:	66 2e 0f 1f 84 00 00 	nopw   %cs:0x0(%rax,%rax,1)
  4003ea:	00 00 00 
  4003ed:	0f 1f 00             	nopl   (%rax)

наша переменная ---> -0x4(%rsp)

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

упс. забыл пофиксить

volatile auto a = 10;
иначе вышло бы:
00000000004003c0 <main>:
  4003c0:	b8 0c 00 00 00       	mov    $0xc,%eax
  4003c5:	c3                   	retq   
  4003c6:	66 2e 0f 1f 84 00 00 	nopw   %cs:0x0(%rax,%rax,1)
  4003cd:	00 00 00 

я это к тому, что не стоит париться из-за таких мелочей, доверься компилятору

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

да блин.

union a_t
{
   int a[10];
   char * b;
};


a_t *p = (a_t *) 0xBIGDOOD0;

p->b = "aaaaaaaa";

int x = p->a[1];

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

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

Интересно, не знал. Спасибо.

Не много не соображу. В объявлении & слева, то есть, адрес присвается? Это штатный эффект в Си или результат оптимизации конкретного компилятора и в другом компиляторе или для другой платформы может оказаться все-таки две переменные?

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

кхем, эти ребята, очевидно тоже «знали что делают» и пренебрегали базовыми правилами безопасности в сишном коде?

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

вот и блин

Естественно, что присвоить можно и конкретное число в качестве адреса, но я то имел ввиду явное размещение в объявлении.

Впрочем анонимус выше подкинул пищу для размышления.

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

Вот тут смотри. Но средствами языка C или C++ задать значение адреса переносимым способом нельзя. И писать и читать через указатели разных типов - тоже.

http://stackoverflow.com/questions/4067811/how-to-place-a-variable-at-a-given...

https://mcuoneclipse.com/2012/11/01/defining-variables-at-absolute-addresses-...

dzidzitop ★★
()
Последнее исправление: dzidzitop (всего исправлений: 1)
Ответ на: комментарий от praseodim

В объявлении & слева, то есть, адрес присвается?

Да, адрес присваивается. Это ссылка (c++) - более безопасная альтернатива голым указателям.

Это штатный эффект в Си или результат оптимизации конкретного компилятора и в другом компиляторе или для другой платформы может оказаться все-таки две переменные?

Это gcc постарался. Можно выключить оптимизацию и получить 3 переменных на стеке.

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

Это gcc постарался. Можно выключить оптимизацию и получить 3 переменных на стеке.

Жаль, значит это эффект оптимизации. Ну что ж union тоже можно использовать для гарантии.

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

https://mcuoneclipse.com/2012/11/01/defining-variables-at-absolute-addresses-...

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

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

Это самый чистый эмулятор absolute на C и C++ из всех возможных. Остальные способы (кроме явной поддержки компилятором) вообще не дают гарантий. Ну и зачем нужен absolute кроме доступа к устройствам минуя ОС в реальном режиме или очень тонких оптимизаций в очень редких случаях - хз.

dzidzitop ★★
()

Оба вопроса решаются через union. По крайней мере c C++ компилятором:

static union {
    int a[10];
    char b[sizeof(int) / sizeof(char) * 10];
};

struct TMyRecord {
    union {
        int64_t i;
        struct {
            uint8_t a1;
            uint8_t a2;
            uint8_t a3;
            uint8_t a4;
            uint8_t a6;
            uint8_t a7;
            uint8_t a8;
        };
    };
};

https://wandbox.org/permlink/LmbxXcekwjeRwbCj

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

Ну и зачем нужен absolute кроме доступа к устройствам минуя ОС в реальном режиме или очень тонких оптимизаций в очень редких случаях - хз.

Ну я же говорю, для того, для чего и union. Кроме того, конкретно я absolute применял для работы с буфером данных, произвольного типа, передаваемом в функцию. Примерно так:


procedure (var buffer; length:integer)
var 
    a:array[0..0] of char absolute buffer;
begin
{$R-} //Чтобы отключить проверку границ 
  writeln(a[length-1]); //Напечатали последний символ.
 //Предполагаем однобайтовую кодировку и sizeof(char)=1
{$R+}
end;

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

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

А про проблема с выравниваем уже сказали? Попробуй прочитать int по невыравненому адресу на чем нибудь более экзотичном, чем x86. Далее arm уже не позволит, если правильно помню.

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

Жаль, значит это эффект оптимизации.

Когда пишут на Си, то всегда учитывают оптимизирующие возможности компилятора, и всегда в голове держат абстрактный ассемблерный листинг вместо кода. Опцию -O0 применяют только для отладки.

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

b=Char(Pointer(@a)^); //Берется адрес a и разыменовывается как указатель на неопределенный тип

И что? b всё ещё не массив.

no-such-file ★★★★★
()
Ответ на: комментарий от praseodim

Ты неверно понимаешь что такое переменная. Это не место в памяти, это просто абстрактное хранилище, которое может отображаться куда угодно - в память, на регистр и даже в никуда, если значение удастся подставить. Так в любом языке выше ассемблера, и в паскале тоже.

По сабжу - да, только union. До C99 (или таская -fno-strict-aliasing) также указателями можно играть. Плюсовые ссылки - довольно точный аналог, но только для совместимых типов, не факт что тут они не сломаются даже с выключенным алиасингом.

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