среда, 6 июня 2012 г.

Сортировка и файлы в Си

Недавно столкнулась с очень интересным "багом" в Си, который не могу объяснить. Для двух сортировок создается текстовый файл со случайными целыми числами и затем каждый раз для получения исходного неотсортированного массива происходит считывание из него, чтобы заново упорядочить (отсортировать). Вот код программы:

#include "stdio.h"
#include "math.h"
#include "conio.h"
#include "stdlib.h"
#include "limits.h"
#include "time.h"


FILE *f,*g;

void Rand(int n,int A[])
{
int i;
srand (time(NULL));
f=fopen("input.txt","w");

for(i=0;i<n;i++)
  {

    A[i]=rand()%100;
    fprintf(f,"%d ",A[i]);

   }
}


void Vvod(int n,int A[])
{
  int i;
  f=fopen("input.txt","r");
  for(i=0;i<n;i++)

       fscanf(f,"%d",&A[i]);
}

void Vivod(int A[],int n)
{
 int i;
 for(i=0;i<n;i++)
   {

     fprintf(g,"%d ",A[i]);
    }
fprintf(g,"\n");

}

void Xoar(int A[],int m,int n)
{

int i,j,x,w,s;
i=m;  j=n;
s=(m+n)/2;
x=A[s];
while(i<=j)
{

   while(A[i]<x) i++;
   while(A[j]>x) j--;
   if(i<=j)

   {
     w=A[i]; A[i]=A[j]; A[j]=w; i++;j--; 
   }
}
if (m<j)  Xoar(A,m,j);
if (i<n)  Xoar(A,i,n);

}

void Puzirek(int A[],int n)
{

int i,j,x;
for(i=0;i<n-1;i++)
for(j=0;j<n-1-i;j++)
       if (A[j]>A[j+1]) {x=A[j]; A[j]=A[j+1]; A[j+1]=x;}
}


int main()
{

 int A[10000],n;
 printf("n<=10000:"); scanf("%d",&n);
 if (n>10000) {printf("Error");}


 Rand(n,A);

   g=fopen("out.txt","w");
 Vvod(n,A);//ввод из файла
 Vivod(A,n);//вывод не отсортированного массива
 Puzirek(A,n);//алгоритм сортировки "пузырьком"
 Vivod(A,n);//вывод отсортированного массива

 Vvod(n,A);//ввод из файла
 
Vivod(A,n);//вывод не отсортированного массива (!) Xoar(A,m,n-1);//алгоритм сортировки Хоара
 Vivod(A,n);//вывод отсортированного массива
 
  
 return 0;
}

Красным цветом отметила место в программе, где выдается массив отсортированный, т.е. как будто и не считывали массив с файла. Что делать в этом случае?
Путем проб и отладочных мер, проблему так и не решили. Не понятно, почему при считывании из файла, используя функцию fscanf(f, "%d", &A[i]), ничего не происходит и массив остается прежним, отсортированным.
Заметили, что при создании файла опустили функцию fclose(f). Добавив ее, программа заработала верно!?

Интересно, что же происходит, если мы не закрыли файл? В Паскале, если не закрыть файл, то он просто не запишет последнюю строку. И вроде бы при считывании должны быть нули в массиве - нет данных в файле. Но почему в Си массив не заменяется "нулями", остается неизменным? Как то нелогично.

3 комментария:

  1. Всё вполне логично. Пока файл не закрыт, он может быть вообще не записан на диск - запись идёт блоками по несколько килобайт, а не построчно. Для того, чтобы убедиться в этом, запустите программу с большим n = 10000. В выведенном массиве начало будет правильное, а конец - нет.
    Буферизация необходима для эффективной записи данных на диск, а программист вообще не должен задумываться как она происходит - чтобы быть уверенным в том, что данные сохранены нужно ОБЯЗАТЕЛЬНО вызвать fclose или fflush.
    При чтении же в вашем случае fscanf обнаруживает
    конец файла, поэтому возвращает значение -1 и не изменяет значений переменных. Си старается максимально экономить силы - в случае ошибки он предпочитёт вообще ничего не делать, нежели заменять значения нулями.

    ОтветитьУдалить
    Ответы
    1. И еще - таким способом можно проверить размер буфера на компьютере.

      Удалить
  2. Удивительно, что кто-то еще просматривает мои сообщения. Значит кому-то это стало интересно.
    Спасибо за такой лаконичный и полный ответ.

    ОтветитьУдалить