Почему C "fopen" принимает в качестве второго аргумента "const char *"?

Мне всегда казалось странным, что функция C "fopen" принимает в качестве второго аргумента "const char *". Я бы подумал, что было бы проще прочитать ваш код и реализовать библиотечный код, если в stdio.h были бит-маски, такие как "IO_READ" и т.д., Чтобы вы могли делать такие вещи, как:

FILE* myFile = fopen("file.txt", IO_READ | IO_WRITE);

Есть ли программная причина для того, как это происходит на самом деле, или это просто исторический? (т.е. "Это так, как есть".)

EDIT Спасибо за объяснения всех. Правильный ответ, вероятно, относится к тем, которые были даны.

Ответ 1

Одно слово: наследие. К сожалению, мы должны жить с ним.

Просто предположение: возможно, в то время "const char *" выглядело более гибким решением, потому что оно ничем не ограничено. Битовая маска может иметь только 32 разных значения. Похоже на меня YAGNI.

Больше предположений: Dudes были ленивы, а запись "rb" требует меньше ввода, чем MASK_THIS | MASK_THAT:)

Ответ 2

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

  • Kernighan или Ritchie (или тот, кто придумал интерфейс для fopen()), просто мне понравилась идея указать режим, используя строку вместо растрового изображения
  • Возможно, они хотели, чтобы интерфейс был подобным, но заметно отличался от интерфейса системного вызова Unix open(), поэтому он был бы сразу знаком, но не ошибочно скомпилирован с константами, определенными для Unix, а не библиотекой C

Например, скажем, что мифический C-стандарт fopen(), который принял параметр растрового режима, использовал идентификатор OPENMODE_READONLY, чтобы указать, что файл, который сегодня указан в строке режима "r". Теперь, если кто-то сделал следующий вызов программы, скомпилированной на платформе Unix (и что заголовок, который определяет O_RDONLY, был включен):

fopen( "myfile", O_RDONLY);

Не было бы ошибки компилятора, но если OPENMODE_READONLY и O_RDONLY не были определены как один бит, вы получили бы неожиданное поведение. Конечно, было бы разумно, чтобы стандартные имена C были определены так же, как имена Unix, но, возможно, они хотели исключить необходимость такого типа связи.

И опять же, это, возможно, совсем не перешло им в голову...

Ответ 3

Я должен сказать, что я благодарен за это - я знаю, что я должен был набирать "r" вместо IO_OPEN_FLAG_R или был ли он IOFLAG_R или SYSFLAGS_OPEN_RMODE или что-то еще

Ответ 4

У Денниса Ричи есть это, от http://cm.bell-labs.com/cm/cs/who/dmr/chist.html

В частности, Леск написал "портативный I/O package [Lesk 72], который был позже переработаны, чтобы стать стандартом C` Процедуры ввода/вывода

Итак, я говорю, спросите Mike Lesk, опубликуйте результат здесь как ответ на свой вопрос и получите стопки очков за него. Хотя вы, возможно, захотите, чтобы вопрос звучал немного меньше, чем критика; -)

Ответ 5

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

FILE *fp = fopen("/dev/something-weird", "r+:bs=4096");

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

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

Ответ 6

Самая ранняя ссылка на fopen, которую я нашел, находится в первом выпуске Kernighan и Ritchie "Язык программирования C" (K & R1), опубликованный в 1978 году.

В нем показана примерная реализация fopen, которая предположительно является упрощенной версией кода в реализации стандартной библиотеки C времени. Здесь приведена сокращенная версия кода из книги:

FILE *fopen(name, mode)
register char *name, *mode;
{
    /* ... */
    if (*mode != 'r' && *mode != 'w' && *mode != 'a') {
        fprintf(stderr, "illegal mode %s opening %s\n",
            mode, name);
        exit(1);
    }
    /* ... */
}

Рассматривая код, ожидается, что mode будет 1-символьной строкой (no "rb", без различия между текстом и двоичным кодом). Если вы передали более длинную строку, любые символы, прошедшие первый, молча игнорировались. Если вы передали недопустимый mode, функция выведет сообщение об ошибке и прекратит вашу программу, а не вернет нулевой указатель (я предполагаю, что фактическая версия библиотеки этого не сделала). В книге подчеркивается простой код проверки ошибок.

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

Ответ 7

Как говорит Туомас Пелконен, это наследие.

Лично мне интересно, поняли ли некоторые ошибочные соки из-за того, что они лучше из-за меньшего количества символов, напечатанных? В прежние времена время программистов оценивалось более высоко, чем сегодня, поскольку оно было менее доступным, а компиляторы были не такими большими и все такое.

Это только предположение, но я вижу, почему некоторые люди предпочитают сохранять несколько символов здесь и там (обратите внимание на отсутствие вербально в любом из стандартных имен библиотечных функций... Я представляю string.h "strstr" и "strchr", вероятно, лучшие примеры ненужной краткости).