Увидев (и задав!) Столько вопросов, похожих на
Что
int (*f)(int (*a)[5])
значит в С?
и даже видя, что они создали программу, чтобы помочь людям понять синтаксис C, я не могу не удивляться:
Почему синтаксис C был разработан таким образом?
Например, если бы я проектировал указатели, я бы перевел «указатель на массив из 10 элементов указателей» в
int*[10]* p;
и не
int* (*p)[10];
Я думаю, что большинство людей с этим согласятся.
Так что мне интересно, почему неинтуитивный синтаксис? Была ли конкретная проблема, которую решает синтаксис (возможно двусмысленность?), О которой я не знаю?
cdecl
очень удобна для декодирования сложных объявлений языка Си. Есть также веб-интерфейс на cdecl.org .Ответы:
Я понимаю историю этого так, что он основан на двух основных моментах ...
Во-первых, авторы языка предпочли сделать синтаксис ориентированным на переменные, а не на типовые. То есть они хотели, чтобы программист посмотрел на объявление и подумал: «если я напишу выражение
*func(arg)
, это приведет кint
; если я напишу, у*arg[N]
меня будет число с плавающей точкой», а не «func
должен быть указатель на функцию, принимающую это и вернуть это ".Запись C в Википедии утверждает, что:
... ссылаясь на p122 из K & R2, который, увы, мне не нужно искать расширенную цитату для вас.
Во-вторых, на самом деле очень, очень сложно придумать синтаксис для объявления, который будет согласован, когда вы имеете дело с произвольными уровнями косвенности. Ваш пример может хорошо работать для выражения типа, который вы придумали там, но масштабируется ли он до функции, которая берет указатель на массив этих типов и возвращает какой-то другой отвратительный беспорядок? (Может быть, но вы проверили? Можете ли вы доказать это? ).
Помните, что часть успеха C связана с тем фактом, что компиляторы были написаны для многих разных платформ, и поэтому было бы лучше игнорировать некоторую степень читабельности для облегчения написания компиляторов.
Сказав это, я не эксперт по грамматике языка или написанию компилятора. Но я знаю достаточно, чтобы знать, что есть, что знать;)
источник
Многие странности языка C можно объяснить тем, как компьютеры работали при его разработке. Объем памяти хранилища был очень ограничен, поэтому было очень важно минимизировать размер самих файлов исходного кода . Практика программирования еще в 70-х и 80-х годах заключалась в том, чтобы исходный код содержал как можно меньше символов и, желательно, не содержал чрезмерных комментариев к исходному коду.
Сегодня это, конечно, нелепо, ведь на жестких дисках практически неограниченное пространство для хранения. Но это одна из причин, почему C имеет такой странный синтаксис в целом.
Что касается конкретно указателей массива, ваш второй пример должен быть
int (*p)[10];
(да, синтаксис очень запутанный). Возможно, я бы прочитал это как "int указатель на массив из десяти" ... что имеет смысл несколько. Если бы не скобки, компилятор интерпретировал бы его как массив из десяти указателей, что придало бы объявлению совершенно другое значение.Так как указатели на массивы и указатели на функции имеют довольно непонятный синтаксис в C, разумная вещь, которую нужно сделать, - это убрать странности. Возможно так:
Неясный пример:
Непонятный, эквивалентный пример:
Вещи могут стать еще более неясными, если вы имеете дело с массивами указателей на функции. Или самые неясные из них: функции, возвращающие указатели на функции (слегка полезные). Если вы не используете typedef для таких вещей, вы быстро сошли с ума.
источник
Это довольно просто:
int *p
означает, что*p
это int;int a[5]
означает, чтоa[i]
это инт.Значит это
*f
это функция,*a
это массив из пяти целых чисел, такf
же как и функция, которая берет указатель на массив из пяти целых чисел и возвращает int. Однако в Си бесполезно передавать указатель на массив.С заявлениями очень редко это усложняется.
Также вы можете уточнить, используя typedefs:
источник
typedef
,const
,volatile
, а также возможность инициализировать вещи внутри объявлений. Многие из досадных двусмысленностей синтаксиса объявления (например,int const *p, *q;
должны ли они связыватьсяconst
с типом или декларантом) не могли возникнуть в языке, который изначально разрабатывался. Хотелось бы, чтобы язык добавил двоеточие между типом и декларантом, но допускал его пропуск при использовании встроенных типов «зарезервированных слов» без квалификаторов. Значениеint: const *p,*q;
иint const *: p,*q;
было бы ясно.Я думаю, вы должны рассматривать * [] как операторы, которые связаны с переменной. * записывается перед переменной, [] после.
Давайте читать тип выражения
Самым внутренним элементом является переменная p, поэтому
означает: р является переменной.
Перед переменной стоит *, оператор * всегда ставится перед выражением, на которое он ссылается, поэтому
означает: переменная p является указателем. Без () оператор [] справа имел бы более высокий приоритет, т.е.
будет проанализирован как
Следующим шагом является []: поскольку дальнейшее () не существует, [] имеет более высокий приоритет, чем внешний *, поэтому
означает: (переменная p является указателем) на массив. Тогда у нас есть второе *:
означает: ((переменная p является указателем) на массив) указателей
Наконец, у вас есть оператор int (имя типа), который имеет самый низкий приоритет:
означает: (((переменная p - указатель) на массив) указателей) на целое число.
Таким образом, вся система основана на выражениях типов с операторами, и каждый оператор имеет свои собственные правила приоритета. Это позволяет определять очень сложные типы.
источник
Это не так сложно, когда вы начинаете думать, и C никогда не был очень простым языком. И на
int*[10]* p
самом деле не проще, чемint* (*p)[10]
И какой тип К будет вint*[10]* p, k;
источник
pointer to int
иint
даже не одного типа, поэтому они должны быть объявлены отдельно. Период. Послушай мужчину. У него есть 18 тыс. Повторений по причине.