Что такое код fseek


C — feof и заменяющий/эквивалентный код fseek?

Я использовал вышеуказанные функции в своем коде и теперь узнал, что нам не разрешают. Может быть, кто-то может помочь в реализации альтернативной функции? Im разрешено использовать только Fgets, fgetc, fputc, fprintf, sprintf, fscanf, sscanf, rewind, stdin, stdout.

О fseek у меня есть общее представление о том, чтобы получить символы, которые мне нужно пропустить во внутреннюю строку. Правильно ли это?

Я рад, если кто-то может помочь. Большое спасибо!

Я предполагаю, что эти функции запрещены из-за некоторого ограничения назначений, а не технического? Это зависит от того, что вы хотите сделать:

  • feof() предназначена для определения того, находится ли поток в конце файла
  • fseek() предназначен для перехода к определенной позиции в этом файле.

Вы можете определить, находится ли поток в конце файла во время чтения. Например, на странице man для fscanf :

Значение EOF возвращается, если происходит сбой ввода до того, как произойдет любое преобразование, такое как конец файла.

Для перемещения внутри файла без использования fseek() вы можете использовать функцию fseek() rewind() из списка разрешенных. На странице man для rewind() :

Функция rewind() задает индикатор положения файла для потока, на который указывает поток, в начало файла. Это эквивалентно:

Оттуда вы должны будете прочитать до того места, в котором хотите seek() .

Что такое код fseek

(PHP 3, PHP 4, PHP 5)

fseek — Устанавливает смещение в файловом указателе

Описание int fseek ( resource handle, int offset [, int whence] )

Устанавливает смещение в файле, на который ссылается handle . Новое смещение, измеряемое в байтах от начала файла, получается путём прибавления параметра offset к позиции, указанной в параметре whence , значения которого определяются следующим образом:

SEEK_SET — Устанавливает смещение в offset байт.
SEEK_CUR — Устанавливает смещение в текущее плюс offset .
SEEK_END — Устанавливает смещение в размер файла плюс offset . (Чтобы перейти к смещению перед концом файла, вы должны передать отрицательное значение в параметр offset .)

Если whence не указан, по умолчанию он устанавливается в SEEK_SET .

В случае успеха возвращает 0; в противном сучае возвращает -1. Обратите внимание, что переход к смещению за концом файла не считается ошибкой.

Пример 1. Пример использования функции fseek()

= fopen ( ‘somefile.txt’ );

// читаем немного данных
$data = fgets ( $fp , 4096 );

// перемещаемся назад к началу файла
// то же самое, что и rewind($fp);
fseek ( $fp , 0 );

Замечание: Параметр whence был добавлен в версии PHP 4.0.0.

См. также описание функций ftell() и rewind() .

fseek() и произвольный доступ

Можно выполнять операции произвольного чтения и записи, используя систему буферизированного ввода/вывода, с помощью fseek(), устанавливающей текущую файловую позицию. Она имеет следующий прототип:

int fseek(FILE *fp long нисло_байт, int начало);

где fp — это указатель на файл, возвращенный fopen(), число_байт — это длинное целое, содер­жащее число байт от начала до позиции маркера, а начало — это одно из следующих макроопре­делений (определенных в stdio.h):

Макроопределения определены как целочисленные значения, причем SEEK_SET соответствует 0, SEEK_CUR — 1, a SEEK_END — 2. Следовательно, для перехода на число_байт от начала файла следует установить начало в SEEK_SET. Для перехода от текущей позиции надо использо­вать SEEK_CUR, а для перехода от конца файла — SEEK_END. Функция fsee() возвращает 0 в случае удачи или ненулевое значение в случае ошибки.
Например, можно использовать следующий код для чтения 234-го байта файла test:

int func1(void)
<
FILE *fp;
if((fp=fopen(«test», «rb»)) == NULL) <
printf(«Cannot open file.»);
exit(1);
>
fseek(fp, 234L, O);
return getc(fp); /* чтение одного символа в 234-й позиции */
>
>

Другим примером, использующим функцию fseek(), является следующая программа dump, по­зволяющая просмотреть содержимое, как в ASCII, так и в шестнадцатиричных форматах. Можно смотреть на файл 128-байтными «секторами», причем двигаться можно в произвольном направле­нии. Для выхода из программы следует набрать -1, когда будет предложено ввести номер сектора. Обратим внимание на использование fread() для чтения файла. Если остается прочитать меньше чем SIZE байт, то число, передаваемое в display(), как раз и определяет, сколько символов необхо­димо вывести. (Надо помнить, что fread() возвращает число прочитанных элементов.) Введем про­грамму в компьютер и основательно с ней разберемся:

/* dump: простая дисковая утилита для просмотра, использующая fseek. */
#include
#include
#define SIZE 128
void display(int numread);
char buf[SIZE];
void display();
int main(int argc, char *argv[])
<
FILE *fp;
int sector, numread;
if(argc!=2) <
printf(«Usage: dump filename»);
return 1;
>
if((fp=fopen(argv[1], «rb»))==NULL) <
printf(«Cannot open file.»);
return 1;
>
do <
printf(«Enter sector: «);
scanf(«%d», &sector);
if(sector >= 0) <
if(fseek(fp, sector*SIZE, SEEK_SET)) <
printf(«seek error»);
>
if((numread=fread(buf, 1, SIZE, fp)) != SIZE)
printf(«EOF reached.»);
display(numread);
>
> while(sector>=0);
return 0;
>

fseek, _fseeki64 fseek, _fseeki64

Перемещает файловый указатель в указанное местоположение. Moves the file pointer to a specified location.

Синтаксис Syntax

Параметры Parameters

вышестоящий stream
Указатель на структуру FILE. Pointer to FILE structure.

offset offset
Количество байт, начиная с origin. Number of bytes from origin.

origin origin
Первоначальная позиция. Initial position.

Возвращаемое значение Return Value

В случае успеха fseek и _fseeki64 возвращают 0. If successful, fseek and _fseeki64 returns 0. В противном случае возвращается ненулевое значение. Otherwise, it returns a nonzero value. Для устройств, которые не поддерживают поиск, возвращаемое значение не определено. On devices incapable of seeking, the return value is undefined. Если Stream является пустым указателем или если источник не является одним из допустимых значений, описанных ниже, fseek и _fseeki64 вызывают обработчик недопустимого параметра, как описано в разделе Проверка параметров. If stream is a null pointer, or if origin is not one of allowed values described below, fseek and _fseeki64 invoke the invalid parameter handler, as described in Parameter Validation. Если выполнение может быть продолжено, эти функции устанавливают значение еинвал и возвращают-1. If execution is allowed to continue, these functions set errno to EINVAL and return -1.

Примечания Remarks

Функции fseek и _fseeki64 перемещают указатель файла (при его наличии), связанный с потоком , в новое расположение, которое принимает байты из источника. The fseek and _fseeki64 functions moves the file pointer (if any) associated with stream to a new location that is offset bytes from origin. Следующая операция в потоке происходит в новом местоположении. The next operation on the stream takes place at the new location. В потоке, открытом для обновления, следующая операция может быть либо операцией чтения, либо операцией записи. On a stream open for update, the next operation can be either a read or a write. Источник аргумента должен быть одной из следующих констант, определенных в stdio. Высоты The argument origin must be one of the following constants, defined in STDIO.H:

значение происхождения origin value Значение Meaning
SEEK_CUR SEEK_CUR Текущая позиция файлового указателя. Current position of file pointer.
SEEK_END SEEK_END Конец файла. End of file.
SEEK_SET SEEK_SET Начало файла. Beginning of file.

Можно использовать fseek и _fseeki64 для перемещения указателя в любое место в файле. You can use fseek and _fseeki64 to reposition the pointer anywhere in a file. Указатель также может быть размещен за пределами файла. The pointer can also be positioned beyond the end of the file. fseek и _fseeki64 очищают индикатор конца файла и отменяют результат всех предыдущих вызовов ungetc в потоке. fseek and _fseeki64 clears the end-of-file indicator and negates the effect of any prior ungetc calls against stream.

Когда файл открыт для добавления данных, текущая позиция в файле определяется последней операцией ввода-вывода, а не тем, где должна произойти следующая запись. When a file is opened for appending data, the current file position is determined by the last I/O operation, not by where the next write would occur. Если в открытом для добавления файле еще не было ни одной операции ввода-вывода, этой позицией является начало файла. If no I/O operation has yet occurred on a file opened for appending, the file position is the start of the file.

Для потоков, открытых в текстовом режиме, fseek и _fseeki64 имеют ограниченный объем использования, так как переводы перевода строки с возвратом каретки могут привести к созданию непредвиденных результатов для fseek и _fseeki64 . For streams opened in text mode, fseek and _fseeki64 have limited use, because carriage return-line feed translations can cause fseek and _fseeki64 to produce unexpected results. Единственные операции fseek и _fseeki64 , которые гарантированно работают с потоками, открытыми в текстовом режиме,: The only fseek and _fseeki64 operations guaranteed to work on streams opened in text mode are:

поиск со смещением 0 относительно любого из значений origin; Seeking with an offset of 0 relative to any of the origin values.

Поиск с начала файла со значением offset, возвращенным из вызова ftell при использовании fseek или _ftelli64 при использовании _fseeki64. Seeking from the beginning of the file with an offset value returned from a call to ftell when using fseek or _ftelli64 when using _fseeki64.

Кроме того, в текстовом режиме при вводе CTRL+Z интерпретируется как символ конца файла. Also in text mode, CTRL+Z is interpreted as an end-of-file character on input. В файлах, открытых для операций чтения и записи, fopen и все связанные подпрограммы проверяют CTRL + Z в конце файла и удаляют его, если это возможно. In files opened for reading/writing, fopen and all related routines check for a CTRL+Z at the end of the file and remove it if possible. Это делается потому, что использование сочетания fseek и ftell или _fseeki64 и _ftelli64для перемещения внутри файла, заканчивающегося клавишами CTRL + Z, может вызвать неправильное поведение fseek или _fseeki64 в конце File. This is done because using the combination of fseek and ftell or _fseeki64 and _ftelli64, to move within a file that ends with a CTRL+Z may cause fseek or _fseeki64 to behave improperly near the end of the file.


Когда CRT открывает файл, который начинается с метки порядка байтов (BOM), указатель файла помещается после метки (т. е. в начале фактического содержимого файла). When the CRT opens a file that begins with a Byte Order Mark (BOM), the file pointer is positioned after the BOM (that is, at the start of the file’s actual content). Если необходимо fseek в начало файла, используйте ftell , чтобы получить начальное расположение и fseek на него, а не в положении 0. If you have to fseek to the beginning of the file, use ftell to get the initial position and fseek to it rather than to position 0.

Эта функция блокирует работу других потоков во время выполнения, поэтому она потокобезопасна. This function locks out other threads during execution and is therefore thread-safe. Описание неблокирующей версии этой функции см. в разделе _fseek_nolock, _fseeki64_nolock. For a non-locking version, see _fseek_nolock, _fseeki64_nolock.

Требования Requirements

Функция Function Обязательный заголовок Required header
fseek fseek
_fseeki64 _fseeki64

Дополнительные сведения о совместимости см. в разделе Совместимость. For additional compatibility information, see Compatibility.

PHP: Функция fseek()

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

В РНР запись бинарных файлов осуществляется так же, как и запись текстовых файлов (посредством функции fputs ()) и поэтому не требует дополнительных разъяснений. Фактически, единственным отличием (которое уже упоминалось) является использование режима b при открытии файла функцией fopen ().

Поэтому текущий раздел посвящен в основном функциям, связанным с чтением из файла бинарных данных и преобразованием их к виду, используемому в РНР. А именно, мы попробуем написать функцию, которая читает заголовок Zip-файла и определяет минимальный номер версии программы Zip, необходимой для распаковки этого файла. Для этого мы сначала ознакомимся с функциями fseek(),fread( ) и unpack().

При работе с бинарными файлами часто возникает необходимость перехода в различные позиции (или смещения) файла. Этим она отличается от работы с текстовыми файлами, которые, как правило, читаются и пишутся последовательно. В РНР установка указателя файла по определенному смещению внутри открытого файла осуществляется с помощью функции fseek (), которая имеет следующий синтаксис:

fseek($file ref, $offset [, $reference]) ;

где $file_ref — это поток, a $offset указывает число байт смещения от позиции, определяемой третьим параметром Sreference, который принимает значение одной из следующих трех констант.

Константы позиционирования для функции fseek()

SEEK_SET (По умолчанию) начало файла.
SEEK_CUR Текущая позиция файла.
SEEK END Байт, следующий за концом файла.

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

Функция fseek () возвращает нуль в случае успешного завершения и -1 в случае, если указатель файла не может быть установлен. Обратите внимание также на то, что отсчет смещений указателя в fseek() начинается с нуля и, следовательно, это необходимо учитывать при передаче параметра $of f set . Чтобы установить указатель файла в позицию $offset, функции fseek() необходимо передать параметр $offset — 1

Функция fseek()

Не могу понять принцип работы функции fseek в С. Допустим, сначала открываем текстовый файл для чтения: fp=fopen(. ) . Это приводит к тому, что создается указатель на структуру FILE , содержимое полей _base и _prt при этом NULL . Далее пытаемся прочесть несколько символов ch=getc(fp) , . Часть файла заносится в буфер, адрес которого записывается в _base , а позиционирование по файлу осуществляется по указателю _ptr . Казалось, что когда мы хотим получить доступ к определенному байту, fseek просто корректирует значение _ptr . Но как я понял, получается, что fseek просто заново записывает файл в буфер уже начиная с указанного в аргументах места. Или я неправильно понял?

C — feof и заменяющий/эквивалентный код fseek?

Я использовал вышеуказанные функции в своем коде и теперь узнал, что нам не разрешают. Может быть, кто-то может помочь в реализации альтернативной функции? Им разрешено использовать только Fgets, fgetc, fputc, fprintf, Sprintf, fscanf, sscanf, перемотка назад, STDIN, STDOUT .

О fseek у меня есть общее представление о том, чтобы получить символы, которые мне нужно перевести во внутреннюю строку Правильно ли это?

Я рад, если кто-то может помочь. Большое спасибо!

Прочитайте онлайн Язык Си — руководство для начинающих | ПРОИЗВОЛЬНЫЙ ДОСТУП: fseek( )

ПРОИЗВОЛЬНЫЙ ДОСТУП: fseek( )

Функция fseek( ) позволяет нам обрабатывать файл подобно массиву и непосредственно достигать любого определенного байта в файле, открытом функцией fopen( ). Вот простой пример, показывающий, как она работает. Как и в наших предыдущих примерах, функция использует аргумент командной строки для получения имени файла, с которым она работает. Заметим, что fseek() имеет три аргумента и возвращает значение типа int.

/*использование fscek( ) для печати содержимого файла */

main(number, names) /* не следует использовать argc и argv */

long offset = OL; /*обратите внимание, что это тип long */

puts(«Мне нужно имя файла в качестве аргумента.»);

if((fp = fopen(names[1], «r»)) == 0)

printf(» Я не могу открыть %s.\n», names[1]);

while(fseek(fp, offset++, 0) == 0)

Первый из трех аргументов функции fseek( ) является указателем типа FILE на файл, в котором ведется поиск. Файл следует открыть, используя функцию fopen( ).

Второй аргумент назван «offset» (вот почему мы выбрали данное имя для переменной). Этот аргумент сообщает, как далеко следует передвинуться от начальной точки (см. ниже); он должен иметь значение типа long, которое может быть положительным (движение вперед) или отрицательным (движение назад).

Третий аргумент является кодом, определяющим начальную точку:

Код Положение в файле
начало файла
1 текущая позиция
2 конец файла

Функция fseek( ) возвращает , если все хорошо, и -1, если есть ошибка, например попытка перемещаться за границы файла. Теперь мы можем разъяснить наш маленький цикл:

while(fseek(fp, offset++, 0)==0)

Поскольку переменная offset инициализирована нулем, при первом прохождении через цикл мы имеем выражение

означающее, что мы идем в файл, на который ссылается указатель fp, и находим байт, отстоящий на 0 байт от начала, т.е. первый байт. Затем функция putchar( ) печатает содержимое этого байта. При следующем прохождении через цикл переменная offset увеличивается до 1L, и печатается следующий байт. Посуществу, переменная offset действует подобно индексу для элементов файла. Процесс продолжается до тех пор, пока offset нe попытается попасть в fseek( ) после конца файла. В этом случае fseek( ) возвращает значение — 1 и цикл прекращается.

Этот последний пример чисто учебный. Нам нe нужно использовать fseek( ), потому что getc( ) так или иначе проходит через файл байт за байтом; fseek( ) приказала getc( ) «посмотреть» туда, куда она сама уже собиралась посмотреть.

Вот пример (рис. 15.2), в котором выполняется что-то несколько более необычное (Мы благодарим Вильяма Шекспира за этот пример в пьесе «Двенадцатая ночь»).

/* чередование печати в прямом и обратном направлениях */

main(number, names) /* вам не нужно применять argc и argv */

long offset = 0L;

puts(» Мне нужно имя файла в качестве аргумента.»);

if(fp = fopen(names[l], «r»)) == 0)

printf(» Я не могу открыть %s.\n», names[l]);

while(fseek(fp, offset++, 0) == 0)

if(fseek(fp, -(offset + 3), 2) == 0)

РИС. 15.2. Программа, чередующая печать в прямом и обратном направлениях.

Применение этой программы к файлу, содержащему имя «Мальволио», дает такой приятный результат:


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

if(fseek(fp, -(offset + 3), 2) == 0)

Код 2 в операторе предполагает, что мы будем считать позиции от конца файла. Знак минус означает счет в обратном направлении. +3 стоит здесь потому, что мы начинаем с последнего регулярного символа файла и пропускаем несколько символов «новая строка» и EOF в самом конце файла. (Точное значение этой корректировки зависит от типа системы. Наши файлы имеют в конце по два символа новой строки, за которыми следуют два EOF, поэтому мы как раз их и обходим.)

Таким образом, эта часть программы чередует печать в обратном направлении и печать в прямом направлении. Следует заметить, что в некоторых системах может не предусматриваться код 2 для fseek( ).

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

Как непродуманные предупреждения компиляторов помогают портить совершенно правильный код

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

На практике далеко не всегда предупреждения компилятора одинаково полезны. Зачастую они не помогают разработчикам, а мешают им и могут провоцировать на исправление совершенно правильного кода, т.е. на нарушение правила «работает — не трогай».

Для примера будет история об одной строке кода из довольно популярной библиотеки с открытым кодом для работы с XML. Контекст этой строки такой:

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

Выполняется (по сути, а в коде стоит противоположная и возврат кода ошибки) проверка, что filelength строго меньше, чем максимальное значение size_t. Проверка выполняется на «строго меньше», потому что нужно выделить памяти на один элемент больше, чтобы записать завершающий нулевой символ перед дальнейшим разбором содержимого файла.

Все бы хорошо, но компилятор негодуэ — ведь с одной стороны сравнения знаковый тип long, а с другой стороны — беззнаковый size_t. ПРЕДУПРЕЖДЕНИЕ -Wsign-compare от gcc.

Что же делать, куда бежать. Это же предупреждение.

Один из разработчиков исправляет проверку:

… и компилятор УЗБАГОИЛСЯ доволен. Успех? Увы, изначально правильный код теперь работает уже не так, как было задумано. Размеры long и size_t не связаны друг с другом. Размер любого из этих типов может быть больше размера второго на конкретной платформе. Первоначальный код требовал в таком случае преобразования обоих значений к одному и тому же типу — достаточного для обоих размера. В «исправленном» коде произойдет потеря части значащих разрядов в случае, если размер long больше размера size_t.

Так для избавления от предупреждения в изначально правильном коде этот код исправили и сломали.

Ошибка могла бы остаться надолго и еще многие годы радовать пользователей при работе с очень большими файлами на некотором подмножестве систем. Не осталась — код библиотеки открыт и даже лежит на Github, вскоре приходит пул-запрос с правкой этого кода. В исправленном коде проверка выглядит так:

В исправленном коде сравниваемые значения оба беззнаковых типов, поэтому ужасная проблема(тм), о которой gcc сообщал предупреждением -Wsign-compare, здесь отсутствует. Приведение long к unsigned long здесь не приводит к неверной интерпретации данных, потому что единственно возможное отрицательное значение обработано ранее.

Успех? Нет, не так быстро.

Правку вливают в основную ветвь, после чего начинают поступать сообщения о новом предупреждении. При компиляции под платформы, где размер unsigned long меньше размера size_t, clang выдает предупреждение -Wtautological-constant-out-of-range-compare «ПРЕДУПРЕЖДЕНИЕ. Диапазон значений unsigned long так уныл и безнадежно ограничен, что сравнение всегда дает один и тот же результат и не имеет решительно никакого смысла».

Полностью переносимый код для любой платформы, говорите? Нет, когда явное приведение long к size_t могло повлечь потерю разрядов, это всех устраивало. А теперь clang выдает бесполезное предупреждение и пользователи библиотеки оставляют комментарии к правке и сообщения о дефектах. Это же предупреждение.

Что же делать… ведь нужно сделать так, чтобы на платформах, где размер unsigned long меньше размера size_t, сравнения не было, а на остальных платформах — было.

А, вот решение «проблемы»:

— Что это, Бэрримор.
— Это шаблон Баскервилей, сэр.

Здесь можно было бы применить шаблон функции, но значения параметров шаблона по умолчанию для шаблонов функций поддерживаются только начиная с C++11, а библиотека — чтобы расширить аудиторию — старается без него обходиться. struct вместо class используется, чтобы не указывать видимость public явно.

В зависимости от соотношения между размерами long и size_t компилятор выбирает либо специализацию, либо реализацию по умочанию. В первом случае бессмысленной проверки не будет, и компилятору не о чем будет ПРЕДУПРЕЖДАТЬ.

В основную ветвь вливают проверку с использованием этого шаблона — и «проблема» решена.

Краткое содержание предыдущего текста… Совершенно правильный код с одним сравнением целочисленной переменной и целочисленной константы правили трижды, чтобы сделать счастливыми два очень популярных компилятора. Первая же из правок этот совершенно правильный код сломала, зато сделала счастливым компилятор, ради которого вносили правку. В итоге вместо простого сравнения получили что-то ближе к FizzBuzz Enterprise. Зато нет предупреждений.

Теперь давайте порассуждаем…

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

Почему так произошло? Причин ровно две.

Первая причина — разработчики почти безоговорочно доверяют компилятору. Рядом с такой сложной программой многие разработчики ощущают себя как рядом со 107-метровым монументом «Покорителям космоса» на проспекте Мира в Москве. Современный компилятор — это очень сложная программа, и многие считают, что она не может ошибаться. Увы, это мнение неверно — чем программа сложнее, тем больше у нее возможностей ошибиться. Компилятор может выдать предупреждение на совершенно правильный код.

Вторая причина — разработчики статических анализаторов (особенно входящих в состав компиляторов) обычно считают, что ложное срабатывание — это не проблема и ничего страшного, главное ПРЕДУПРЕДИТЬ, а «разработчик должен разобраться». Разберется непременно, но далеко не факт, что в результате код станет лучше.

В обсуждениях этой проблемы проскакивают идеи, что компилятор никак не может разобраться, есть ли в коде проблема, и только «разработчик должен».
Действительно ли компилятор так безнадежно беспомощен и не может «понять» код? Самое время вспомнить об оптимизациях кода. Нетривиальные оптимизации кода — автоматическая векторизация и размотка циклов, вычисление инвариантов, удаление лишних проверок и другие полезные и умные преобразования — требуют от компилятора очень вдумчивого анализа кода.

Насколько вдумчивый анализ нужен в приведенном примере? Проверяемое значение получено от выполнения функции ftell(), ее поведение задокументировано. gcc, например, уже умеет оптимизировать — и «доламывать» не соответствующий Стандарту — код, используя требования Стандарта передавать в «строковые функции» — например, memcpy() — только ненулевые указатели, подробности есть в этой публикации. Как он это делает? Разработчики gcc добавили в него такую возможность — опознавать ряд функций как «строковые» и делать некоторые предположения о значениях передаваемых в эти функции указателей.

Точно так же разработчики компилятора могут добавить в него возможность использовать данные о поведении других функций стандартной библиотеки — вряд ли тривиально, но определенно выполнимо. Сторонние статические анализаторы также «знают» о функциях стандартной библиотеки и популярных сред, это позволяет им сообщать, например, о возможной логической ошибке в приведенном выше коде в случае, когда результат ftell() не проверяется на сообщающее об ошибке значение -1L.

Техническая возможность для значительных улучшений есть. Это вопрос приоритетов и отношения к проблеме.

Можно «научить» компилятор оптимизировать и «доламывать» не соответствующий Стандарту код с вызовом строковых функций, а можно «научить» его выдавать меньше бесполезных предупреждений.

Что на выходе? Многие средства статического анализа пока работают по принципам «главное предупредить» и «разработчик должен», этим они провоцируют разработчиков вносить в код ненужные правки, попутно иногда ломать работающий код. Можно, конечно, объявить этих разработчиков недостойными и криворукими, но не стоит забывать, что от их работы зависит судьба миллионов строк кода, используемых каждый день сотнями миллионов человек.

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

Каждый раз как средство статического анализа выдает предупреждение на правильный код, где-то грустит один котик(тм) и, возможно, в совершенно правильный код вносится ненужная правка, провоцирующая очередную ошибку с последствиями как у Heartbleed.

Дмитрий Мещеряков,
департамент продуктов для разработчиков

Что такое код fseek

int fseek(FILE * stream , long offset , int whence );
long ftell(FILE * stream );
void rewind(FILE * stream );
int fgetpos(FILE * stream , fpos_t * pos );
int fsetpos(FILE * stream , fpos_t * pos );

ОПИСАНИЕ

Функция ftell получает текущее значение позиции в файле для потока, на который указывает stream .

Функция rewind устанавливает позицию в файле для потока, на который указывает stream , равную началу файла. Эта функция эквивалентна вызову

(void)fseek(stream, 0L, SEEK_SET),

хотя в этом случае «очищается» также индикатор ошибок (см. clearerr (3)).

Функции fgetpos и fsetpos равны по значению вызовам ftell и fseek (при этом значение положения (whence) установлено равным SEEK_SET ); они сохраняют (или устанавливают) текущее значение смещения в файле в объектах, определяемых pos , или берут эти значения из данных объектов. В некоторых не-UNIX системах объект fpos_t может быть сложным объектом, а данные функции могут быть единственным способом быстро менять позицию в текстовом потоке.

ВОЗВРАЩАЕМЫЕ ЗНАЧЕНИЯ


НАЙДЕННЫЕ ОШИБКИ

Фукнции fgetpos , fseek , fsetpos и ftell при ошибках устанавливают значение глобальной переменной errno равным значению, определенному в функциях fflush (3), fstat (2), lseek (2) и malloc (3).

Илон Маск рекомендует:  Server side includes основы и приемы использования
Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL