Эбботт: В чем смысл?
Костелло: Я понимаю суть, но куда она указывает?!?
Эбботт: Какой компилятор вы используете?
Костелло: Не удивляйтесь, меня зовут не компилятор, и я иду, а не бегу. Похоже, я тороплюсь?
Жаль, что Эббота и Костелло уже нет в живых. Я уверен, что они сделали бы достойное обновление скетча «Кто первый», который затмит мой.
У нас больше нет Эбботта и Костелло, чтобы развлекать нас. Но это нормально. Мы, программисты, довольно веселая компания, и компьютеры умеют нас развлекать.
Как программист, вы могли бы задуматься над вопросом: В чем смысл? быть вне сферы действия. Но немного другой вопрос, который определенно актуален для программиста на C: На что указывает этот указатель? Рассмотрим пример:
Если вы привыкли смотреть на C-код, то могли заметить нечто странное. Первый оператор printf
кажется вполне нормальным. Он выводит адрес памяти первого элемента двухэлементного массива символов. Но что делает следующий оператор printf
??? Сколько элементов в массиве с именем 0
??? 0
не является объявленной переменной, и переменные в любом случае не могут иметь номер в качестве первого символа своего имени. Что будет делать этот код? Будет ли он даже скомпилирован? И если да, то какой будет результат? Давайте бросим его компилятору и посмотрим, что произойдет…
Хорошо, но что будет на выходе после выполнения? Прежде чем мы доберемся до этого…
Как выглядеть умным, не отвечая на вопрос
Когда ваш друг задает вам вопрос о каком-то малоизвестном уголке языка C, ответьте на вопрос другим вопросом, например: «Какой компилятор вы будете использовать для компиляции этого кода, о котором вы говорите? Мне нужно знать компилятор, который вы используете, чтобы дать окончательный ответ». Этот ответ должен дать вам некоторую передышку, если вы не в курсе C arcana.
Мало ли они знают…
… что вы настойчивы и на самом деле *будете* устанавливать другой компилятор C, чтобы быть знающим программистом. Какой компилятор поставить? Что ж, gcc
и clang
— два наиболее часто используемых компилятора, и они должны быть ими какое-то время (с точки зрения 2017 года). Итак, уже увидев, как gcc
передает код нашего примера, давайте посмотрим, что clang v.3.8.0
может сказать о нашем странном синтаксисе:
clang
, похоже, тоже не возражает. Что эти компиляторы знают такого, чего не знаем мы?
Для обзора:
Мы состряпали скрипт с каким-то странным синтаксисом (см. выше). В частности, этот фрагмент: &0[c]
Кажется, он просит компилятор C сослаться на массив, который не имеет допустимого имени переменной. Индекс массива внутри квадратных скобок — это указатель c
, указывающий на место в верхней части памяти на моем обычном ноутбуке с 64-битной операционной системой. Мы знаем, что любой результат, который мы получим от 0[c]
, будет разыменован амперсандом: &
даст нам не значение, хранящееся в 0[c]
, а местоположение в памяти 0[c]
.
Вот вывод (скомпилированный gcc v.4.8.4 в 64-битной системе Linux):
Что нам говорит этот вывод?
Важно отметить, что мы распечатали местоположение элемента массива двух разных массивов, используя: &a[0]
и &0[a]
. То, что мы видим выше, когда мы изучаем вывод нашего сценария, заключается в том, что две области памяти являются одними и теми же. Почему это должно быть? Вот вывод, к которому я вынужден прийти: я мог бы ввести любое число для имени массива, и до тех пор, пока оно удовлетворяет этому условию:
Самое низкое доступное место в памяти ≤ число, связанное с именем переменной массива ≤ максимальное место в памяти.
Кроме того, число, связанное с именем переменной (например, a
), можно заменить простым числовым значением (например, 0
). Разумно ли жестко запрограммировать числовую ячейку памяти вместо того, чтобы позволять C определять, где в памяти хранить ваши переменные? Как правило, нет. Но возможно ли это? Да, доказательство прямо здесь, и оно даже работает на двух разных компиляторах.
И это дает мне что?
Экспериментируя с кодом, мы получаем представление о внутренней работе языка Си, реализованной компилятором. И мы на шаг ближе к самому возвышенному искусству: созданию и пониманию запутанного кода!
Спасибо моей однокурснице Джулии Ли, которая написала и протестировала код для этой записи в блоге!
Кроме того, вам следует отдать должное, если вы измените и протестируете пример сценария. Что произойдет, если указатель указывает не на char
, а на что-то другое?
Загляните в мою учетную запись на github здесь и подпишитесь на меня в твиттере!