Недавно столкнулась с очень интересным "багом" в Си, который не могу объяснить. Для двух сортировок создается текстовый файл со случайными целыми числами и затем каждый раз для получения исходного неотсортированного массива происходит считывание из него, чтобы заново упорядочить (отсортировать). Вот код программы:
#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). Добавив ее, программа заработала верно!?
Интересно, что же происходит, если мы не закрыли файл? В Паскале, если не закрыть файл, то он просто не запишет последнюю строку. И вроде бы при считывании должны быть нули в массиве - нет данных в файле. Но почему в Си массив не заменяется "нулями", остается неизменным? Как то нелогично.
#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). Добавив ее, программа заработала верно!?
Интересно, что же происходит, если мы не закрыли файл? В Паскале, если не закрыть файл, то он просто не запишет последнюю строку. И вроде бы при считывании должны быть нули в массиве - нет данных в файле. Но почему в Си массив не заменяется "нулями", остается неизменным? Как то нелогично.
Всё вполне логично. Пока файл не закрыт, он может быть вообще не записан на диск - запись идёт блоками по несколько килобайт, а не построчно. Для того, чтобы убедиться в этом, запустите программу с большим n = 10000. В выведенном массиве начало будет правильное, а конец - нет.
ОтветитьУдалитьБуферизация необходима для эффективной записи данных на диск, а программист вообще не должен задумываться как она происходит - чтобы быть уверенным в том, что данные сохранены нужно ОБЯЗАТЕЛЬНО вызвать fclose или fflush.
При чтении же в вашем случае fscanf обнаруживает
конец файла, поэтому возвращает значение -1 и не изменяет значений переменных. Си старается максимально экономить силы - в случае ошибки он предпочитёт вообще ничего не делать, нежели заменять значения нулями.
И еще - таким способом можно проверить размер буфера на компьютере.
УдалитьУдивительно, что кто-то еще просматривает мои сообщения. Значит кому-то это стало интересно.
ОтветитьУдалитьСпасибо за такой лаконичный и полный ответ.