Почему 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", вероятно, лучшие примеры ненужной краткости).