Что означает && в void * p = && abc;


102

Я наткнулся на кусок кода void *p = &&abc;. Какое &&здесь значение? Я знаю о ссылках на rvalue, но думаю, что &&в этом контексте они используются по-другому. Что &&указывает в void *p = &&abc;?


14
Где ты это видел?
user541686

3
Я также хотел бы знать, где вы это видели.
Флавий

Ответы:


154

&&является расширением gcc для получения адреса метки, определенной в текущей функции.

void *p = &&abc недопустимо в стандартных C99 и C ++.

Это компилируется с g ++.


42
А теперь представьте, насколько лучше были бы программисты, если бы в gcc по умолчанию была -pedantic. Тогда люди будут изучать C вместо нерелевантной информации о gnus.
Lundin

4
Что за взлом. Если они изобретают новый синтаксис для указателей меток, они должны также изобрести новый тип для него вместо использования void*.
Марк Рэнсом

1
@Prasoon Saurav: Тот, кто проголосовал против, вероятно, не одобряет эту функцию и отказывается от нее.
Чак

@Lundin: То же самое можно сказать и о большинстве компиляторов. __forceinline? __declspec(naked)? Один из моих любимых MSVCisms:, template<typename T> class X { friend T; }который является недопустимым C ++ 03.
Себастьян Мах

Вторая ссылка мертва.
Cœur

96

Как это узнать

Это адрес метки, и это особенность GCC .

int main(void) {
    void* startp;
s:
    startp = &&s;
    printf("the assignment above starts at address %p\n", startp);
    return 0;
}

Вы могли понять это сами, протестировав:

int main(void) {
    void* startp;
    int a;
    startp = &&a;
    printf("startp=%p\n", startp);
    return 0;
}

В этом случае GCC говорит:

ошибка: метка 'a' используется, но не определена

Под капотом - сборка

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

После того, как ОС загружает файл .exe с диска, компонент операционной системы называется «загрузчик» (в Windows есть «Загрузчик PE», в Linux есть «Загрузчик ELF» или, возможно, даже другие, если они скомпилированы в kernel), он выполняет «виртуализацию» этой программы, превращая ее в процесс.

Этот процесс считает, что он единственный в ОЗУ и имеет доступ ко всей ОЗУ (то есть 0x00000000-0xFFFFFFFF на 32-битной машине).

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

Теперь метка в исходном коде - это, по сути, адрес. "goto label;" ничего не делает, кроме перехода к этому адресу (подумайте об указателе инструкции в сборке). На этой этикетке хранится этот адрес ОЗУ, и именно так вы можете узнать этот адрес.

Изучив ASM, вы поймете, что этот адрес указывает на инструкцию в .textразделе исполняемого файла. В.text разделе хранится исполняемый (двоичный) код вашей программы.

Вы можете проверить это с помощью:

objdump -x a.out

Практический пример

Как описано в GCC , вы можете использовать это для инициализации таблицы переходов. Некоторые генераторы сканеров, такие как re2c (см. -gПараметр), используют это для создания более компактных сканеров. Может быть, есть даже генератор парсеров, использующий ту же технику.


4
Скомпилируйте с -std = c99 -pedantic.
Lundin

2
Очень важно упомянуть, что это расширение GCC, а не основная часть языка
jalf

1
Флавий впечатляет.
Октавиан А. Дамиан

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

1
Спасибо за подробное объяснение. Оценил анализ, связанный с сборкой.
CᴴᴀZ
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.