Почему C имеет различие между -> и?


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

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

если вы пиши

s->x

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

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

если вы пишите

ps.x

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

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

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

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

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

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

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

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

7 54

7 ответов:

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

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

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

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

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

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

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

s.c = 99;
f(s);

assert(s.c == 99);

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

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

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

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

p->x = 3;

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

p.x = 3;

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

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

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

(*item)->elem

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

item.elem

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

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

Да, это нормально, но это не то, что действительно нужно C вообще

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

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

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

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

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

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

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