2014-09-24 2 views
0

У меня возникают проблемы с ошибкой сегментации. Когда я запускаю этот код на машине Windows с devC++ или кодовыми блоками, он работает так, как я предполагал. Проблема возникает, когда я пытаюсь работать на Linux-машине. Мне сказали, что это скорее всего из-за указателей, пытающихся получить доступ к местам, где они не разрешены, но я не могу понять, почему.Ошибка сегментации с массивами

Когда я запустил программу без файла «empinfo.txt», она запустится и разрешит мне выполнять любые параметры меню, кроме «1-Add», поэтому проблема, похоже, имеет отношение к способу Я использую массивы с моей структурой.

Я включил весь код, но единственные функции проблемы (я думаю) инициализируются и добавляются. Любая помощь в том, что является правильным способом использования массива символов в этом случае, будет высоко оценена.

Ниже приведен пример ввода, который будет находиться в файле empinfo.txt

12 JackSprat 2  1 65000 
13 HumptyDumpty 5 3 30000 
17 BoPeep 2  3  30000 
20 BoyBlue 3 2  58000 
0 

-

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

FILE *empinfo=NULL; 

struct employeeData { 
    int EMP_ID; 
    char name[20]; 
    int dept; 
    int rank; 
    double salary; 
    struct employeeData *next; 
}; 

struct employeeData *head = NULL; 

void initializeList(); 
void add(int ID, char name[0], int dept, int rank, double newSalary); 
void deleteEmp(int ID); 
void modify(int ID, double NewSalary); 
void query(int rank); 
void print(); 

int main(){ 

    int choice=1, ID=0, rank=0, dept=0; 
    double newSalary=0; 
    char name[20]; 
    initializeList(); 

    while (choice != 0) { 
     printf("1-Add 2-Delete 3-Modify 4-Query 5-Print 0-Exit\n"); 
     fflush(stdin); 
     scanf("%d", &choice); 

     if (choice == 1) { 
      printf("Enter new employee info. Format: \"ID name dept rank salary\"\n"); 
      if((scanf("%d %s %d %d %lf", &ID, name, &dept, &rank, &newSalary))==5) 
       add(ID, name, dept, rank, newSalary); 
      else{ 
       printf("invalid format entered\n\n"); 
       continue; 
      } 
     } 
     else if (choice == 2){ 
      printf("Enter employee ID to delete: "); 
      if(scanf("%d", &ID)==1){ 
       deleteEmp(ID);} 
      else{ 
       printf("Employee ID must be an integer\n"); 
       continue; 
      } 
     } 
     else if (choice == 3){ 
      printf("Enter employee ID to modify: "); 
      if(scanf("%d", &ID)==1){ 
       printf("Enter new salary amount: "); 
       if(scanf("%lf", &newSalary)==1) 
        modify(ID,newSalary); 
       else 
        printf("Salary must be a number\n"); 
      } 
      else{ 
       printf("Employee ID must be an integer\n"); 
       continue; 
      } 
     } 
     else if (choice == 4){ 
      printf("Enter the rank you wish to query: "); 
      scanf("%d", &rank); 
      query(rank); 
     } 
     else if (choice == 5){ 
      print(); 
     } 
    } 
    printf("Goodbye...\n"); 
    head=NULL; 
    free(head); 
    return 0; 
} 

void initializeList(){ 


    empinfo=fopen("empinfo.txt", "r"); 
    head = (struct employeeData *)malloc(sizeof(struct employeeData)); 
    head->next = NULL; 
    if (empinfo==NULL){ 
     printf("empinfo.txt not found, file not opened.\n"); 
     head=NULL; 
     free(head); 
     free(empinfo); 
     return; 
    } 

    struct employeeData *tempPtr = head; 


    while (tempPtr->EMP_ID != 0){ 
     fscanf(empinfo, "%d %s %d %d %lf", &tempPtr->EMP_ID, tempPtr->name, &tempPtr->dept, &tempPtr->rank, &tempPtr->salary); 
     if (tempPtr->EMP_ID == 0){ 
      break; 
      } 
     tempPtr->next = (struct employeeData *)malloc(sizeof(struct employeeData)); 
     tempPtr=tempPtr->next; 
    } 
    tempPtr=head; 
    while(tempPtr->next->EMP_ID!=0){ 
     tempPtr=tempPtr->next; 
    } 
    empinfo=NULL; 
    free(empinfo); 
    tempPtr->next=NULL; 
    fclose(empinfo); 
    tempPtr=NULL; 
    free(tempPtr); 
} 

void add(int ID, char name[], int dept, int rank, double newSalary){ 
    struct employeeData *tempPtr = head; 
    while ((tempPtr->next!=NULL) && (tempPtr->next->EMP_ID < ID)) { 
     tempPtr=tempPtr->next; 
    } 
    struct employeeData *newNode = (struct employeeData *)malloc(sizeof(struct employeeData)); 
    newNode->EMP_ID = ID; 
    strcpy(newNode->name,name); 
    newNode->dept = dept; 
    newNode->rank = rank; 
    newNode->salary = newSalary; 
    newNode->next=NULL; 
    if (tempPtr==head) { 
     if(ID>tempPtr->EMP_ID){ 
      newNode->next = tempPtr->next; 
      tempPtr->next = newNode; 
     } 
     else { 
      newNode->next=tempPtr; 
      head=newNode; 
     } 
    } 
    else if (tempPtr->next == NULL){ 
     tempPtr->next=newNode; 
     newNode->next=NULL; 
    } 
    else{ 
     newNode->next = tempPtr->next; 
     tempPtr->next = newNode; 
    } 
    printf("Employee #%d has been added to the database.\n\n", ID); 
    tempPtr=NULL; 
    newNode=NULL; 
    free(newNode); 
    free(tempPtr); 

} 
void deleteEmp(int ID){ 
    struct employeeData *seek=head,*tempPtr = head; 

    if(head!=NULL) { 
     while((tempPtr->EMP_ID!=ID)){ 
       seek = tempPtr; 
       tempPtr=tempPtr->next; 
       if(tempPtr==NULL){ 
        printf("Employee ID not found\n"); 
        free(tempPtr); 
        seek=NULL; 
        free(seek); 
        return; 
       } 
     } 
     if (tempPtr!=NULL){ 
      if(tempPtr==head){ 
       if(tempPtr->next==NULL){ 
        printf("List cannot be empty\n"); 
        free(tempPtr); 
        seek=NULL; 
        free(seek); 
        return; 
       } 
       head=tempPtr->next; 
       free(tempPtr); 
      } 
      else if (tempPtr->next==NULL){ 
       free(tempPtr); 
       seek->next = NULL; 
      } 
      else{ 
       seek->next=tempPtr->next; 
       free(tempPtr); 
      } 
     } 
    } 
    printf("Employee #%d has been deleted\n", ID); 
    tempPtr=NULL; 
    free(tempPtr); 
    seek=NULL; 
    free(seek); 
} 

void modify(int ID, double NewSalary){ 
    struct employeeData *tempPtr = head; 

    while (tempPtr!=NULL&&tempPtr->EMP_ID!=ID){ 
     tempPtr=tempPtr->next; 
    } 
    if(tempPtr==NULL){ 
     printf("Employee ID not found\n"); 
     free(tempPtr); 
     return; 
    } 
    tempPtr->salary=NewSalary; 
    printf("Employee salary updated.\n\n"); 
    tempPtr=NULL; 
    free(tempPtr); 
} 

void query(int rank){ 
    struct employeeData *tempPtr = head; 

    while (tempPtr!=NULL){ 
     if(tempPtr->rank == rank){ 
      printf("%s\n", tempPtr->name); 
      tempPtr=tempPtr->next; 
     } 
     else 
      tempPtr=tempPtr->next; 
    } 
    tempPtr=NULL; 
    free(tempPtr); 
} 

void print(){ 
    struct employeeData *tempPtr = head; 

    while (tempPtr!=NULL){ 
     printf("%d %s %d %d %.0lf\n", tempPtr->EMP_ID, tempPtr->name, tempPtr->dept, tempPtr->rank, tempPtr->salary); 
     tempPtr=tempPtr->next; 
    } 
    free(tempPtr); 
+0

Продолжения не нужны; код в блоке 'if' перед каждым продолжением автоматически переходит в то же место, что и код в блоке else с продолжением. Однако это не имеет ничего общего с ошибками сегментации. –

+0

В 'initializeList()' было бы проще отложить выделение памяти до тех пор, пока вы не проверите, что вы успешно открыли файл; на пути ошибки будет меньше очистки. –

+0

Хорошо, я сделал это, и он все еще сегментирует ошибки, поэтому он обращается к файлу, который я предполагаю. –

ответ

2

Используйте отладчик. Если вы находитесь в Linux, у вас, вероятно, есть доступ к gdb. Вот сессия с вашим примером и предоставленный тестовый файл:

$ gdb test 
... 
(gdb) run 
... 
Program received signal SIGSEGV, Segmentation fault. 
0x0000000000400bb2 in initializeList() at test.c:111 
111  while(tempPtr->next->EMP_ID!=0){ 
Missing separate debuginfos, use: debuginfo-install glibc-2.18-14.fc20.x86_64 
(gdb) p tempPtr 
$1 = (struct employeeData *) 0x603250 
(gdb) p tempPtr->next 
$2 = (struct employeeData *) 0x0 

Мы можем видеть, что мы пытаемся разыменования NULL указателя на линии 111. Это должно быть легко найти проблему с этой информацией.

Это также обязательно вызвать ошибки:

empinfo=fopen(...) 
... 
empinfo=NULL; 
free(empinfo); 
fclose(empinfo); 

Вы не должны освободить ручку вы получили от fopen и вы не должны установить его в NULL, пусть fclose справиться с этим.

+0

Спасибо за помощь. Я попытаюсь понять это из этой информации. Я не знал, что могу использовать отладчик при подключении к Windows-серверу Windows. благодаря –

0

Добро пожаловать в мир нестандартного поведения! Помимо многих других вещей, UB произойдет, если вы сделаете это:

The value of the object allocated by the malloc function is used (7.20.3.3). 

Узнайте больше в C99 standard, в частности, обратите внимание на приложение J. Это не весело читать, но необходимо, если вы хотите использовать язык C в любом другом контексте, чем играть.

Итак, что случилось с вами, так это то, что на платформе Windows память, возвращаемая malloc(), была явно инициализирована на ненулевое значение, включая tempPtr->EMP_ID. Вот почему он работал там, но поскольку он по-прежнему является неопределенным поведением, он по-прежнему неправ! В Linux память явно установлен на все 0 ведет к while() петле в initializeList() пропуску, в результате разыменования из tempPtr->next, который был установлен в NULL несколько линий до ...

Поскольку ты много вещей неправильно в вашем коде, here're несколько советов:

  • Используйте calloc() вместо malloc(). Это инициализирует память все 0
  • Не cast the result of malloc (сделал это сам в течение долгого времени, но было убеждено в связанном потоке)
  • Проверьте обратный результат всех вызовов функций, в частности, выделение памяти!
  • Установить всех элементов-указателей выделенных структур вручную в NULL
  • Инициализировать все переменные перед их использованием! Это может включать memset() ing массивы
  • Прочитайте документацию по fscanf(). Никогда не использовал его сам, но ваше использование не защищено (переполнение буфера)
  • Всегда читайте документацию по функциям системы и библиотеки, особенно в том, что касается обработки ошибок. В настоящее время вы вообще не обрабатываете ошибки.
  • Убедитесь, что каждое использование free() имеет смысл. Просто положить его везде, где вы думаете, что это может быть мудрым, не является хорошей практикой кодирования :). Использование его для дескрипторов файлов просто неверно.
  • Серьезно Рассматривают другой язык. Печально видеть так много строк кода для такой задачи. C имеет свои сильные стороны, ваш случай вообще не воспроизводит их.
Смежные вопросы