How to create dynamic struct array in C++? - c++

Im trying to learn dynamic arrays in C++. For integer, dynamic arrays are like that:
int main()
{
int x;
cin >> x;
int *dynamic = new int[x];
//some codes
delete [] dynamic;
return 0;
}
How can i create dynamic struct array? I tried this code and i failed.
struct Phone{
char name[30];
char number[20];
}
int main(){
int x;
cin >> x;
Phone *record;
Phone *record = new Phone[x];// Code fails here
}
Im so confused in dynamic arrays. Please help me. Thanks.

There is no difference in syntax between allocating an int and allocating a struct.
Your syntax is correct. You're just defining the record pointer twice. Remove the first definition and you're all set (oh, and missing a semicolon after the struct{} declaration).
Note that modern C++ would probably prefer using an existing STL container (vector<Phone> or similar) instead of manually calling new and delete[]. I assume this is for learning, not for production code.

I would also suggest to rather use an std::vector. It's going to save you a lot of issues and memory bugs. Just do:
struct structName
{
...
}
std::vector<structName> structVector;
Then to get the values onto the vector do a
structVector.push_back(structName{});

Related

Normal array declaration vs. dynamic array declaration

I just started learning C++. I learned the easy way of declaring arrays and now I'm confused about the usage of
int* foo = new int[n];
and how it is different from
int foo [n];
I tried testing with code but couldn't find any difference. I read from sources that using "new" requires me to manually de-allocate the memory after I don't need it anymore. In that case, there is no advantage in using "new" or dynamic memory allocation at all. Am I missing something here?
I tried running this:
#include <iostream>
int main() {
int n;
std::cout << "array size" ;
std::cin >> n ;
std::cout << n ;
int foo [n]; //line A
// int* foo = new int[n]; //line B
foo[6] = 30;
std::cout<<foo[6]<<std::endl;
}
Commenting out line B to run line A, or vice versa, gave the exact same result.
There are several ways these are different. First, let's talk about this one:
int n = 10;
int array[n];
This is not part of the ANSI C++ standard and may not be supported by all compilers. You shouldn't count on it. Imagine this code:
int n = 10;
int array[n];
n = 20;
How big is array?
Now, this is the way you can do it (but it's still problematic):
int n = 10;
int * array = new int[n];
Now, that is legal. But you have to remember later to:
delete [] array;
array = nullptr;
Now, there are two other differences. The first one allocates space on the stack. The second allocates space on the heap, and it's persistent until you delete it (give it back). So the second one could return the array from a function, but the first one can't, as it disappears when the function exits.
HOWEVER... You are strongly, strongly discouraged from doing either. You should instead use a container class.
#include <array>
std::array<int, n> array;
The advantages of this:
It's standard and you can count on it
You don't have to remember to free it
It gives you range checking

Question of dynamic memory allocation and dynamic memory deallocation

I need someone to solve the problem which is a question of dynamic memory allocation and
dynamic deallocation.
Here's a part of code to create database
include <iostream>
#include <string>
using namespace std;
struct subject {
string subname;
int score;
string grade;
float gpa;
};
struct student {
string stuname;
int stunum;
int subnum;
subject *sub;
float avegpa = 0;
};
int main(void){
int i = 0;
cout << "Put number of students : ";
cin >> i;
student* p = new student[i];
.
.
.
delete p->sub;
delete[] p;
return 0;
}
here's my desired result
Now, I have to enter the value of i first, but I hope I can automatically set
the value of i.
In order to try number 1, I pushed back value of i and increased i, but there was an
error. I don't know why.
This is the error message from number 2.
C++ crt detected that the application wrote to memory after end of heap buffer
delete p->sub;
You never initialised p[0].sub. Deleting (or even reading) an uninitialised pointer will result in undefined behaviour. You may delete only what you new.
P.S. Owning bare pointers are a bad idea. I recommend using std::vector to manage dynamic arrays.

Program crashes when running this function

I'm having trouble with wrapping my head around pointers, and using pointers in structs. To start, I don't know if I am using the pointer properly in the struct. Additionally, when I run my program, it seems to crash when it reaches readRecords, so there must be something wrong with it. Since I don't quite know how to use pointers very well, I am probably doing something wrong here... I just don't know what. Is there some way that I can edit this function so that I don't get crashes? Also, I have to keep these functions, as they are part of my project requirements.
struct testScores
{
string name;
string idNum;
int testNum;
int *tests; // This is supposed to be a dynamically allocated array
double average;
char grade;
};
[...]
void arrStruct(testScores*& sPtr)
{
sPtr = new testScores[];
}
void readRecords(ifstream& data, int record, testScores*& sPtr)
{
for (int count = 0; count < record; count++)
{
data >> sPtr[count].name;
data >> sPtr[count].idNum;
data >> sPtr[count].testNum;
sPtr[count].tests = new int[sPtr[count].testNum]; // tests is dynamically allocated (?)
for (int tCount = 0; tCount < sPtr[count].testNum; tCount++)
data >> sPtr[count].tests[tCount];
}
}
sPtr = new testScores[];
This appears to be illegal syntax - array new requires a subscript to know how much space to allocate.
Usually this is at least 1, in your case the compiler probably interprets this as new testScores[0] which does return a valid pointer, but without allocating any memory from the heap.
Of course any subsequent access to memory pointed to by sPtr is out-of-bounds and causes undefined behaviour (in your case a crash).

Multiple arrays in a class and XCode

I am trying to use XCode for my project and have this code in my .h:
class FileReader
{
private:
int numberOfNodes;
int startingNode;
int numberOfTerminalNodes;
int terminalNode[];
int numberOfTransitions;
int transitions[];
public:
FileReader();
~FileReader();
};
I get a "Field has incomplete type int[]" error on the terminalNode line... but not on the transitions line. What could be going on? I'm SURE that's the correct syntax?
Strictly speaking the size of an array is part of its type, and an array must have a (greater than zero) size.
There's an extension that allows an array of indeterminate size as the last element of a class. This is used to conveniently access a variable sized array as the last element of a struct.
struct S {
int size;
int data[];
};
S *make_s(int size) {
S *s = (S*)malloc(sizeof(S) + sizeof(int)*size);
s->size = size;
return s;
}
int main() {
S *s = make_s(4);
for (int i=0;i<s->size;++i)
s->data[i] = i;
free(s);
}
This code is unfortunately not valid C++, but it is valid C (C99 or C11). If you've inherited this from some C project, you may be surprised that this works there but not in C++. But the truth of the matter is that you can't have zero-length arrays (which is what the incomplete array int transitions[] is in this context) in C++.
Use a std::vector<int> instead. Or a std::unique_ptr<int[]>.
(Or, if you're really really really fussy about not having two separate memory allocations, you can write your own wrapper class which allocates one single piece of memory and in-place constructs both the preamble and the array. But that's excessive.)
The original C use would have been something like:
FileReader * p = malloc(sizeof(FileReader) + N * sizeof(int));
Then you could have used p->transitions[i], for i in [0, N).
Such a construction obviously doesn't make sense in the object model of C++ (think constructors and exceptions).
You can't put an unbound array length in a header -- there is no way for the compiler to know the class size, thus it can never be instantiated.
Its likely that the lack of error on the transitions line is a result of handling the first error. That is, if you comment out terminalNode, transitions should give the error.
It isn't. If you're inside a struct definition, the compiler needs to know the size of the struct, so it also needs to know the size of all its elements. Because int [] means an array of ints of any length, its size is unknown. Either use a fixed-size array (int field[128];) or a pointer that you'll use to malloc memory (int *field;).

Scope, arrays, and the heap

So, I have this array. It needs to be accessed outside the scope of this function. I have been slapping a pointer to it into a pair which gets put into a deque. But once I'm outside the scope, the local stack is gone, the array is invalid, and I've just got a useless pointer, right?
So I've trying to put this array onto the scope-transcending heap, where it will remain until I delete it at a later time. But I'm having issues getting this working. Right now g++ is coughing up errors about invalid conversion from 'int' to 'int*'.
void randomFunction(int x, int y, int width, int height)
{
int **blah[4] = {x, y, width, height};
std::pair <foobar*, int* > tempPair (foobar1, blah);
randomDeque.push_front(tempPair);
}
I've also tried initializing it like this:
int *blah[4] = new int[4];
...and it says that the array must be initialized with a brace-enclosed initializer.
I'm really not used to working with pointers. What am I doing wrong?
There are two problems. First, indeed, you are confused about pointers/arrays:
int a[4]; // this is an array of 4 integers, a is the name of the array
int *a[4]; // This is an array of 4 *pointers* to int
So your declaration:
int **blah[4];
Define an array of 4 pointers to pointers array. Maybe you are confused by the following fact (I know I was when I learned C). If you declare a variable:
int *a;
This is a declaration of a pointer to an integer. But if you have a variable a which is a pointer, you get the thing it points to (an integer here) by using *a:
*a = 1; // a is a pointer (i.e. an address), *a is the value pointed to by a.
So * in declaration is used to declare pointer, but * in statements is used to deference value.
But your second problem has nothing to do with pointer per-se. It is about ressource-management (memory being one, but file, locks being others). Anything allocated on the stack does not exist anymore when it is out of scope. In pure C, you only really have one solution: allocating on the heap with malloc, and making sure to free afterwards. So you would do something like:
// foo is a struct
foo *init_foo()
{
foo* tmp;
tmp = malloc(sizeof(*tmp));
// initialize tmp
return tmp;
}
And then, you will clean it with another function:
foo *a;
a = init_foo();
// do stuff
clean_foo(a);
Example: the FILE* handle and fopen/fclose (in addition to allocating stuff, there are some things related to the OS to handle the file). Another solution is to use alloca, which is not standard C, but is supported by many toolchains.
In C++, you can use smart pointers, which use for example reference counting to do resources management. I am less familiar with C++, and I am sure people will jump in on that part. The idea with reference counting is that it still gives some of the advantages of auto pointers (you don't have to call delete by yourself, which is extremely error-prone for non trivial projects), but without being purely scope-based. One reference counting-based smart pointer is shared_ptr in boost.
The whole concept looks strange to me. If you declare array on the stack, it will not exist outside the scope of your function. If you allocate it using 'new' - make sure you 'delete' it sometime, otherwise it's memory leak!
The correct code with 'new' is:
int *blah = new int[4];
...
// don't forget to:
delete [] blah;
I'm not sure if I got right what you want to do, but in case you want to return a reference to an int array which will be valid after randomFunction returns, a good way to do it is with Boost:
#include <boost/shared_ptr.hpp>
#include <vector>
boost::shared_ptr<std::vector<int> > randomFunction(int x, int y, int width, int height)
{
boost::shared_ptr<std::vector<int> > blahPtr(new std::vector<int>(4));
(*blahPtr)[0] = x;
(*blahPtr)[1] = y;
(*blahPtr)[2] = width;
(*blahPtr)[3] = height;
return blahPtr;
}
You don't have to remember about deleteing blahPtr -- when all copies of it go out of scope, Boost will delete your std::vector object automatically, and C++ standard library will delete the underlying array.
It looks like you want a 4x4 array, in which case you should create it like so (untested code from the top of my head):
int **blah = new int* [4];
for(int i = 0; i < 4; ++i)
{
*blah[i] = new int[4];
}
Alternatively you can create a 1D array and treat it like a 2D array:
int *blah = new int[16];
#define ELEM(x,y) w*4+h
blah[ELEM(1,1)] = 123;