Where does a list inside an object gets its stack?
The program below shows a size of the object of 40. So where did the list allocate its memory from?
class TEST
{
public:
TEST(void);
void Push(void);
private:
std::list<int> _list;
std::list<int>::iterator _it;
int f;
};
TEST::TEST(void) : f(1)
{
}
void TEST::Push(void)
{
_list.push_back(f++);
}
main:
{
TEST n;
int a = sizeof(n);
std::cout << a << std::endl;
n.Push();
a = sizeof(n);
std::cout << a << std::endl;
for(int r=0; r<10000; r++){
n.Push();
}
a = sizeof(n);
std::cout << a << std::endl;
}
Output:
40 <br/>
40 <br/>
40
Just to add on to the comment about your use of sizeof()
I would take a look at the answer to this question here:
Size of class object in C++
Essentially the sizeof() operator only takes into account the members of the class and not any associated heap allocations. Although a member variable, _list is essentially just a pointer to an area of memory on the heap, so it maintains a constant size no matter how many items it is referring to - since in reality it is basically just storing a single address of memory. (It obviously stores some more information than that, but I want to keep it simple)
So no matter what you do to _list, the result of sizeof will remain constant since the actual members within n remain constant size.
You can use the public method std::list::get_allocator to see where your data is stored.
Here is an example of how you can use it(but just to reaserch, It's not supposed to be used like that):
int main ()
{
int * ptr;
// allocate an array of 5 elements using _list's allocator:
ptr=_list.get_allocator().allocate(5);
// assign some values to array
for (int i=0; i<5; ++i) ptr[i]=i;
std::cout << "The allocated array contains:";
for (int i=0; i<5; ++i) std::cout << ' ' << ptr[i];
std::cout << '\n';
_list.get_allocator().deallocate(ptr,5);
return 0;
}
BTW: Is not a good idea to name members starting with underscore:
all identifiers that begin with an underscore are reserved for use as
names in the global namespace
Related
I know that the following is not correct:
int arr[2][3] = {}; //some array initialization here
int** ptr;
ptr = arr;
But I am quite surprised that the following lines actually work
int arr[2][3] = {}; //some array initialization here
auto ptr = arr;
int another_arr[2][3] = {}; //some array initialization here
ptr = another_arr;
Can anyone possibly explain what is the type assigned to ptr in the second block of code, and what happened underneath?
Well, arrays decay to pointers when used practically everywhere. So naturally there's decay going on in your code snippet too.
But it's only the "outer-most" array dimension that decays to a pointer. Since arrays are row-major, you end up with int (*)[3] as the pointer type, which is a pointer to a one-dimensional array, not a two dimensional array. It points to the first "row".
If you want ptr's deduction to be a pointer to the array instead, then use the address-of operator:
auto ptr = &arr;
Now ptr is int(*)[2][3].
In
auto ptr = arr;
arr decays into a pointer to its first element in the normal way; it's equivalent to
auto ptr = &arr[0];
Since arr[0] is an array of three ints, that makes ptr a int (*)[3] - a pointer to int[3].
another_arr decays in exactly the same way, so in
ptr = another_arr;
both sides of the assignment have the type int (*)[3], and you can assign a T* to a T* for any type T.
A pointer to arr itself has type int(*)[2][3].
If you want a pointer to the array rather than a pointer to the array's first element, you need to use &:
auto ptr = &arr;
First, let's look at why you can't assign int arr[2][3] to int **. To make it easier to visualise, we'll initialise your array with a sequence, and consider what it looks like in memory:
int arr[2][3] = {{1,2,3},{4,5,6}};
In memory, the array data is stored as a single block, just like a regular, 1D array:
arr: [ 1, 2, 3, 4, 5, 6 ]
The variable arr contains the address of the start of this block, and from its type (int[2][3]) the compiler knows to interpret an index like arr[1][0] as meaning "take the value that is at position (1*2 + 0) in the array".
However for a pointer-to-pointer (int**), it is expected that the pointer-to-pointer contains either a single memory address or an array of memory addresses, and this/these adress(es) point to (an)other single int value or array of ints. Let's say we copied the array arr into int **ptrptr. In memory, it would look like this:
ptrptr: [0x203F0B20, 0x203F17D4]
0x203F0B20: [ 1, 2, 3 ]
0x203F17D4: [ 4, 5, 6 ]
So in addition to the actual int data, an extra pointer must be stored for each row of the array. Rather than converting the two indexes into a single array lookup, access must be performed by making a first array lookup ("take the second value in ptrptr to get an int*"), then nother array lookup ("take the first value in the array at the address held by the previously obtained int*").
Here's a program that illustrates this:
#include <iostream>
int main()
{
int arr[2][3] = {{1,2,3},{4,5,6}};
std::cout << "Memory addresses for int arr[2][3]:" << std::endl;
for (int i=0; i<2; i++)
{
for (int j=0; j<3; j++)
{
std::cout << reinterpret_cast<void*>(&arr[i][j]) << ": " << arr[i][j] << std::endl;
}
}
std::cout << std::endl << "Memory addresses for int **ptrptr:" << std::endl;
int **ptrptr = new int*[2];
for (int i=0; i<2; i++)
{
ptrptr[i] = new int[3];
for (int j=0; j<3; j++)
{
ptrptr[i][j] = arr[i][j];
std::cout << reinterpret_cast<void*>(&ptrptr[i][j]) << ": " << ptrptr[i][j] << std::endl;
}
}
// Cleanup
for (int i=0; i<2; i++)
{
delete[] ptrptr[i];
ptrptr[i] = nullptr;
}
delete[] ptrptr;
ptrptr = nullptr;
return 0;
}
Output:
Memory addresses for int arr[2][3]:
0x7ecd3ccc0260: 1
0x7ecd3ccc0264: 2
0x7ecd3ccc0268: 3
0x7ecd3ccc026c: 4
0x7ecd3ccc0270: 5
0x7ecd3ccc0274: 6
Memory addresses for int **ptrptr:
0x38a1a70: 1
0x38a1a74: 2
0x38a1a78: 3
0x38a1a90: 4
0x38a1a94: 5
0x38a1a98: 6
Notice how the memory addresses always increase by 4 bytes for arr, but for ptrptr there is a jump of 24 bytes between values 3 and 4.
A simple assignment can't create the pointer-to-pointer structure needed for type int **, which is why the loops were necessary in the above program. The best it can do is to decay the int[2][3] type into a pointer to a row of that array, i.e. int (*)[3]. That's what your auto ptr = arr; ends up as.
What is the type of [...]
Did you already try to ask the compiler to tell you the type of an expression?
int main()
{
int arr[2][3] = {{0,1,2}, {3,4,5}}; // <-- direct complete initialized here
auto ptr = arr; // <-- address assignment only
cout << "arr: " << typeid(arr).name() << endl;
cout << "ptr: " << typeid(ptr).name() << endl;
return 0;
}
I've to confess that the output
arr: A2_A3_i
ptr: PA3_i
seems to be not very readable at first glance (compared to some other languages), but when in doubt it may help. It's very compact, but one may get used to it soon. The encoding is compiler-dependent, in case you are using gcc, you may read Chapter 29. Demangling to understand how.
Edit:
some experimentation with some simple_cpp_name function like this rudimentary hack
#include <typeinfo>
#include <cxxabi.h>
#include <stdlib.h>
#include <string>
std::string simple_cpp_name(const std::type_info& ti)
{
/// simplified code extracted from "Chapter 29. Demangling"
/// https://gcc.gnu.org/onlinedocs/libstdc++/manual/ext_demangling.html
char* realname = abi::__cxa_demangle(ti.name(), 0, 0, 0);
std::string name = realname;
free(realname);
return name;
}
will show you that auto &rfa = arr; makes rfa having the same type as arr which is int [2][3].
I am working on a paint program and i create an array of figures in a class
and i want to pass that array to another class and loop through it's elements
i dont understand the problem
the main class:
CFigure* FigureList[Figcount];
CFigure* getfiglist()
{
return *FigureList;
}`
and the other class include:
CFigure *P=pManager->Getfiglist();
for(int i=0;i<pManager->Getfigcount;i++)
{
*(p+i)->resize(factor); //displays error : operand of * must be a pointer
}
how should i access the array elements using the passed pointer and what did i do here that caused the error . thanks in advance
There is a lot to say about arrays in C++, here is some information that might help :
Native C++ arrays
if you create an array like so :
int array[10] = {0,1,2,3,4,5,6,7,8,9};
And you try to print the values of array, &array or &array[0] you will notice they are all the same values. This value is the adress of the first element in your array. There is no such thing as an array pointer, there is just a pointer to the first element.
As the other elements of your array follow the first, you can access them by adding 1 to the address of the first element (array+1) :
void print(int *array, int length)
{
for (int i=0 ; i<length ; i++){
cout << *(array+i) << endl;
}
}
int main()
{
int array[10] = {0,1,2,3,4,5,6,7,8,9};
print(array, 10);
}
It turns out that you can also use the regular expression to access your array elements and it will work fine :
void print(int *array, int length)
{
for (int i=0 ; i<length ; i++){
cout << array[i] << endl;
}
}
Vectors
That being said, if one is not careful with native arrays, the code may quickly get very nasty and hard to read... This is why you should use std::vector instead as suggested by #Ðаn.
Vectors have the particularity to be dynamic so you don't have to specify any size when you create them, and you can add as many elements as you want without caring about any pointer or memory stuff.
void print(vector<int> &v)
{
for (int i=0 ; i<v.size() ; i++){
cout << v[i] << endl;
}
}
int main()
{
vector<int> v = {0,1,2,3,4,5,6,7,8,9};
v.push_back(10);
v.push_back(11);
v.push_back(12);
print(v);
}
Links
You can read more about vectors here.
I didn't talk about it but there is also std::array.
Lets say I wanted to make a vector of objects and another vector of pointers to those objects (I cannot use dynamic memory). The way I would do it is in the following example.
#include <iostream>
#include <vector>
using namespace std;
class Foo {
public:
int bar;
Foo(int x) : bar(x) {
}
};
int main () {
vector<Foo> foos;
vector<Foo*> pFoos;
for (int i = 0; i < 10; i++) {
Foo foo(i);
foos.push_back(foo);
pFoos.push_back(&foos.back());
}
for (int i = 0; i < 10; i++) {
cout << foos[i].bar << endl;
cout << pFoos[i]->bar << endl;
}
}
I think this should work because foos stores a copy of the object, and then I store the reference to the copy (because the original foo will be undefined, so I shouldn't store a reference to that). But this is what I get:
0
36741184
1
0
2
2
3
3
4
4
5
5
6
6
7
7
8
8
9
9
The first to numbers from pFoos are wrong. Furthermore, the large number changes every time. I don't see anything that would cause this undefined behavior. Could someone tell me what I'm doing wrong?
Adding items to a vector invalidates all previous iterators. Calling push_back on the vector may make the pointers you got from it previously invalid if the vector needs to reallocate its internal storage.
If you knew that you were never going to grow the vector again then this would work:
for (int i = 0; i < 10; i++) {
foos.push_back(Foo(i));
}
for (int i = 0; i < 10; i++) {
pFoos.push_back(&foos[i]);
}
or as commented by rodrigo:
foos.reserve(10)
for (int i = 0; i < 10; i++) {
Foo foo(i);
foos.push_back(foo);
pFoos.push_back(&foos.back());
}
for (int i = 0; i < 10; i++) {
cout << foos[i].bar << endl;
cout << pFoos[i]->bar << endl;
}
I guess this is not a good programming approach. The vector is responsible for storing objects and it may REALLOC memory to get bigger memory blobs ... So, your pointer is not valid anymore ...
vector::push_back can move elements, which is why the addresses aren't valid. You can pre-load the memory for the vector to its final size by calling reserve before you start pushing things into the vector, or you can wait until you've finished pushing things before you take their addresses.
But you say that you "cannot use dynamic memory". vector uses dynamic memory.
I am working on a homework assignment with a few specific requirements. There must be a class named TestScores that takes an array of scores as its argument. It throws an exception if any scores are negative or greater than 100. Finally, it must have a member function that returns an average for all the scores. I wasn't clever enough to find a way to only pass the array into the constructor, so I also added in an int that tells the size of the array.
Running the code (I haven't even gotten around to testing the exceptions yet), I keep getting a Segmentation fault error. Valgrind and gdb have been rather unhelpful, outputting messages like:
==9765== Jump to the invalid address stated on the next line
==9765== at 0x2200000017: ???
Even more mysteriously (to me at least), in the for loop in the client code, my incrementor, i, somehow gets bumped from 0 to a seemingly random two-digit number right after creating the TestScores object. In previous versions, before I started using rand() to populate the array, i just never incremented and did the infinite loop thing.
Here's the contents of TestScores.cpp:
#include <iostream>
using std::cout;
using std::endl;
#include "TestScores.h"
#include <stdexcept>
using std::runtime_error;
// Constructor.
TestScores::TestScores(int a[], int s):
_SIZE(s), _scores()
{
// Look at each item in a[], see if any of them are invalid numbers, and
// only if the number is ok do we populate _scores[] with the value.
for (int i = 0; i < _SIZE; ++i)
{
if (a[i] < 0)
{
throw runtime_error ("Negative Score");
}
else if (a[i] > 100)
{
throw runtime_error ("Excessive Score");
}
_scores[i] = a[i];
cout << _scores[i] << " ";
}
cout << endl;
}
// Finds the arithmetic mean of all the scores, using _size as the number of
// scores.
double TestScores::mean()
{
double total = 0;
for (int i = 0; i < _SIZE; ++i)
{
total += _scores[i];
}
return total / _SIZE;
}
// median() creates an array that orderes the test scores by value and then
// locates the middle value.
double TestScores::median()
{
// Copy the array so we can sort it while preserving the original.
int a[_SIZE];
for (int i = 0; i < _SIZE; ++i)
{
a[i] = _scores[i];
}
// Sort the array using selection sort.
for (int i = 0; i < _SIZE; ++i)
{
int min = a[i];
for (int j = i + 1; j < _SIZE; ++j)
{
if (a[j] < min)
{
min = a[j];
a[j] = a[i];
a[i] = min;
}
}
}
// Now that array is ordered, just pick one of the middle values.
return a[_SIZE / 2];
}
And here's the client code:
#include <iostream>
#include "TestScores.h"
#include <stdexcept>
#include <cstdlib>
#include <ctime>
using std::exception;
using std::cout;
using std::endl;
int main()
{
const int NUM_STUDENTS = 20,
NUM_TESTS = 4;
int test [NUM_TESTS][NUM_STUDENTS];
// Make random seed to populate the arrays with data.
unsigned seed = time(0);
srand(seed);
// Populate the scores for the individual tests graded for the semester.
// These will all be values between 0 and 100.
for (int i = 0; i < NUM_TESTS; ++i)
{
for (int j = 0; j < NUM_STUDENTS; ++j)
{
test[i][j] = rand() % 100;
cout << test[i][j] << " ";
}
cout << endl;
}
// Now we have the data, find the mean and median results for each test.
// All values should be valid, but we'll handle exceptions here.
for (int i = 0; i < NUM_TESTS; ++i)
{
cout << "For Test #" << i + 1 << endl;
try
{
cout << "i = " << i << endl; // i = 0 here.
TestScores results(test[i], NUM_STUDENTS);
cout << "i = " << i << endl; // i = some random number here.
cout << "Mean: " << results.mean() << endl;
cout << "Median:" << results.median() << endl << endl;
}
catch (exception &e)
{
cout << "Error, invalid score: " << e.what() << endl;
}
cout << "For Test #" << i + 1 << endl;
}
return 0;
}
Edit:
The header was requested as well:
#ifndef TEST_SCORES_H
#define TEST_SCORES_H
class TestScores
{
private:
const int _SIZE;
int _scores[];
public:
// Constructor
TestScores(int a[], int);
double mean() const,
median() const;
};
#endif
I played around with making the array dynamic, and didn't initialize the array as empty, which fixed my problems, so that's what I ended up turning in. That leads me to a few follow-up questions.
Before going dynamic, I played around with initializing the array, _scores, by trying to give it the size value that was supposed to already be initialized. This led to compiler problems. I talked with my teacher about that, and he said that you can't allocate space for an array unless there's a hardwired global constant. That is, you can't pass a size value in the constructor to initialize an array. Is that true, and if so, why?
Stepping back a bit, it seems to me that dynamic arrays are better if you need a lot of values, because then you don't need a contiguous block of space in memory. So if you are making small arrays, it seems like a waste of space and time typing to make dynamic arrays. Is this untrue? Should I be doing all arrays from now on as dynamic? This experience certainly changed my opinion on the utility of regular arrays, at least as they pertain to classes.
Also, though I got full credit on the assignment, I feel like I violated the spirit by passing an argument for size (since the literal problem statement reads: "The class constructor should accept an array of test scores as its argument"). Aside from a hardwired global constant or having a size argument, is there a way to pass just the array? I swear I spent a good hour trying to think of a way to do this.
It seems you don't initialize _scores at all. You need _scores = new int[s]; at the top of the constructor (and also delete[] s; in the destructor).
Without initializing _scores, you write things to undefined memory locations.
Without TestScores.h one has to guess, but given what you say about the value of i being corrupted in the loop where you're creating the TestScores objects, that points to your _scores member variable not being properly initialized and when you're trying to load it you are actually trashing memory.
Once TestScores.h is visible, I'll revisit this answer taking the file into account.
Updated now that TestScores.h is available.
The problem is that you are not initializing _scores. You are not actually allocating any memory to hold the array, let alone setting the pointer to point to that memory. So when you attempt to store things into the array you're just trashing memory somewhere.
The first line in your constructor should be:
_scores = new int[_SIZE];
That will allocate memory to hold _SIZE ints and set _scores to point to that memory. Then your assignments to _scores[i] will actually go into defined memory belonging to your program.
Of course, you also have to release this memory (C++ won't do it for you) when instances of TestScore get destroyed. So you will need to define and implement a destructor for TestScores and that destructor needs to contain the line:
delete [] _scores;
This will free the block of memory that _scores points to. You can read docs on the delete operation to see why the [] have to be there in this case.
I've been trying to erase an element from an array without changing the index order, for instance:
class MyObject
{
int id;
public:
MyObject() { }
void setId( int i ) { id = i; }
void showId() { std::cout << "ID: "<< id << "\n"; }
};
MyObject *myArray;
int main ( )
{
myArray = new myArray[6];
for( int i = 0; i < 6; i++ )
{
myArray[i]->setId(i);
myArray[i]->showId();
}
}
I want to remove myArray[3] without changing the index of the others. e.g:
myArray[0] = ID: 1
myArray[1] = ID: 2
myArray[2] = nothing
myArray[3] = ID: 4
myArray[4] = ID: 5
myArray[5] = ID: 6
I've tried to use use memset(), but it didn't work.
memset(&myArray[3],0,sizeof(MyObject));
There's no such thing as "nothing" in C++ language. Once you have an array, all elements of that array will contain "something". There's no way to make an array element just disappear with keeping all other elements in their original places. You can't create a hole in the array.
All you can do in this case is simply label some element as "deleted" and then later recognize it as such. The element will, of course, continue to exist physically. It is you who'll have to recognize it as "deleted" and ignore it in your further processing. You can either add some bool is_deleted field to your object, or you can use some reserved value of id (like -1) to indicate a deleted element.
In your example with memset you essentially set the id to zero. Is 0 a valid id value? If it is not, then 0 is a good choice to mark a deleted element. In that sense your memset attempt works perfectly fine, as it should. Although I'd recommend doing it by explicitly assigning zero to id, without using memset.
You are calling memset to write a bunch of zeros over the top of an object instance. Do not do this! You may get away with it if your class is a true POD class. You might end up just setting the ID to 0. But maybe there is more to your class that you aren't showing. In anycase, even if it isn't POD, don't use memset like that.
You can either store pointers to object and use the null pointer to indicate there is nothing there. I'd do this with std::vector<MyObject*>. Or you use a sentinel object instance, for example with ID of -1.
The other thing that could be a problem is that you appear to be using 1-based indices. C++ arrays are 0-based, so the first element is myArray[0] and not myArray[1].
Using memset that way is setting all the bytes of that object to 0. This is usually equivalent to setting id to 0, because the memory of an object is the memory of its members (not counting vtables, padding, etc). But don't do that anyway.
One way to do this is to use new and have an array of pointers.
MyObject* myArray[6];
int main ( )
{
for( int i = 0; i < 6; i++ )
{
myArray[i] = new MyObject;
myArray[i]->setId(i);
myArray[i]->showId();
}
}
Then to display them all:
for (int i = 0; i < 6; i++) {
cout << "myArray[" << i << "] = ";
if (myArray[i])
myArray[i]->showId();
else
cout << "nothing" << endl;
}
Then when you want to remove an object, delete it and set its pointer to NULL:
delete myArray
myArray[3] = NULL;
Then when you do anything with one of the objects in myArray, you must check if it is NULL to see if it's a valid object.
Consider boost::optional:
typedef boost::optional<MyObject> MyObjectOpt;
MyObjectOptArr *myArray;
The syntax/usage is a bit different (resembles using pointer):
for (int i = 0; i < 6; ++i) {
if (myArray[i])
cout << "myArray[" << i << "] = " << *(myArray[N]);
else
cout << "nothing" << endl;
}
To unset value do:
myArray[N] = boost::none;