Вопрос или проблема
Я учу C/C++ самостоятельно. Когда я создавал программу для чтения формата файла из игры, я смог получить необходимые данные, но проблема в том, что они доступны только внутри цикла, который их читает. Я не могу использовать их нигде else в функции, тем более снаружи.
Я знаю, что переменные и т.д. «исчезают» после цикла, но я использую динамическое выделение памяти с помощью метода «new», это массив структур. Я не могу инициализировать его заранее, потому что количество элементов неизвестно до чтения файла.
Мне нужна эта информация для последующего использования, так что если она живет только внутри цикла, это действительно бесполезно…
Вот мой код
typedef struct {
uint32_t FbxHash;
string ShapeName;
float ShapeTransforms[18];
bool converted;
} ShapeEntity;
и функция, которая читает файл:
void CompositeMeshReader(FILE* MeshCompPtr) {
uint8_t NumberOfLods;
uint8_t NumberOfMeshes;
uint8_t ShadersPerMesh;
float GlobalTransforms[6];
int NumberOfShapes;
int CurrentLOD;
int w;
int y;
int i;
int j;
int k;
string fname;
string* fnameptr = &fname;
uint8_t z;
uint8_t NullProperty;
uint8_t UnknownByte;
bool stay = true;
fseek(MeshCompPtr, 5, SEEK_SET);
StringSkipper(fnameptr);
cout << "Lods Settings File: " << fname << endl;
fread(&NumberOfLods, 1, 1, MeshCompPtr);
fseek(MeshCompPtr, 3, SEEK_CUR);
cout << "Lods detected: " << int(NumberOfLods) << ", which one to extract?" << endl;
cout << "for lod 0, type 0, lod 1, type 1, etc" << endl;
cin >> i;
fname = "";
CurrentLOD = 0;
while ((CurrentLOD < NumberOfLods) && (stay == true)) {
fread(&NullProperty, 1, 1, MeshCompPtr);
fread(&ShadersPerMesh, 1, 1, MeshCompPtr);
fread(&NumberOfMeshes, 1, 1, MeshCompPtr);
for (j = 0; j < NumberOfMeshes; j++) {
StringSkipper(fnameptr);
if (i == CurrentLOD) {
cout << "Mesh " << j << " " << fname << endl << endl;
}
for (k = 0; k < ShadersPerMesh; k++) {
StringSkipper(fnameptr);
if (i == CurrentLOD) {
cout << "Shader " << k << " " << fname << endl << endl;
}
}
}
fread(GlobalTransforms, sizeof GlobalTransforms[0], 6, MeshCompPtr);
fseek(MeshCompPtr, 8, SEEK_CUR);
fread(&UnknownByte, 1, 1, MeshCompPtr);
fread(&NumberOfShapes, 4, 1, MeshCompPtr);
ShapeEntity* Shapes = new ShapeEntity[NumberOfShapes]; // это то, что мне нужно сохранить в памяти
for (w = 0; w < NumberOfShapes; w++) {
fread(&Shapes[w].FbxHash, 4, 1, MeshCompPtr);
StringSkipper(fnameptr);
Shapes[w].ShapeName = fname;
fread(Shapes[w].ShapeTransforms, 4, 18, MeshCompPtr);
Shapes[w].converted = false;
}
fseek(MeshCompPtr, ShadersPerMesh * 24, SEEK_CUR);
if (i != CurrentLOD) {
cout << "Shapes destroyed" << endl;
delete[] Shapes;
}
else {
print(Shapes, ShadersPerMesh, NumberOfMeshes, NumberOfShapes, GlobalTransforms);
stay = false;
}
CurrentLOD++;
}
return;
}
Я пытаюсь сохранить Shapes в памяти даже после выхода из этой функции, но после выхода из цикла они просто исчезают, я просто схожу с ума, кажется, это невозможно? Без переписывания всего с нуля.
Я смотрел повсюду, но все равно не могу найти подходящее решение.
Либо верните shapes
(или передайте его как ссылку), либо используйте глобальную переменную для этого (что может стать проблемой, если вам понадобятся многопоточность или возникнут проблемы с динамически связанными библиотеками). Как написана ваша функция, она просто вызывает утечку памяти (выделяет память, но затем никогда не освобождает ее, поскольку вы не передаете указатель никуда больше).
Кроме того, если вы используете C++, лучше использовать что-то вроде std::vector
для этого, а не сырой указатель на динамически выделенный массив, так как тогда вы не столкнетесь с утечками памяти. Ваш код в основном является кодом на C, а не C++.
Ответ или решение
Конечно, давайте разберем вашу проблему и предложим несколько решений для того, чтобы сохранить структуру Shapes
вне цикла.
Ваша текущая реализация создает массив Shapes
внутри функции CompositeMeshReader
, но как только вы выходите из этой функции, память, которую вы выделили с помощью new
, становится недоступной, и вы не можете ее использовать. Однако, есть несколько подходов к решению этой проблемы.
Подход 1: Возврат указателя на массив
Один из простых способов – возвратить указатель на выделенный массив из функции. Вы можете изменить сигнатуру функции так, чтобы она возвращала указатель на массив ShapeEntity
. Пример:
ShapeEntity* CompositeMeshReader(FILE* MeshCompPtr, int* outShapeCount) {
// Объявление переменных...
// Реализация чтения данных...
// Выделяем память под Shapes:
ShapeEntity* Shapes = new ShapeEntity[NumberOfShapes];
for (w = 0; w < NumberOfShapes; w++) {
// Чтение данных в Shapes[w]...
}
*outShapeCount = NumberOfShapes; // Указатель на количество форм
return Shapes; // Возвращаем указатель
}
Теперь вы можете вызвать эту функцию следующим образом:
int shapeCount;
ShapeEntity* shapes = CompositeMeshReader(filePointer, &shapeCount);
// Не забудьте освободить память после использования
delete[] shapes;
Подход 2: Использование стандартного контейнера std::vector
Если вы работаете на C++, лучше всего использовать std::vector
, который автоматически управляет памятью. Пример:
#include <vector>
// Изменяем сигнатуру функции для возврата вектора
std::vector<ShapeEntity> CompositeMeshReader(FILE* MeshCompPtr) {
std::vector<ShapeEntity> Shapes;
// Читаем данные и заполняем вектор
// Пример:
Shapes.resize(NumberOfShapes); // Предварительно изменяем размер вектора
for (w = 0; w < NumberOfShapes; w++) {
// Чтение данных в Shapes[w]...
}
return Shapes; // Возвращаем вектор
}
Теперь вызов функции будет выглядеть следующим образом:
std::vector<ShapeEntity> shapes = CompositeMeshReader(filePointer);
// Используйте shapes по необходимости
Используя std::vector
, вам не нужно беспокоиться о выделении и освобождении памяти, так как он делает это автоматически.
Подход 3: Передача указателя в качестве параметра
Вы также можете передать указатель на массив в качестве параметра функции, чтобы заполнить его изнутри функции:
void CompositeMeshReader(FILE* MeshCompPtr, ShapeEntity** Shapes, int* outShapeCount) {
// Читаем данные...
*Shapes = new ShapeEntity[NumberOfShapes];
for (w = 0; w < NumberOfShapes; w++) {
// Чтение данных в Shapes[w]...
}
*outShapeCount = NumberOfShapes;
}
Использование этой функции выглядит так:
ShapeEntity* shapes;
int shapeCount;
CompositeMeshReader(filePointer, &shapes, &shapeCount);
// Не забудьте освободить память
delete[] shapes;
Заключение
Каждый из этих подходов предоставляет способ сохранить массив Shapes
за пределами функции. Наиболее предпочтительным решением в C++ является использование std::vector
, так как это более безопасно и удобно, чем управление памятью вручную.
Если возникнут дополнительные вопросы или сложности, пожалуйста, дайте знать!