Чтение - динамическая память

Привет, ребята! Помогите, пожалуйста, считать структуры из файла, выделяя блок динамической памяти для хранения прочитанных данных. Буду очень признателен)

int load (struct zvezda *p, int max)
{
	FILE *file;
	char fname[20];
	int i;
	
	printf("Укажите имя файла и путь к нему: ");
	scanf ("%s", fname);
	file=fopen(fname, "rb");
	if (file == NULL)
	{
		printf("Возникла ошибка!");
		return(0);
	}
	
	max = fread(p, sizeof(*p), max, file);

        fclose(file);
        return max;
}

Так а сейчас что не получается?

Скобки не нужны.

20 что-то мало на путь и имя )

Ребята, пожалуйста, помогите использовать динамическую память при создании массива данных в case 1!

int main()
{
    struct zvezda A[50];
    int number, n=0, i, k, d=0;
    double zv;
 
    system("chcp 1251 > nul");
    
    while (d<2) {
        
    system("cls");
    
     printf("___________________________________________________________________\n"
            "********************|-----------MENU----------|********************\n"
            "*******************************************************************\n"
            "*   1. Добавить запись                                            *\n"
            "*   2. Вставить запись                                            *\n"
            "*   3. Удалить запись                                             *\n"
            "*   4. Просмотреть запись                                         *\n"
            "*        41 - по указанному номеру                                *\n"
            "*        42 - по видимой величине                                 *\n"
            "*        43 - все записи                                          *\n"
            "*   5. Сортировать записи (по возрастанию расстояния до Земли)    *\n"
            "*   6. Сохранить записи в файл                                    *\n"
            "*   7. Загрузить записи из файла                                  *\n"
            "*   ???                                                           *\n"
            "*   9. Выйти из меню                                              *\n"
            "*******************************************************************\n>> ");
 
    fflush(stdin);
    scanf("%d", &number);
    
    switch (number) {
        
    case 1:
        
        if (n < 50)
        {
          printf("Запись №%d:\n", n+1);
          vvod(&A[n]);
          n++;
        }
        else
          printf("\aНет памяти!\n");
        system("pause");
        break; 

Например, при сохранении записать в начало файла количество записанных объектов.
Потом при чтении читать это количество, выделять нужный размер памяти через malloc, ну и дальше можно fread как в первом сообщении.

Не… Пока интересует только по данному отрывку добавление элемента. Помоги, пожалуйста, эту строчку правильно составить.

int main()
{
    struct zvezda * A;
    int number, n=0, i, k, d=0;
    double zv;
 
    system("chcp 1251 > nul");
    
    while (d<2) {
        
    system("cls");
    
     printf("___________________________________________________________________\n"
            "********************|-----------MENU----------|********************\n"
            "*******************************************************************\n"
            "*   1. Добавить запись                                            *\n"
            "*   2. Вставить запись                                            *\n"
            "*   3. Удалить запись                                             *\n"
            "*   4. Просмотреть запись                                         *\n"
            "*        41 - по указанному номеру                                *\n"
            "*        42 - по видимой величине                                 *\n"
            "*        43 - все записи                                          *\n"
            "*   5. Сортировать записи (по возрастанию расстояния до Земли)    *\n"
            "*   6. Сохранить записи в файл                                    *\n"
            "*   7. Загрузить записи из файла                                  *\n"
            "*   ???                                                           *\n"
            "*   9. Выйти из меню                                              *\n"
            "*******************************************************************\n>> ");
 
    fflush(stdin);
    scanf("%d", &number);
    
    switch (number) {
        
    case 1:
       
          A = (struct zvezda *)malloc(sizeof(struct zvezda) * n) //или вообще не так?

          printf("Запись №%d:\n", n+1);
          vvod(&A[n]);
          n++;
        }
   
        break;

malloc в самом начале нужен.
Потом, когда не хватает памяти, надо перевыделять (выделять новую память большего размера, копировать туда). В С вроде realloc это делает: https://en.cppreference.com/w/c/memory/realloc

В идеале создать абстракцию “Динамический Массив”, по примеру std::vector из С++.
То есть например структура DynamicArray с полями размер и сам обычный массив (указатель), и работать с ней через функции типа append, insert, remove, которые проверяют размер и перевыделяют когда нужно.
И еще для оптимизации производительности обычно размер памяти увеличивают например в 2 раза, а не +1 элемент (хранят отдельно реальный размер и используемый размер).

https://stackoverflow.com/a/3536261/964478

struct DynamicArray
{
    Star* items;
    int capacity;
    int size;
}

void create(DynamicArray* arr)
{
    arr->size = 0;
    arr->capacity = 2;
    arr->items = malloc arr->capacity...;
}

void append(DynamicArray* arr, Star item)
{
    if (arr->capacity == arr->size)
    {
        arr->capacity *= 2;
        arr->items = realloc arr->capacity...;
    }
    arr->items[arr->size] = item;
    arr->size++;
}


DynamicArray stars;
create(&stars);

...

Star newStar;
newStar.name = "Sun";

append(&stars, newStar);

Похоже на правду?) В case 1 элемент добавляется, в case 2 - вставляется

case 1:
		
		if (A == NULL) 
		{	
		A = (struct zvezda*)malloc(sizeof(struct zvezda) * (n+1));     /* создаем массив динамически */
  			if (!A)                                                    /* проверка, удалось ли выделить память */
  			{
    		printf("Ошибка при выделении памяти!!\n");
    		system("pause");
    		return 1;
  			}
  		}
		
		else
		{
			A = (struct zvezda*)realloc(A, sizeof(struct zvezda) * (n+1));
			if (!A)                                                          /* проверка, удалось ли выделить память */
  			{
    		printf("Ошибка при выделении памяти!!\n");
    		system("pause");
    		return 1;
  			}
		}  
		
		  	
		printf("Запись №%d:\n", n+1);
        vvod(&A[n]);
        n++;
        
        break;	
	
	case 2:	
	
          k = dobav_number(n) - 1;
          
        if (k==-1) 
       	{
       		printf("Записи отсутствуют!\n");
       		system("pause");
       		break;
		}
        
		if (A == NULL) 
		{	
			A = (struct zvezda*)malloc(sizeof(struct zvezda) * (n+(k+1)));     /* создаем массив динамически */
  			if (!A)                                                            /* проверка, удалось ли выделить память */
  			{
    		printf("Ошибка при выделении памяти!!\n");
    		system("pause");
    		return 1;
  			}
  		}
		
		else
		{
			A = (struct zvezda*)realloc(A, sizeof(struct zvezda) * (n+(k+1)));    
			if (!A)                                                                /* проверка, удалось ли выделить память */
  			{
    		printf("Ошибка при выделении памяти!!\n");
    		system("pause");
    		return 1;
  			}
		}  
		  
  		if (k >= 0)
        {	
		for (i = n-1; i >= k; i--)
		{
       	A[i+1] = A[i];
		}
		printf("\nВведите информацию для записи №%d:\n\n", k+1);
        vvod(&A[k]); 	 	
    	}
		  
    	n++;
        break;

Так вот чтобы не копипастить if NULL, malloc и т.д. во все case и нужна абстракция как в примере выше )
Тогда бы всё было в 100500 раз проще:

create(&stars);

switch ...
case 1:
    star = inputStarData();
    append(&stars, star);
    break;
case 2:
    k = ...;
    star = inputStarData();
    insert(&stars, k, star);
    break;

Что-то странное :thinking:
Размер же всегда просто +1, причем тут k.

В предыдущем коде A кстати не инициализировано и не факт, что NULL.

Это примерно одно и то же, лучше выбрать какой-то один из этих вариантов )

Т е можно просто: n+1?

Не много не разобрался с этим… Подскажи, пожалуйста, как исправить

инициализировать все переменные при объявлении.

понял. а n+1 - эта запись вернее, чем n+(k+1)?

Alex P., подскажи, пожалуйста, что я не так здесь делаю?

int load (struct zvezda *p)
{
	FILE *file;
	char fname[20];
	int i;
	
	printf("Укажите имя файла и путь к нему: ");
	scanf ("%s", fname);
	file=fopen(fname, "rb");
	if (file == NULL)
	{
		printf("Возникла ошибка!");
		return(0);
	}
	
	// определяем размер файла
  	fseek(file , 0 , SEEK_END);                          // устанавливаем позицию в конец файла
  	long lSize = ftell(file);                            // получаем размер в байтах
  	rewind (file);                                       // устанавливаем указатель в конец файла
 
  	p = (struct zvezda* )malloc(sizeof(*p) * lSize); // выделить память для хранения содержимого файла
  	if (p == NULL)
  	{
      	printf("Возникла ошибка!");
		return 0;
 	}
	
	size_t result = fread(p, sizeof(*p), lSize, file);
	if (result != lSize)
  	{
      	printf("Возникла ошибка!");
		return 0;
 	}
	
    fclose(file);
    return result;
}

А зачем умножать на количество байтов в файле? :thinking:

все равно выдает ошибку(

И как понять в каком из этих мест она?))

Надо разные сообщения выводить. Ну и значения переменных.

int load (struct zvezda *p)
{
	FILE *file;
	char fname[20];
	int i;
	
	printf("Укажите имя файла и путь к нему: ");
	scanf ("%s", fname);
	file=fopen(fname, "rb");
	if (file == NULL)
	{
		printf("Возникла ошибка 1!");
		return(0);
	}
	
	// определяем размер файла
  	fseek(file , 0 , SEEK_END);                          // устанавливаем позицию в конец файла
  	long lSize = ftell(file);                            // получаем размер в байтах
  	rewind (file);                                       // устанавливаем указатель в конец файла
 
  	p = (struct zvezda* )malloc(sizeof(*p)); // выделить память для хранения содержимого файла
  	if (p == NULL)
  	{
      	printf("Возникла ошибка 2!");
		return 0;
 	}
	
	size_t result = fread(p, sizeof(*p), lSize, file);
	if (result != lSize)
  	{
      	printf("Возникла ошибка 3!");
		return 0;
 	}
	
    fclose(file);
    return result;
}

выделили память под размер указателя (если p не статичный массив: https://onlinegdb.com/BJhGTSXRI)
UPD: точнее если с *, то под размер одного элемента: https://onlinegdb.com/B1g-tIm0L

попытались прочитать намного больше (даже больше, чем было в файле).

fread принимает и возвращает количество элементов, а не байтов.
Надо привести одно к другому, например,

size_t itemSize = sizeof(struct Star);
long fileSize = ftell...;
size_t itemsCount = fileSize / itemSize;

Надо выводить как можно больше полезной инфы. В данном случае хотя бы значения переменных из этого в if.


ЗЫ текст в консоли легко выделяется мышкой (или Ctrl+A) и копируется (например, Ctrl+C в вин10) :wink:

Ребята, привет! Помогите, пожалуйста исправить вот эти ошибки в приведенной ниже функции:

  • у схемы *A=realloc(*A, …); есть серьезный недостаток: если память перевыделить не удалось, то обнуляется рабочий указатель и мы теряем наш массив, хотя в случае ошибки старый блок не уничтожается и данные в нем актуальны. В таких ситуациях надо использовать вспомогательный указатель, проверять его, а потом присваивать рабочему, если все в порядке.
  • кстати, если указатель на блок нулевой, то realloc ведет себя как malloc, т.е. просто выделяет память, это может упростить реализацию.
  • освободив блок, надо обнулить и указатель на него.
  • в штатных ситуациях функция завершается, не возвращая значение явно. Результат непредсказуем. Как снаружи узнать, удалось выполнить операцию с памятью или нет?

Заранее спасибо!

int memory(struct zvezda ** A, int n, int q)
{
	if (q == 1)
	{
	if (*A == NULL) 
		{	
		*A = (struct zvezda* )malloc(sizeof(struct zvezda) * (n+1));     
  			if (*A == NULL)                                                    /* проверка, удалось ли выделить память */
  			{
    		printf("Ошибка при выделении памяти!\n");
    		system("pause");
    		return 0;
  			}
  		}
		
		else
		{
			*A = (struct zvezda* )realloc(*A, sizeof(struct zvezda) * (n+1));
			if (*A == NULL)                                                          /* проверка, удалось ли выделить память */
  			{
    		printf("Ошибка при выделении памяти!\n");
    		system("pause");
    		return 0;
  			}
		} 
	} 
	
	if (q == 2)
	{
		*A =(struct zvezda* )realloc(*A, sizeof(struct zvezda) * n ); 
		if (*A == NULL)                                                          /* проверка, удалось ли выделить память */
  			{
  				return 0;
  			}
	}
	
	if (q == 3)
	{
		free (*A);
	}	
}

Так и что непонятного в предложенном решении?)

= NULL

Да, тут почему-то не во всех случаях завершения функции есть return.
И непонятно что она возвращает (что такое 0?). Если “не успешно”/“успешно” (0/не ноль), то с C99 есть bool: https://en.wikibooks.org/wiki/C_Programming/stdbool.h

Значит можно убрать q и все if (кроме проверки для первого замечания тут). И передавать сразу в параметр n или n+1.

q == 3 непонятно для чего нужен. Это ж просто один вызов в конце программы и free(A) намного проще понять, чем memory(A, n, 3);