Каковы основные различия между ANSI C и K&R C?

Статья в Википедии о ANSI C гласит:

Одной из целей процесса стандартизации ANSI C было создание расширенного набора K&R C (первого опубликованного стандарта), включающего в себя многие из неофициальных функций, которые были впоследствии представлены. Тем не менее, комитет по стандартам также включил несколько новых функций, таких как прототипы функций (заимствованные из языка программирования C++) и более мощный препроцессор. Синтаксис для объявлений параметров также был изменен, чтобы отразить стиль C++.

Это заставляет меня думать, что есть различия. Однако я не видел сравнения между K & R C и ANSI C. Есть ли такой документ? Если нет, каковы основные различия?

РЕДАКТИРОВАТЬ: Я считаю, что книга K & R говорит "ANSI C" на обложке. По крайней мере, я верю версии, которую я имею дома. Так что, может быть, больше нет разницы?

11 ответов

Решение

Здесь может быть некоторая путаница относительно того, что такое "K&R C". Термин относится к языку, как описано в первом издании "Язык программирования Си". Грубо говоря: язык ввода компилятора Bell Labs C около 1978 года.

Керниган и Ричи были вовлечены в процесс стандартизации ANSI. Диалект "ANSI C" заменил "K&R C", и последующие издания "языка программирования C" принимают соглашения ANSI. "K&R C" является "мертвым языком", за исключением того, что некоторые компиляторы все еще принимают устаревший код.

Прототипы функций были наиболее очевидным изменением между K&R C и C89, но было множество других. Много важной работы ушло и в стандартизации библиотеки Си. Хотя стандартная библиотека C была кодификацией существующей практики, она кодифицировала множество существующих практик, что усложняло ее. Книга PJ Plauger " Стандартная библиотека C"- отличный справочник, а также рассказывает некоторые закулисные подробности того, почему библиотека оказалась такой, какой она была.

Стандарт C ANSI/ISO очень похож на K&R C в большинстве случаев. Предполагалось, что большая часть существующего кода на C должна основываться на компиляторах ANSI без особых изменений. Важно отметить, что семантика языка была открыта для интерпретации каждым поставщиком компиляторов. ANSI C ввел общее описание семантики языка, которая ставит все компиляторы в равное положение. Это легко принять как должное сейчас, спустя 20 лет, но это было значительным достижением.

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

Есть некоторые незначительные различия, но я думаю, что более поздние выпуски K&R предназначены для ANSI C, поэтому больше нет никакой разницы.
"Классика C" из-за отсутствия лучших терминов имела несколько иной способ определения функций, т.е.

int f( p, q, r )  
int p, float q, double r;  
{  
    // Code goes here  
}

Я считаю, что другим отличием были функциональные прототипы. Прототипы не должны были - фактически они не могли - взять список аргументов или типов. В ANSI C они делают.

  1. прототип функции.
  2. постоянные и изменчивые классификаторы.
  3. широкая поддержка персонажей и интернационализация.
  4. разрешить использование указателя на функцию без разыменования.

Другое отличие состоит в том, что типы возвращаемых функций и типы параметров не нужно определять. Предполагается, что они целые.

f(x)
{
    return x + 1;
}

а также

int f(x)
int x;
{
    return x + 1;
}

идентичны

Основные различия между ANSI C и K&R C заключаются в следующем:

  • прототипирование функций
  • поддержка спецификаторов типа const и volatile
  • поддержка широких символов и интернационализация
  • разрешить использование указателей на функции без разыменования

ANSI C принимает метод прототипа функции C++, где определение и объявление функции включают имена функций, типы данных аргументов и типы данных возвращаемых значений. Прототип функции позволяет компилятору ANSI C проверять вызовы функций в пользовательских программах, которые передают недопустимые числа аргументов или типы данных несовместимых аргументов. Это исправляет основные недостатки компилятора K & R.

Пример: to объявляет функцию foo и требует, чтобы foo принимала два аргумента

 unsigned long foo (char* fmt, double data)
 {
      /*body of foo */
 }
  • ПРОТОТИПИРОВАНИЕ ФУНКЦИЙ:ANSI C принимает метод прототипа функции C++, где определение функции и объявление включают имена функций, аргументы t, типы данных и возвращаемые значения. Типы функций. Прототип функции позволяет компиляторам ANSI проверять вызов функции в программе пользователя, которая передает недопустимый номер аргумента. или несовместимые типы данных аргументов. Это исправляет серьезную слабость компиляторов K&R: некорректный вызов в пользовательской программе часто проходит компиляцию, но вызывает сбой программы при их выполнении.

Главное отличие, которое еще никто не упомянул, состоит в том, что до ANSI C определялся в основном прецедентом, а не спецификацией; в тех случаях, когда определенные операции будут иметь предсказуемые последствия на некоторых платформах, но не на других (например, использование реляционных операторов на двух несвязанных указателях), прецедент настоятельно одобряет предоставление программным гарантиям платформ. Например:

  1. На платформах, которые определяют естественное ранжирование среди всех указателей на все объекты, применение реляционных операторов к произвольным указателям может быть использовано для получения этого ранжирования.

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

  3. На платформах, где два или более целочисленных типа имеют один и тот же размер и представление, можно полагаться на указатель на любой такой целочисленный тип для чтения или записи информации любого другого типа с таким же представлением.

  4. На платформах с двумя дополнительными компонентами, где целочисленные переполнения естественным образом переносятся молча, можно полагаться, что операция, включающая в себя значения без знака, меньшие, чем "int", будет вести себя так, как если бы значение было без знака, в тех случаях, когда результат находился бы между INT_MAX+1u и UINT_MAX, и это не был повышен до более крупного типа и не использовался в качестве левого операнда >>ни операнд /, %или любой оператор сравнения. Между прочим, обоснование Стандарта дает это как одну из причин, по которой мелкие неподписанные типы становятся подписанными.

До C89 было неясно, на какие длины компиляторы для платформ, где вышеуказанные предположения естественно не будут иметь места, в любом случае могли бы поддержать эти предположения, но было мало сомнений в том, что компиляторы для платформ, которые могли бы легко и дешево поддержать такие предположения должен сделать это. Авторы Стандарта С89 не удосужились прямо сказать это, потому что:

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

  2. Стандарт только требовал, чтобы реализации были способны запускать одну, возможно, изобретенную программу без переполнения стека, и признал, что хотя тупая реализация может рассматривать любую другую программу как вызывающую Undefined Behavior, но не думала, что стоит беспокоиться о написании тупых писателей компилятора реализации, которые были "соответствующими", но бесполезными.

Хотя "C89" одновременно интерпретировалось как означающее "язык, определенный C89, плюс все дополнительные функции и гарантии, предоставляемые платформой", авторы gcc выдвигают интерпретацию, которая исключает любые функции и гарантии, помимо тех, которые предусмотрены C89.

Разница в том, что:

  1. Прототип
  2. широкая поддержка персонажей и интернационализация
  3. Поддержка постоянных и изменчивых ключевых слов
  4. разрешить использование указателей на функции в качестве разыменования

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

Несмотря на все претензии к контриам, K&R была и вполне способна предоставить любой материал от самого низкого уровня до аппаратного обеспечения. Теперь проблема состоит в том, чтобы найти компилятор (предпочтительно бесплатный), который может дать чистую компиляцию на пару миллионов строк K&R C без необходимости возиться с ним. И работает на чем-то вроде многоядерного процессора AMD.

Насколько я могу судить, взглянув на источник серии GCC 4.xx, нет простого взлома, чтобы повторно активировать функциональность -traditional и -cpp-Traditional lag к их предыдущему рабочему состоянию без большего количества усилий, чем я ожидал. и проще. Построить предварительный компилятор K&R с нуля.

Другие вопросы по тегам