Я хочу инициализировать структуры в цикле и обновить массив. Следующий код, который я использую.
#include <iostream>
struct Record {
char *name;
};
struct Response {
Record *records[];
};
int main()
{
Response *response = new Response;
for(int i=0; i<4; i++) {
Record *record= new Record();
char x[20];sprintf(x, "%d", i);
record->name = (char*)x;
response->records[i] = record;
std::cout << "Inserting: " << x << "\n";
//Not sure if I have to delete.
delete record;
}
for(int i=0; i<4; i++) {
std::cout << "Fetching: " << response->records[i]->name << "\n";
}
}
Странно при печати всех элементов в массиве печатаются те же значения. Любая идея, комментарий или мысли о том, что здесь может быть неправильным?
Пример вывода:
Inserting: 0
Inserting: 1
Inserting: 2
Inserting: 3
Fetching: 3
Fetching: 3
Fetching:
Fetching: 3
Вы сохраняете указатели на локальные переменные в объектах, к которым вы обращаетесь после их удаления.
Даже если Response::records
были должным образом отсортированы, эти два фактора сделали бы код недействительным.
Idiomatic C++ будет использовать std::string
и std::vector<Record>
, но если вам действительно нужны строки и массивы C-стиля, это должно работать:
struct Record {
char name[20];
};
struct Response {
Record records[4];
};
int main()
{
Response response;
for(int i = 0; i < 4; i++) {
Record record;
sprintf(record.name, "%d", i);
response.records[i] = record;
std::cout << "Inserting: " << record.name << "\n";
}
for(int i = 0; i < 4; i++) {
std::cout << "Fetching: " << response.records[i].name << "\n";
}
}
Самая большая проблема здесь в том, что это C не C++, однако рассмотрим решение C:
Есть несколько проблем:
struct Response {
Record *records[];
};
Не забудьте указать измерение для вашего массива:
Record *records[4];
Следующий:
struct Record {
char *name;
};
Это просто объявляет указатель с именем name, для которого не указывается память. Вам нужно будет выделить память для имени в будущем или сделать ее статическим массивом здесь вместо указателя.
record->name = (char*)x;
Теперь имя указывает на статический массив x. Это будет происходить каждый раз через цикл, поэтому все экземпляры имен record-> будут указывать на один и тот же массив. Вот почему все они печатают одно и то же значение :-) Вам нужно скопировать строку в x в record-> имя, которую вы не можете сделать, потому что нет хранилища, связанного с именем record->.
2 выхода:
struct Record {
char name[20]; // static array
};
...
char x[20] = {0}; // initialise so we're sure of a terminating null
strcpy(record->name, x);
Или
struct Record {
char *name;
};
...
record->name = new char[20];
strcpy(record->name, x);
В заключение,
//Not sure if I have to delete.
delete record;
Нет, вы не должны удалять здесь, поскольку эти записи будут использоваться позже. Если вы должны удалить, настройте что-то вроде цикла инициализации, но только до конца main(), только на этот раз вы будете уничтожать. Разумеется, если говорить, что это C, вы не должны использовать новые и удалять. malloc() и бесплатное() семейство вызовов лучше подходят для стиля C. Главное преимущество new и delete over malloc() и free() - вызов конструкторов и деструкторов выделенных объектов в нужное время, но вы не использовали ни одну из этих функций.
Это было бы намного проще, короче и безопаснее в C++. Должен ли вы сделать это на C или вы считаете C++?
#include <iostream>
#include <cstring>
struct Record {
char *name;
};
struct Response {
Record *records[4];
};
int main()
{
Response *response = new Response;
for(int i=0; i<4; i++) {
Record *record= new Record();
char x[20];sprintf(x, "%d", i);
record->name = new char[20];
strcpy(record->name, x);
response->records[i] = record;
std::cout << "Inserting: " << x << "\n";
//Not sure if I have to delete.
//delete record;
}
for(int i=0; i<4; i++) {
std::cout << "Fetching: " << response->records[i]->name << "\n";
}
// the program is about to exit and the resources will be freed by the system, so this is not strictly necessary
for(int i=0; i<4; i++) {
delete [] response->records[i]->name;
delete response->records[i];
}
delete response;
}
EDIT: Здесь одно из возможных решений, которые больше C++ дружественные, без сырых указателей, все распределение памяти обрабатывается стандартной библиотекой в строке и векторе.
#include <iostream>
#include <string>
#include <vector>
using namespace std;
struct Record {
Record(string record_name) : name (record_name) {}
string name;
};
struct Response {
vector<Record> records;
};
int main()
{
Response response;
for(int i=0; i<4; i++) {
response.records.push_back(Record(to_string(i)));
std::cout << "Inserting: " << i << "\n";
}
for (auto r : response.records) {
cout << "Fetching: " << r.name << "\n";
}
}
record_name
должно быть передано по record_name
ссылке. Вы должны использовать [const] auto&
или вы делаете много копий.