2016-06-17 2 views
3

Я пытаюсь написать простой код С, который подсчитывает, сколько раз байт повторяется в файле. Мы попробовали код с файлами .txt и творят чудеса (максимальный размер: 137 МБ). Но когда мы попробовали его с изображением (даже маленьким, 2 КБ), он вернул Ошибка сегментации 11.Ошибка сегментации 11 при попытке чтения байта изображения за байт

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

Это код:

#include <stdio.h> 
#include <stdlib.h> 

int main(int argc, char **argv) { 

    FILE *f; 
    char *file; 
    long numTotalBytes = 0; 
    int bytesCount[256] = {0}; 

    f = fopen (argv[1], "rb"); 
    fseek(f, 0L, SEEK_END); 
    numTotalBytes = ftell(f); 
    rewind(f); 

    file = calloc(1, numTotalBytes);  
    fread(file, numTotalBytes, 1, f); 
    fclose(f); 

     printf("numTotalBytes: %ld", numTotalBytes); //<- this gives the right output even for images 

    unsigned int i; 
    for (i=0; i<numTotalBytes; ++i) { 
     unsigned char pointer = file[i]; //<- This access fails at file[1099] 
     int pointer_int = (int)pointer; 
     printf("iteration %i with pointer at %i\n", i, pointer_int); //<- pointer_int is never below 0 or above 255 
     //++bytesCount[(int)file[i]]; 
     ++bytesCount[pointer_int]; 
    } 

    free(file); 
} 

Некоторые дополнительные сведения:
- Изменение расширение IMG, чтобы .txt не работает.
- Код возвращает ошибку сегментации точно на итерации 1099 (файл, который я использую, равен aprx 163 КБ, поэтому файл [i] должен принимать обращения до aprox-файла [163000]).
- Для файлов txt работает отлично. Читает байты один за другим и считает их ожидаемыми, независимо от размера файла.
- Я на Mac (вы никогда не знаете ...)

// EDIT: Я редактировал код более desglosed и пояснительная один, потому что некоторые из вас, где говорит мне, что я уже пробовал ,

// EDIT_2: Хорошо, ребята, неважно. Эта версия должна работать на любом другом компьютере, чтобы она не моя. Я думаю, проблема связана с моим терминалом при передаче аргументов, но я просто переключил ОС и работает.

+1

расширение действительно ничего не значит ... –

+0

Попробуйте изменить '(int)' to '(unsigned)'. Вам не нужны отрицательные индексы. – alk

+0

'fseek()' и 'ftell()' не является хорошим способом рассчитать размер файлов. [FIO19-С. Не используйте fseek() и ftell() для вычисления размера обычного файла - CERT C Coding Standard - Стандарты безопасного кодирования CERT] (https://www.securecoding.cert.org/confluence/display/c/FIO19- C. + Do + not + use + fseek() + и + ftell() + to + compute + + + + + + + + + файл) – MikeCAT

ответ

4
  • Проверьте, выполнены ли fopen() и calloc().
  • Спецификатор формата для печати long: %ld, а не %lu.
  • (int)file[i] плохо для индекса массива, поскольку преобразование char в int сохранит свое значение, если все значения, которые могут быть представлены в виде char представимы в int, и потому, что если char подписан в вашей среде (и настройке), он может получить доступ к отрицательному индекс, вызывать доступ за пределами диапазона и вызывать не определено поведение.

Вы должны изменить ++bytesCount[(int)file[i]]; на номер ++bytesCount[(unsigned char)file[i]];, чтобы предотвратить использование отрицательного индекса.

отметить также, что ftell() с SEEK_END можно отметить поддерживаться для двоичного потока (N1570 7.21.9.2 FSEEK функции), так что лучше читать один за одним, используя fgetc() для того, чтобы избежать undefined behavior и использовать меньше памяти ,

+0

- В исходном коде есть проверки для fopen и calloc, я просто поставил здесь меньшую версию. - Изменено% lu до% ld (спасибо!). - Доступ к bytesCount работает хорошо, я сделал много отладки, разделил строку кода на две части, с большим количеством отпечатков, отбрасывает int, unsigned int, unsigned char и т. Д. Иногда они печатают отрицательные числа, если я не умею используйте unsigned (как и ожидалось), но доступ к работе в любом случае. Это вход в файл [i], вызывающий ошибку сегментации. Другие никогда не получают доступа к чему-либо ниже 0 или более 255. Я могу поместить снятый код, если это поможет. – p4x

0

Это может быть вызвано этой линии

++bytesCount[(int)file[i]]; 

bytesCount является массив 256 целых чисел. Если file[i] - более 256, вы получаете доступ к недопустимой памяти, что может привести к ошибке сегментации.

+0

Отредактировано оригинальное сообщение для лучшего объяснения. Его не bytesCount [index] тот, у кого нет, но доступ к файлу [index], вызывающий ошибку сегментации. – p4x

1

MikeCAT просто избил меня.Ниже приводится немного больше объяснений, если это поможет.

Исправить: изменить file на unsigned char *file и приращение до ++bytesCount[file[i]];.

Exaplanation: за this answer, простой char может быть signed или unsigned. В этом случае я предполагаю, что он по умолчанию равен signed. Это означает, что любое значение >=0x80 станет отрицательным числом. Такие значения вряд ли будут в вашем текстовом файле на английском языке, но, скорее всего, будут в изображении! Привод типа (int) будет отрицательным отрицательным. Поэтому код будет индексировать byteCounts с отрицательным числом, что приведет к ошибке сегментации.

+0

'0x80' также может быть преобразовано в отрицательное число. – MikeCAT

+0

@MikeCAT thanks - fixed typo – cxw

+0

Отредактировано оригинальное сообщение для лучшего объяснения. Его не bytesCount [index] тот, у кого нет, но доступ к файлу [index], вызывающий ошибку сегментации. – p4x

Смежные вопросы