Почему в C есть различие между -> и.? - PullRequest
55 голосов
/ 29 ноября 2009

ОК, это не имеет серьезных последствий, но меня это беспокоит while: Есть ли причина для различия между операторами -> и .?

Конечно, текущее правило таково, что . действует на структуру, а -> действует на указатель на структуру (или объединение). Но вот как это работает на практике. Пусть s будет структурой, включающей элемент x, и пусть ps будет указателем на структуру той же формы.

Если вы напишите

s->x

компилятор выдаст предупреждение в виде

Вы имели в виду s.x. Пожалуйста, перепечатайте это и перекомпилируйте.

Если вы напишите

ps.x

компилятор выдаст предупреждение в виде

Вы имели в виду ps-> x. Пожалуйста, перепечатайте это и перекомпилируйте.

Поскольку компилятор знает тип s и ps во время компиляции, он имеет всю информацию, необходимую для интерпретации того, каким будет правильный оператор. Я подозреваю, что это не похоже на другие предупреждения (как пропущенная точка с запятой), в том, что в правильном исправлении нет двусмысленности.

Итак, вот гипотетическое предложение для комитета по стандартам C1x (которое никогда не будет рассматриваться, потому что ISO находится на консервативной полосе):

Учитывая выражение lhs.rhs, если lhs является типом struct или union, тогда выражение должно ссылаться на элемент lhs с именем rhs. Если lhs имеет тип указатель на структуру или -union, то это должно быть интерпретируется как (* lhs) .rhs.

Это, безусловно, сэкономит нам все время и облегчит людям изучение C [и я научил достаточно C говорить, что учащиеся считают, что -> вещь либо сбивает с толку, либо раздражает.]

Есть даже прецедент, когда C делает несколько подобных вещей. Например, по причинам реализации объявления функций всегда приводятся к указателю на функцию, поэтому f(x,y) и (*f)(x,y) будут работать независимо от того, был ли f объявлен как функция или как указатель на функцию.

Итак, мой вопрос: что не так с этим предложением? Можете ли вы вспомнить примеры, в которых между ps.x и s.x может быть фатальная двусмысленность, или почему в противном случае полезно сохранять обязательное различие?

Ответы [ 7 ]

37 голосов
/ 29 ноября 2009

Не думаю, что в твоих словах есть что-то безумное. Использование . для указателей на структуры будет работать.

Однако мне нравится тот факт, что указатели на структуры и структуры обрабатываются по-разному.

Это дает некоторый контекст об операциях и подсказках относительно того, что может быть дорогим.

Рассмотрим этот фрагмент, представьте, что он находится в середине довольно большой функции.

s.c = 99;
f(s);

assert(s.c == 99);

В настоящее время я могу сказать, что s является структурой. Я знаю, что он будет скопирован полностью для вызова f. Я также знаю, что это утверждение не может выстрелить.

Если бы разрешалось использование . с указателями на структуру, я бы не знал ничего из этого, и утверждение могло бы сработать, f могло бы установить s.c (err s->c) на что-то другое.

Другим недостатком является то, что это уменьшит совместимость с C ++. C ++ позволяет -> быть перегруженным классами, так что классы могут быть похожими на указатели. Важно, чтобы . и -> вели себя по-разному. «Новый» код C, использующий . с указателями на структуры, вероятно, больше не будет приемлемым, как код C ++.

30 голосов
/ 29 ноября 2009

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

p->x = 3;

вы знаете, p - указатель, но если вы разрешите:

p.x = 3;

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

27 голосов
/ 29 ноября 2009

Отличительной особенностью языка программирования C (в отличие от его относительного C ++) является то, что модель затрат очень явная . Точка отличается от стрелки, потому что стрелка требует дополнительной ссылки на память, и C очень осторожно делает количество ссылок на память очевидным из исходного кода.

10 голосов
/ 29 ноября 2009

Что ж, если вы действительно хотите ввести такую ​​функциональность в спецификацию языка C, то для того, чтобы он "сливался" с остальной частью языка, логично было бы расширить понятие " распад на указатель "на структурные типы. Вы сами сделали пример с функцией и указателем на функцию. Причина, по которой он работает таким образом, заключается в том, что тип функции в C затухает до типа указателя во всех контекстах, кроме sizeof и унарных & операторов. (То же самое происходит с массивами, кстати.)

Итак, чтобы реализовать нечто подобное тому, что вы предлагаете, мы могли бы ввести концепцию «распада структуры в указатель», которая работала бы точно так же, как и все другие «распады» в C (а именно, работа распада массива в указатель и распада функции в указатель) работает: когда в выражении используется объект структуры типа T, его тип немедленно затухает до типа T* - указатель на начало объекта структуры - кроме случаев, когда это операнд sizeof или унарный &. Как только такое правило затухания введено для структур, вы можете использовать оператор -> для доступа к элементам структуры независимо от того, есть ли у вас указатель на структуру или сама структура слева. Оператор . станет совершенно ненужным в этом случае (если я что-то упустил), вы всегда будете использовать -> и только ->.

Выше, еще раз, как бы выглядела эта функция, на мой взгляд, если бы она была реализована в духе языка Си.

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

P.S. Очевидным негативным последствием такого правила распада для структур будет то, что помимо нынешней армии новичков, самоотверженно верящих в то, что «массивы - это просто постоянные указатели», у нас будет армия новичков, которые самоотверженно верят, что «структурные объекты - это просто постоянные указатели» , И часто задаваемые вопросы по массиву Криса Торека должны быть примерно в 1,5-2 раза больше, чтобы покрыть структуры:)

8 голосов
/ 29 ноября 2009

Ну, определенно могут быть случаи, когда у вас есть что-то сложное, например:

(*item)->elem

(что я имел в некоторых программах), и если вы написали что-то вроде

item.elem

Имея в виду вышеизложенное, может возникнуть путаница, является ли elem элементом элемента struct, или элементом структуры, на которую указывает элемент, или элементом структуры, которая указывается как элемент в указанном списке элементу итератора и т. д. и т. п.

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

7 голосов
/ 29 ноября 2009

Да, все в порядке, но это совсем не то, что С действительно нужно

Мало того, что это нормально, но это современный стиль. Java и Go просто используют .. Поскольку все, что не помещается в регистр, на некотором уровне является ссылкой, различие между thing и указателем на вещь определенно немного произвольно, по крайней мере, до тех пор, пока вы не доберетесь до функции вызовов.

Первым эволюционным шагом было создание постфикса оператора разыменования, что-то dmr однажды подразумевало, что он в какой-то момент предпочел. Паскаль делает это, поэтому у него есть p^.field. Единственная причина, по которой даже - это оператор ->, заключается в том, что глупо набирать (*p).field или p[0].field.

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

Я утверждал, что использование () для вызовов функций и [] для подписки на массивы некорректно. Почему бы не позволить различным реализациям экспортировать разные абстракции?

Но нет особых причин вносить изменения. Программисты на C вряд ли будут возмущены отсутствием синтаксического сахарного расширения, которое сохраняет один символ в выражении, и его было бы трудно использовать в любом случае, потому что оно не было бы немедленно, если когда-либо повсеместно приниматься. Помните, что когда комитеты по стандартизации становятся мошенниками, они заканчивают тем, что проповедуют в пустые комнаты. Они требуют добровольного сотрудничества и согласия мировых разработчиков компиляторов.

То, что на самом деле нужно C, - это не слишком быстрые способы написания небезопасного кода. Я не против работать в C, но руководителям проектов не нравится, когда их надежность определяется их худший парень, и вполне возможно, что C действительно нужен безопасный диалект, что-то как Cyclone или, возможно, что-то так же, как Go .

5 голосов
/ 29 ноября 2009

Во всяком случае, текущий синтаксис позволяет читателям кода знать, работает ли код с указателем или с реальным объектом. Кто-то, кто не знает код заранее, понимает его лучше.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...