Я пытаюсь отладить фрагмент кода, который читает созданные мной двоичные файлы. Кажется, что когда количество считываемых элементов превышает определенный порог, написанный мной сценарий дает сбой.
Файл содержит следующую структуру: для каждого времени t он имеет время t, затем структуру N частиц (каждая из которых содержит некоторую информацию об этой частице, положении, скорости и т. д., вероятно, не имеет значения).
Файл создается с помощью
int update_binary_file(const char *name, particle *P, parameters par, double *time)
{
FILE *file;
file = fopen(name, "a+");
if (!file)
{
return 1;
}
fwrite(time, sizeof(double), 1, file);
fwrite(P, sizeof(particle), par.N, file);
fclose(file);
return 0;
}
Где частица — это структура, определенная следующим образом:
typedef struct
{
double x, y, z; // position
double vx, vy, vz; // velocity
double wx, wy, wz; // acceleration
int next_inbox;
} particle;
Параметры — это еще одна структура, но она здесь не имеет значения. Я использую ее только для передачи значения par.N (количества частиц в системе).
Наконец я прочитал файл следующим образом:
void read_binaryfile(parameters par, char *filename, double *x, double *y, double *z, double *vx, double *vy, double *vz, double *wx, double *wy, double *wz, double *t)
{
int dump;
FILE *file;
file = fopen(filename, "rb");
if (!file)
{
printf("ERROR: invalid file %s\n", filename);
return;
}
else
{
for (int line = 0; line < par.Nsnap; line++)
{
if (fread(&t[line], 1, sizeof(double), file) != sizeof(double))
break;
printf("%f\n", t[line]);
for(int n = 0; n < par.N; n++){
int lin_index = linear_index(n, line);
if (fread(&x[lin_index], 1, sizeof(double), file) != sizeof(double)){
printf("ERROR reading file!\n");
break;
}
if (fread(&y[lin_index], 1, sizeof(double), file) != sizeof(double))
{
printf("ERROR reading file!\n");
break;
}
if (fread(&z[lin_index], 1, sizeof(double), file) != sizeof(double))
{
printf("ERROR reading file!\n");
break;
}
if (fread(&vx[lin_index], 1, sizeof(double), file) != sizeof(double))
{
printf("ERROR reading file!\n");
break;
}
if (fread(&vy[lin_index], 1, sizeof(double), file) != sizeof(double))
{
printf("ERROR reading file!\n");
break;
}
if (fread(&vz[lin_index], 1, sizeof(double), file) != sizeof(double))
{
printf("ERROR reading file!\n");
break;
}
if (fread(&wx[lin_index], 1, sizeof(double), file) != sizeof(double))
{
printf("ERROR reading file!\n");
break;
}
if (fread(&wy[lin_index], 1, sizeof(double), file) != sizeof(double))
{
printf("ERROR reading file!\n");
break;
}
if (fread(&wz[lin_index], 1, sizeof(double), file) != sizeof(double))
{
printf("ERROR reading file!\n");
break;
}
if (fread(&dump, 1, sizeof(int), file) != sizeof(int))
{
printf("ERROR reading file!\n");
break;
}
if (fread(&dump, 1, sizeof(int), file) != sizeof(int))
{
printf("ERROR reading file!\n");
break;
}
}
}
}
fclose(file);
}
По сути, он считывает все значения структуры частиц одно за другим, вместо чтения самой структуры (потому что это более практично). Скрипт хорошо работает с небольшими файлами, и я могу читать их с помощью Python. Когда общее количество элементов в файле больше, я получаю ошибку сегментации. Более конкретно в GDB:
Program received signal SIGSEGV, Segmentation fault.
__memmove_avx_unaligned_erms () at ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S:418
418 ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S: No such file or directory.
(gdb) where
#0 __memmove_avx_unaligned_erms () at ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S:418
#1 0x00007ffff788b423 in __GI__IO_file_xsgetn (fp=0x55555556cea0, data=<optimized out>, n=8) at ./libio/fileops.c:1295
#2 0x00007ffff787fba9 in __GI__IO_fread (buf=0x7ffdf7ad6af8, size=1, count=8, fp=0x55555556cea0) at ./libio/iofread.c:38
#3 0x00005555555559ae in read_binaryfile (par=...,
filename=0x7fffffffe127 "snap/m0.68_J18.00_g80.00_n1.00_T7.60_r1.00_v2.00/rho4.0_L4_N256_dt0.010_correlations_ICrand_dtsave0.05_t10.00.bin", x=0x7ffff7e37010, y=0x7ffff7b9d010, z=0x7ffff7b3a010, vx=0x7ffff7ad7010, vy=0x7ffff7a74010,
vz=0x7ffff779d010, wx=0x7ffff773a010, wy=0x7ffff76d7010, wz=0x7ffff7674010, t=0x55555556beb0)
at spacetime_correlations.c:151
#4 0x000055555555602a in main (argc=2, argv=0x7fffffffdd28) at spacetime_correlations.c:226
Все массивы x,y,z,vx,vy,vz,wx,wy,wz определяются как:
double *x = (double *)calloc(par.N * par.Nsnap, sizeof(double));
и мне кажется, что я никогда не превышаю их максимальную размерность, присваивая им значения, считанные из файла с помощью fread(&vz[lin_index] и т. д...).
Я не знаю, откуда возникла проблема, обычно ошибка сегментации означает проблему в памяти, но здесь с индексами мне кажется все в порядке.
ОБНОВЛЕНИЕ 1. Минимальный код для создания выходного файла с именем N100.bin.
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
typedef struct
{
double x, y, z; // position
double vx, vy, vz; // velocity
double wx, wy, wz; // acceleration
int next_inbox;
} particle;
int update_binary_file(const char *name, particle *P, int N, double *time)
{
FILE *file;
file = fopen(name, "a+");
if (!file)
{
return 1;
}
fwrite(time, sizeof(double), 1, file);
fwrite(P, sizeof(particle), N, file);
fclose(file);
return 0;
}
int main(int argc, char *argv[])
{
int N = 100;
particle *P = (particle *)calloc(N, sizeof(particle));
int Nsteps = 1000;
for (int i = 0; i < N; i++)
{
P[i].x = 1;
P[i].y = 1;
P[i].z = 1;
P[i].vx = 1;
P[i].vy = 1;
P[i].vz = 1;
P[i].wx = 1;
P[i].wy = 1;
P[i].wz = 1;
P[i].next_inbox = 1;
}
double time;
for (int i = 0; i < Nsteps; i++)
{
time = i*0.1;
update_binary_file("N100.bin", P, N, &time);
}
}
Минимальный код для чтения созданного файла N100.bin.
// gcc spacetime_correlations.c -o spacetime_correlations -lm -O3
#include <stdio.h>
#include <stdlib.h>
#define linear_index(n, t) ((n) + (t) * par.N )
typedef struct
{
int N, Nsnap, L;
} parameters;
typedef struct
{
double x, y, z; // position
double vx, vy, vz; // velocity
double wx, wy, wz; // acceleration
int next_inbox;
} particle;
void read_binaryfile(parameters par, char *filename, double *x, double *y, double *z, double *vx, double *vy, double *vz, double *wx, double *wy, double *wz, double *t)
{
int dump;
FILE *file;
file = fopen(filename, "rb");
if (!file)
{
printf("ERROR: invalid file %s\n", filename);
return;
}
else
{
for (int line = 0; line < par.Nsnap; line++)
{
if (fread(&t[line], 1, sizeof(double), file) != sizeof(double))
break;
printf("%f\n", t[line]);
for(int n = 0; n < par.N; n++){
int lin_index = linear_index(n, line);
if (fread(&x[lin_index], 1, sizeof(double), file) != sizeof(double)){
printf("ERROR reading file!\n");
break;
}
if (fread(&y[lin_index], 1, sizeof(double), file) != sizeof(double))
{
printf("ERROR reading file!\n");
break;
}
if (fread(&z[lin_index], 1, sizeof(double), file) != sizeof(double))
{
printf("ERROR reading file!\n");
break;
}
if (fread(&vx[lin_index], 1, sizeof(double), file) != sizeof(double))
{
printf("ERROR reading file!\n");
break;
}
if (fread(&vy[lin_index], 1, sizeof(double), file) != sizeof(double))
{
printf("ERROR reading file!\n");
break;
}
if (fread(&vz[lin_index], 1, sizeof(double), file) != sizeof(double))
{
printf("ERROR reading file!\n");
break;
}
if (fread(&wx[lin_index], 1, sizeof(double), file) != sizeof(double))
{
printf("ERROR reading file!\n");
break;
}
if (fread(&wy[lin_index], 1, sizeof(double), file) != sizeof(double))
{
printf("ERROR reading file!\n");
break;
}
if (fread(&wz[lin_index], 1, sizeof(double), file) != sizeof(double))
{
printf("ERROR reading file!\n");
break;
}
if (fread(&dump, 1, sizeof(int), file) != sizeof(int))
{
printf("ERROR reading file!\n");
break;
}
if (fread(&dump, 1, sizeof(int), file) != sizeof(int))
{
printf("ERROR reading file!\n");
break;
}
}
}
}
fclose(file);
}
int main(int argc, char **argv){
// Provide filename from command line
char *filename = argv[1], *dump;
parameters par;
double *time = (double *)calloc(par.Nsnap, sizeof(double));
double *C_t = (double *)calloc(par.Nsnap, sizeof(double));
double *Vx = (double *)calloc(par.Nsnap, sizeof(double));
double *Vy = (double *)calloc(par.Nsnap, sizeof(double));
double *Vz = (double *)calloc(par.Nsnap, sizeof(double));
double *x = (double *)calloc(par.N * par.Nsnap, sizeof(double));
double *y = (double *)calloc(par.N * par.Nsnap, sizeof(double));
double *z = (double *)calloc(par.N * par.Nsnap, sizeof(double));
double *vx = (double *)calloc(par.N * par.Nsnap, sizeof(double));
double *vy = (double *)calloc(par.N * par.Nsnap, sizeof(double));
double *vz = (double *)calloc(par.N * par.Nsnap, sizeof(double));
double *wx = (double *)calloc(par.N * par.Nsnap, sizeof(double));
double *wy = (double *)calloc(par.N * par.Nsnap, sizeof(double));
double *wz = (double *)calloc(par.N * par.Nsnap, sizeof(double));
double *delta_vx = (double *)calloc(par.N * par.Nsnap, sizeof(double));
double *delta_vy = (double *)calloc(par.N * par.Nsnap, sizeof(double));
double *delta_vz = (double *)calloc(par.N * par.Nsnap, sizeof(double));
// Count number of "lines" (e.g. the number of particles arrays at different time)
int count = 0;
FILE *file;
file = fopen(filename, "rb");
while(fgetc(file) != EOF)
{
count++;
}
fclose(file);
// Get number of particles from file name
sscanf(filename,"N%d", &par.N);
par.Nsnap = count/(sizeof(particle)*par.N+sizeof(double));
printf("%d\n", par.N);
read_binaryfile(par, filename, x, y, z, vx, vy, vz, wx, wy, wz, time);
free(time);
free(C_t);
free(Vx);
free(Vy);
free(Vz);
free(x);
free(y);
free(z);
free(vx);
free(vy);
free(vz);
free(wx);
free(wy);
free(wz);
free(delta_vx);
free(delta_vy);
free(delta_vz);
return 1;
}
Обратите внимание: если я изменю Nsteps в первом скрипте на меньшее значение, то при использовании второго скрипта у меня не будет ошибки сегментации.
Параметры все время инициализируются одинаково? Всегда ли par.N
и par.Nsnap
одинаковы при записи и чтении файла? Когда вы предоставляете MRE, запрошенный Эриком Постпишилом, пожалуйста, также включите некоторые тестовые данные, которые вы используете для создания файла.
да, пар.Н один и тот же и известен. par.Nsnap — это просто количество «строк» двоичного файла. Как мне добавить файлы к вопросу?
Вы можете добавить пример кода, который использует update_binary_file
для создания файла.
Как узнать, сколько «строк» хранится в файле до его открытия? Почему вы используете структуру при записи файла, но отдельные значения для чтения? При написании структуры могут присутствовать некоторые байты заполнения, о которых нужно будет позаботиться при чтении отдельных значений.
@Gerhardh Думаю, именно поэтому в конце есть два вызова fread
размера sizeof(int)
. Последний будет для заполнения. Но это все довольно непереносимо.
@IanAbbott Ты прав. Это могло бы исправить ситуацию, но, как вы упомянули, портативность вышла из-под контроля...
Да, это действительно не очень умно. Было бы лучше читать как структуру (я собирался изменить ее на следующем этапе отладки). В любом случае, я не думаю, что это проблема. Про переносимость я всегда писал только для себя, поэтому мне никогда не приходилось об этом думать; Извини за это!
В «spacetime_correlations.c» main()
, par
не инициализируется перед использованием, что приводит к UB.
Вы должны выделить эти буферы после заполнения par
, чтобы par.N
и par.Nsnap
были допустимы в вычислениях для установки размера буферов. Я не знаю, есть ли в вашем реальном коде та же проблема, что и в этом минимально воспроизводимом примере.
Разве ваш компилятор не выдал никаких предупреждений об использовании неинициализированной переменной par
? Если нет, вам необходимо повысить уровень предупреждения. Для gcc используйте -Wall -Wextra -pedantic
.
@Gerhardh Действительно, мой компилятор не жаловался... Спасибо за ценное предложение! Будет следовать :)
Размеры буфера зависят от неинициализированной переменной, что приводит к неопределенному поведению.
parameters par;
double *time = (double *)calloc(par.Nsnap, sizeof(double));
double *C_t = (double *)calloc(par.Nsnap, sizeof(double));
double *Vx = (double *)calloc(par.Nsnap, sizeof(double));
double *Vy = (double *)calloc(par.Nsnap, sizeof(double));
double *Vz = (double *)calloc(par.Nsnap, sizeof(double));
double *x = (double *)calloc(par.N * par.Nsnap, sizeof(double));
double *y = (double *)calloc(par.N * par.Nsnap, sizeof(double));
double *z = (double *)calloc(par.N * par.Nsnap, sizeof(double));
double *vx = (double *)calloc(par.N * par.Nsnap, sizeof(double));
double *vy = (double *)calloc(par.N * par.Nsnap, sizeof(double));
double *vz = (double *)calloc(par.N * par.Nsnap, sizeof(double));
double *wx = (double *)calloc(par.N * par.Nsnap, sizeof(double));
double *wy = (double *)calloc(par.N * par.Nsnap, sizeof(double));
double *wz = (double *)calloc(par.N * par.Nsnap, sizeof(double));
double *delta_vx = (double *)calloc(par.N * par.Nsnap, sizeof(double));
double *delta_vy = (double *)calloc(par.N * par.Nsnap, sizeof(double));
double *delta_vz = (double *)calloc(par.N * par.Nsnap, sizeof(double));
Переместите эти выделения буфера в точку, где par.N
и par.Nsnap
действительны:
// Get number of particles from file name
sscanf(filename,"N%d", &par.N);
par.Nsnap = count/(sizeof(particle)*par.N+sizeof(double));
printf("%d\n", par.N);
double *time = (double *)calloc(par.Nsnap, sizeof(double));
double *C_t = (double *)calloc(par.Nsnap, sizeof(double));
double *Vx = (double *)calloc(par.Nsnap, sizeof(double));
double *Vy = (double *)calloc(par.Nsnap, sizeof(double));
double *Vz = (double *)calloc(par.Nsnap, sizeof(double));
double *x = (double *)calloc(par.N * par.Nsnap, sizeof(double));
double *y = (double *)calloc(par.N * par.Nsnap, sizeof(double));
double *z = (double *)calloc(par.N * par.Nsnap, sizeof(double));
double *vx = (double *)calloc(par.N * par.Nsnap, sizeof(double));
double *vy = (double *)calloc(par.N * par.Nsnap, sizeof(double));
double *vz = (double *)calloc(par.N * par.Nsnap, sizeof(double));
double *wx = (double *)calloc(par.N * par.Nsnap, sizeof(double));
double *wy = (double *)calloc(par.N * par.Nsnap, sizeof(double));
double *wz = (double *)calloc(par.N * par.Nsnap, sizeof(double));
double *delta_vx = (double *)calloc(par.N * par.Nsnap, sizeof(double));
double *delta_vy = (double *)calloc(par.N * par.Nsnap, sizeof(double));
double *delta_vz = (double *)calloc(par.N * par.Nsnap, sizeof(double));
read_binaryfile(par, filename, x, y, z, vx, vy, vz, wx, wy, wz, time);
О боже, такая глупая ошибка! Не могу поверить, что не заметил этого :( В любом случае спасибо, а также спасибо @Gerhardh, я введу более строгий уровень предупреждений!
Отредактируйте вопрос, чтобы предоставить минимально воспроизводимый пример.