Страницы

Поиск по вопросам

понедельник, 17 декабря 2018 г.

Как работают compound literals или внутреннее представление

Вероятно некоторые знают что такое compound literals и умеют использовать это на практике.
Однако у меня возник вопрос по внутреннему представлению этих литералов.
Ну предположим я имею такую запись: int *a = (int[]){10, 20, 30}; - таким образом я создал и присвоил указателю адрес созданного где-то в памяти массива типа int.
Можно ли предположить что запись: int *a = (int[]){10, 20, 30}; - эквивалентна следующей записи?
int arr[] = {10,20,30}; int *parr = arr;
По какой схеме идет присвоение адреса компаунд литерала?


Ответ

Да, можно так считать. Аналогия из двух объявлений вами составлена корректно.
Время жизни compound literal в точности определяется тем декларативным регионом, в котором он указан и совпадает с временем жизни обычной переменной того же типа, объявленной в том же контексте. То есть compound literal - это просто аналог безымянной переменной соответствующего типа с соответствующим инициализатором.

Однако с этим в языке С связана одна тонкость. До появления compound literals в С не существовало отдельного вопроса времени жизни объектов, объявленных, например, в ветках if
if (...) int a = 5; else int b = 42;
Так как объявления в языке С не являются и никогда не являлись statements, такой код является в С некорректным. Чтобы в С создать локальный декларативный регион, допускающий объявление переменных, необходимо явно использовать {}. То есть время жизни обычных (именованных) локальных объектов в С всегда очерчено явным блоком {} и полностью объясняется через свойства блока.
Однако с появлением в языке compound literals проблема времени жизни возникла вот в такой форме
int *a, *b, *c; if (a = (int []) { 1, 2, 3 }) b = (int []) { 4, 5, 6 }; else c = (int []) { 7, 8, 9 };
Каково должно быть время жизни таких compound literals? Должны ли они существовать после if?
До сих пор ни сам if, ни его ветки в языке С не являлись отдельными декларативными регионами - как сказано выше, в этом не было никакой необходимости. Однако после введения в язык compound literals в С99, было принято решение пойти по пути С++ и локализовать время жизни таких литералов. Теперь в С как сам if, так и его ветки, являются неявными блоками, ограничивающими время жизни созданных в них compound literals (и область видимости объявленных в них имен, см. ниже).
Т.е. теперь, начиная с С99, вышеприведенный if эквивалентен
int *a, *b, *c; { if (a = (int []) { 1, 2, 3 }) { b = (int []) { 4, 5, 6 }; } else { c = (int []) { 7, 8, 9 }; } }
со всеми вытекающими, т.е. с "повисшими" значениями указателей. Эта же модификация относится и к другим видам statements.
Это, кстати, привело к потере обратной совместимости с "классическим" С в ряде редко используемых контекстов. Например, в языке С разрешается объявлять новые типы в sizeof и в кастах. Пользуясь этой возможностью в С89/90 можно написать такой код
int foo(int a) { if (a == sizeof(enum { A })) a = sizeof(enum { B }); else a = sizeof(enum { C });
return a + A + B + C; /* ОК в С89/90. Ошибка в C99 */ }
После С99, из-за введения неявных блоков в if, такой код больше не компилируется.

Комментариев нет:

Отправить комментарий