Программа C аварийно завершает работу с Seg Fault, но прекрасно работает с LLDB

Я пишу код чтения Vanilla File.

Большая часть выглядит так.

Во-первых, заголовочный файл file.h

// fheader.h
#ifndef __file_h__
#define __file_h__
// start from here
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void readFile(FILE *fr);

FILE* openFile(char *file_name);
#endif

основной файл fmain.c

#include "fheader.h"


void readFile(FILE *fr) {
    // Added Printf
    printf("\n...\n");
    printf("\n...\n");
    printf("\n...\n");
    printf("\n...\n");
  char *buffer = calloc(sizeof(char),1);
  while(!feof(fr)) {
    fread(buffer,sizeof(char),1,fr);
    printf("%s\n",buffer);
  }
  free(buffer);
  return;
}


FILE* openFile(char *file_name) {
  // printf("the file name that is going to be opened is %s",file_name);
  FILE *fr;
  fr = fopen(file_name,"r");
  return fr;
}

int main(int argc,char *argv[]) {
  if(argc < 2) {
    printf("USAGE: ./file test.txt\n");
    return 1;
  }

  if (argc > 2) {
    printf("ERROR: Too many argument\n");
    return 1;
  }
  FILE *fr;
  char *file_name = calloc(strlen(argv[1]),sizeof(char));

  strncpy(file_name,argv[1],strlen(argv[1]));
  fr = openFile(file_name);
  printf("\nReading from file\n");
  readFile(fr);
  fclose(fr);
  free(file_name);
  return 0;
}

Я скомпилировал код, используя следующую команду

gcc -g3 -Wall fmain.c -o file.o

Когда я запустил код

./file.o "~/workspaces/myWork/C_experiment/test.txt"

я вижу Segmentation fault: 11

Но когда я запускаю вышеуказанную программу в lldb, я работаю и выхожу с кодом возврата 0.

lldb ./file.o
(lldb) run "~/workspaces/myWork/C_experiment/test.txt"
// output of the file 
Process 28806 exited with status = 0 (0x00000000)
(lldb) quit

Теперь я понятия не имею, как отладить код и найти причину Seg Fault.


person Ratatouille    schedule 10.03.2017    source источник
comment
Используйте дамп ядра, чтобы хотя бы увидеть, где он был, когда произошел сбой.   -  person Ruslan    schedule 10.03.2017
comment
Кстати, вы не показали следующую команду, с которой вы скомпилировали код. И расширение имени файла .o заставляет меня сомневаться в правильности этой команды.   -  person Ruslan    schedule 10.03.2017
comment
Вы не проверяете openFile успешно. Если openFile не может открыть файл (например, потому что он не существует), он возвращает NULL, а readfile будет демонстрировать неопределенное поведение (скорее всего, сбой). Скорее всего проблем больше.   -  person Jabberwocky    schedule 10.03.2017
comment
char *buffer = calloc(sizeof(char),1); бессмысленно, если размер буфера известен во время компиляции, пишите просто char buffer[1] (что в любом случае неверно, см. ответ ниже).   -  person Jabberwocky    schedule 10.03.2017
comment
@Ruslan Обновите команду компиляции.   -  person Ratatouille    schedule 10.03.2017
comment
@MichaelWalz Я знаю, что смысл этого делать также в том, чтобы ознакомиться с командой malloc, calloc famliy.   -  person Ratatouille    schedule 10.03.2017
comment
#ifndef __file_h__ идентификаторы, начинающиеся с одного (или двух) символов подчеркивания, зарезервированы. Не используйте их. Просто не надо.   -  person joop    schedule 10.03.2017
comment
@Ruslan Как здесь сделать coredump и Clue?   -  person Ratatouille    schedule 10.03.2017
comment
Зависит от вашей ОС. Для Linux см. этот вопрос, для MacOS X см. этот.   -  person Ruslan    schedule 10.03.2017


Ответы (1)


вы забыли добавить «\ 0» в конце имени файла, и когда вы выполняете fread для чтения 1 символа, вы не добавляете «\ 0» в конце буфера. И если вы читаете только один символ, вы должны использовать char вместо массива 1.

Ошибки вот:

 char *file_name = calloc(strlen(argv[1]),sizeof(char));
  strncpy(file_name,argv[1],strlen(argv[1]));
  fr = openFile(file_name);

Вы копируете argv[1], но не добавляете «\0» в конце. Итак, как fopen узнает, где заканчивается ваша строка? Вы должны добавить +1 к calloc и после strncpy вы должны добавить '\0' следующим образом:

file_name[strlen(argv[1])] = '\0';

Вторая ошибка здесь:

 char *buffer = calloc(sizeof(char),1);
  while(!feof(fr)) {
    fread(buffer,sizeof(char),1,fr);
    printf("%s\n",buffer);
  }

Вы выделяете 1 в свой буфер и читаете 1, это нормально, но когда вы отправляете его в printf, вы не добавляете '\ 0' в свой буфер, поэтому как printf может знать, где остановиться? вы должны вызвать 2, а не один, а затем добавить buffer[1] = '\0';

Итак, с этими исправлениями:

#include "fheader.h"


void readFile(FILE *fr) {
  char buffer;
  while(!feof(fr)) {
    fread(&buffer,sizeof(char),1,fr);
    printf("%c\n",buffer);
  }
  return;
}


FILE* openFile(char *file_name) {
  // printf("the file name that is going to be opened is %s",file_name);
  FILE *fr;
  fr = fopen(file_name,"r");
  return fr;
}

int main(int argc,char *argv[]) {
  if(argc < 2) {
    printf("USAGE: ./file test.txt\n");
    return 1;
  }

  if (argc > 2) {
    printf("ERROR: Too many argument\n");
    return 1;
  }
  FILE * fr = openFile(argv[1]);
  printf("\nReading from file\n");
  readFile(fr);
  fclose(fr);
  return 0;
}
person Gabriel de Grimouard    schedule 10.03.2017
comment
В любом случае, не используйте strncpy, это может привести к тому, что строка назначения останется без терминатора NUL. - person Jabberwocky; 10.03.2017
comment
вы тоже можете это сделать :), но strncpy может быть весьма полезен для объединения строк. - person Gabriel de Grimouard; 10.03.2017
comment
strncpy(file_name,argv[1],strlen(argv[1])); file_name[name_size] = '\0'; можно заменить на strcpy(file_name, argv[1]);. - person Jabberwocky; 10.03.2017
comment
А код между int name_size = strlen(argv[1]); и fr = openFile(file_name); можно заменить на fr = openFile(argv[1]);. Вам не нужно копировать имя файла в argv[1]. - person Jabberwocky; 10.03.2017
comment
@MichaelWalz Я использовал strncpy, потому что думаю, что они снова лучше Buffer Overflow attack http://stackoverflow.com/questions/1258550/why-should-you-use-strncpy-instead-of-strcpy - person Ratatouille; 10.03.2017
comment
@MichaelWalz Не говоря уже о том, что часть таких вещей заключается в ознакомлении с общей практикой написания безопасного кода C. - person Ratatouille; 10.03.2017
comment
strncpy() непригоден для использования. В вашем случае это оставит буфер незавершенным. Также: ваш выделенный буфер для имени файла слишком мал на 1 байт. - person joop; 10.03.2017
comment
@GabrieldeGrimouard Можете ли вы сказать мне, что именно, без изменения кода, в чем проблема в моем коде. Я хочу понять, какое зло я сделал. Я рад принять ваш ответ как решение, но сначала мне нужно понять, что не так в моем коде. даже printf, который я добавил в начале ReadFile, не вызывается - person Ratatouille; 10.03.2017
comment
@joop Верно. Но, как я уже сказал, частью всего этого является ознакомление с общей практикой написания safe кода на C. - person Ratatouille; 10.03.2017
comment
@ Рататуй, ты сделал 2 неправильные вещи. сначала strncpy сделал копию argv[1], но после этого вы не добавили «\ 0». поэтому вы должны добавить strlen(argv[1])+1 к вашему calloc, чтобы добавить '\0' в конце file_name, если вы действительно хотите сделать strncpy. (просто отправьте argv[1] лучше, потому что вы не будете его изменять). Во-вторых, когда вы читаете, вы делаете вызов 1 для буфера, и вы снова не добавляете «\ 0» после того, как читаете символ в конце, поэтому printf не знает, где остановиться. (вам лучше изменить его на символ и отправить адрес, так как вы читаете 1 на 1, а затем делаете printf (% c, буфер)) - person Gabriel de Grimouard; 10.03.2017
comment
@GabrieldeGrimouard Скопируйте и вставьте свою версию кода. Все та же ошибка. - person Ratatouille; 10.03.2017
comment
strcpy здесь совершенно безопасно, потому что вы знаете, что буфер назначения достаточно велик, чтобы вместить строку, которую вы копируете, потому что только что вы выделили нужное количество памяти. - person Jabberwocky; 10.03.2017
comment
@ Рататуй, ты все еще сталкиваешься с ошибкой сегментации? Вы его перекомпилировали? в вашем коде остались ошибки. иначе это может быть ваша система. - person Gabriel de Grimouard; 10.03.2017
comment
@GabrieldeGrimouard Я обнаружил проблему. Кажется, аргумент командной строки не может развернуть home directory syntax, то есть ~/workspaces/, но когда я запускаю его из lldb, мне удается это понять. Как бы странно это ни звучало, я проверяю указатель после openFile, он был Null и errorno и strerror No such file or directory - person Ratatouille; 10.03.2017
comment
@GabrieldeGrimouard Именно так, как я и думал, теперь работает даже моя версия кода. В любом случае, я принимаю ваш ответ, сэр. Так как вы просветили меня в этом процессе. - person Ratatouille; 10.03.2017
comment
@Ratatouille, это хорошо, но вы все равно должны удалить ошибки в своем коде, так как это приведет к некоторому неопределенному поведению и поэтому не будет работать каждый раз :) - person Gabriel de Grimouard; 10.03.2017
comment
@GabrieldeGrimouard Очень хорошо. Спасибо - person Ratatouille; 10.03.2017
comment
@MichaelWalz, вы говорите, что strcpy безопасен в моем случае, потому что я создал буфер точно такого же размера, как argv[1], верно? - person Ratatouille; 10.03.2017
comment
вам нужно malloc(strlen(argv[1])+1) еще один, если вы используете strcpy. так как все еще нужно место для добавления '\ 0', но да, это безопасно. - person Gabriel de Grimouard; 10.03.2017
comment
@Ratatouille да, более или менее, ваш код неверен, потому что вы выделяете недостаточно памяти, вам нужен еще один байт для разделителя строки NUL. Но если вы знаете, что размер целевого буфера достаточен для хранения копируемой вами строки, тогда strlen безопасен. - person Jabberwocky; 11.03.2017