When we use arrays and pointers this way:
int *g() {
int arr[] = {1, 2, 3};
return arr;
}
int f() {
int *value = g();
for (size_t i = 0; i < 3; i++) {
std::cout << value[i] << " ";
}
std::cout << std::endl;
}
int main(int argc, const char **argv) {
f();
return 0;
}
It is deleted from the stack after function g returns, so we get a segment error in the function f()
But using std::vector this same logic works without any problem:
std::vector<int> g() {
std::vector<int> arr = {1, 2, 3};
return arr;
}
int f() {
std::vector<int> value = g();
for (size_t i = 0; i < value.size(); i++) {
std::cout << value.at(i) << " ";
}
std::cout << std::endl;
}
int main(int argc, const char **argv) {
f();
return 0;
}
I was thinking that this is something related to the fact that we have the std::vector class acting as a container to the array but using a struct as a container delete the array either:
struct T {
int *arr;
};
T g() {
T t;
int arr[] = {1, 2, 3};
t.arr = arr;
return t;
}
int f() {
T value = g();
for (size_t i = 0; i < 3; i++) {
std::cout << value.arr[i] << " ";
}
std::cout << std::endl;
}
int main(int argc, const char **argv) {
f();
return 0;
}
And in this case, g++ allow us to overflow the array pointer memory limits with the index subscription.
Why vectors doesn't share the same problem?
All the elements in the vector essentially count as part of the vector. And notice you are returning the vector, not a pointer to the vector. But you are returning a pointer to (the start of) the array.
When the function g returns, the vector arr is destroyed but that's okay because that's not the same one that is returned. A copy of arr is returned. (Actually, the compiler is clever enough to reuse the same vector instead of copying it and then deleting one - but you can imagine that it makes a copy)
Then f prints values from its own local variable value - no problem.
By contrast, in the pointer version, f is printing values from the local variable arr inside g, which has already been destroyed. If g would return a pointer to a vector, it would have the same problem.
Your intuition was somewhat right it has something to do with the containment of the array inside a struct, what you misunderstand however is the difference between (int[]) and (int*) and probably you don't understand well where memory resides as well.
To clarify:
In C++ C-style array types (int[]), (char[]) etc cannot be returned from a function.
Your function does not return (int[]) it returns int* which is a pointer that points to an an (int[]'s zero element in the stack space of some function f() ) and hence the existence of this (int[]) stops past the lifetime of the function f(), but a dangling pointer was copied to the caller's site;
Try
using int_array = int[4];
int_array f()
{
int_array arr { 1, 2, 3, 4};
return arr; // Does not compile cannot return array
}
int* f2()
{
int_array arr { 1, 2, 3, 4};
return arr; // Compiles because int[4] is implicitly cast to int*
}
In the case of vector, it contains a pointer to a dynamically allocated memory block, acquired through some means(new, malloc, custom user defined allocator, etc) hence this memory block is not bound to the lifetime of a function call, but it is managed by the owner of it. When you return the vector to the caller, the memory block behind continues to exist until a (delete, free, custom-delete) is called on it. The vector does this in it's destructor ( ~vector() ), but before that happening a vector( const vector& ) constructor call occurs that copies the content of one vector to the other. Most compilers will optimize away this operation, as mentioned by another answer.
In the case where you return the struct, the problem is the same, you are not returning an array (int[]) you are returning a pointer. If you were to contain an array inside the struct it would be successfully copied back to caller, because the restriction only applies for arrays and not for user defined types.
Try:
struct Foo {
int arr[4];
};
Foo f()
{
Foo obj{ 1, 2, 3, 4 }; // brace initialization of struct
return obj;
}
int main()
{
Foo obj = f();
for(int i = 0; i < 4; i++)
std::cout << i << ' '; // outputs 1 2 3 4 with no issues
}
As you can see no issues with memory here. That's why the std::array<> from header exists - to wrap C-Style arrays so they can be copied over.
I hope this explains well enough.
In the second method returning vector<int>. The std::vector<int> arr was copied to another a temporary vector<int>, then std::vector<int> arr was destroyed.
Finally, the vector<int> value was assigned with temporary vector<int>, after assigning completed, temporary vector<int> was destroyed as well.
That's what i think happening with your example.
Related
I'm trying to write an example code for the following line of code:
int (*(*foo)(const void*))[3];
I got this code from here (at each refresh of the website you get a different piece of code and there is a finite set of them so by reloading a couple of times you will get my example). I wanted to see out of curiosity is it even possible to write some code that would make this line work.
Here is what I wrote so far:
#include<stdio.h>
#include<malloc.h>
int* goo(const void * ptr) {
int* ret = (int*) malloc(sizeof(int) * 3);
if (!ret)
return NULL;
for (int i = 0; i < 3; ++i)
ret[i] = i;
return ret;
}
int doo() { return 0; }
int main(void) {
//NOTE: Following 2 lines have syntax errors
int (*(*foo)(const void*))[3] = &goo;
int* values = foo(goo(doo));
for (int i = 0; i < 3; ++i) {
printf("%d", values[i]);
}
free(values);
return 0;
}
If I understood correctly, foo should be some function that receives as an argument another function and returns an int array of size 3. The problem is I don't know how to make foo in this case point to a function or even how to get the int array.
Is it even possible to make this line of code work?
If I understood correctly, foo should be some function
foo is a pointer to a function.
that receives as an argument another function
No, the parameter type of the function that foo points to must be a pointer to const void i.e. const void*. A pointer to void is an object pointer. It can point to an object of any type.
Functions are not objects, but on some systems, pointers to functions can be reinterpreted as pointers to void.
and returns an int array of size 3.
No; Return types cannot be arrays. The function must return a pointer to an array of 3 ints i.e. int(*)[3].
Is it even possible to make this line of code work?
Here is an example program that uses such foo. It doesn't make much sense, but that's because the goal is to use an obscure function pointer:
#include<iostream>
int arr[] {
1,
2,
3,
};
int (*goo(const void*))[3] {
return &arr;
}
int main() {
int (*(*foo)(const void*))[3] = &goo;
int (*values)[3] = foo(nullptr);
for (int i : *values) {
std::cout << i << " ";
}
}
We can greatly improve readability by introducing type aliases:
using IntArr3 = int[3];
using FooFun = IntArr3*(const void*);
IntArr3* goo(const void*) {
return &arr;
}
int main() {
FooFun* foo = &goo;
IntArr3* values = foo(nullptr);
for (int i : *values) {
std::cout << i << " ";
}
}
I am trying to understand how unique pointers and move semantics. Now, I have created a dummy example below to show what the issue is. My question is, why does this code throw a pointer being freed was not allocated error?:
#include <iostream>
#include <memory>
using namespace std;
template <class T>
class Object
{
public:
T *values = nullptr;
int size;
Object(int size) : size(size)
{
values = new T[size];
}
~Object()
{
delete[] this->values;
}
void myFunc(T *&&new_values)
{
cout << "myFunc called!" << endl;
delete[] this->values;
values = new_values;
}
void print()
{
for (int i = 0; i < size; i++)
cout << this->values[i] << " ";
cout << endl;
}
};
int main()
{
auto my_object = new Object<int>(4);
std::unique_ptr<Object<int>> my_other_object(new Object<int>(4));
int values[4] = {1, 2, 3, 4};
int my_other_values[4] = {10, 20, 30, 40};
/* This works all fine! */
my_object->myFunc(std::move(values));
my_object->print();
/* This next bit throws pointer being freed was not allocated */
my_other_object->myFunc(std::move(my_other_values));
my_other_object->print();
}
This has nothing to do with std::unique_ptr. The reason you get the "pointer being freed was not allocated" error is because the shown code is trying to delete something that was not newed.
delete[] this->values;
values = new_values;
This properly deletes the old values, but then just blindly sets values to point to something that was never newed (the new_values that get passed into here are not newed anywhere).
Hence, later, this object's destructor attempts to delete what's now in values, but it was never newed (it just points to some static array) and you get the runtime error.
I have absolutely no clue, what the difference is between the two following examples:
void function(int *p) {
p++;
}
int main() {
int values[] = {1,2,3};
int *p = values;
function(p);
cout << *p;
return 0;
}
This one returns "1".
Whereas a slight modification yields "2" (which is the wanted result):
int main() {
int values[] = {1,2,3};
int *p = values;
p++;
cout << *p;
return 0;
}
Where lies the problem? Is it due to passing by reference or incrementing?
The issue here is
void function(int *p) {
p++;
}
Is using pass by value - not pass by reference. Since the pointer is passed by value any change you make to the pointer itself is not reflected in the call site. If you need to modify where the pointer points then you need to pass it by reference like
void function(int*& p) {
p++;
}
Now when you increment it will point to the second element like it does in your second example.
In this example
void function(int *p) {
p++;
}
int main() {
int values[] = {1,2,3};
int *p = values;
function(p);
cout << *p;
return 0;
}
You are passing a pointer by value, which means you simply pass a copy of the address the pointer is pointing at. You then proceed to increment the function's local copy of that pointer and then exit the function. This has no effect on the original pointer as you incremented a local copy.
In this example, however
int main() {
int values[] = {1,2,3};
int *p = values;
p++;
cout << *p;
return 0;
}
You directly increment your pointer, which means it is now pointing at the next element in the array.
In the first case the value of address is passed by value.
function(p) ==> void function(int *p){}
The 'p' on right side a local variable. So any modification to the pointer will be visible only inside function.
I'm new at using vector, I need a method that return the addres of the vector, this is the semplifide code:
class:
#include<vector>
class A
{
private:
std::vector<int> v;
public:
A()
{
for (int i = 0; i < 5; i++)
v[i] = i;
}
std::vector<int> give()
{
return v;
}
~A() {}
};
main:
#include <iostream>
#include "class.cpp"
int main(int argc, char **argv)
{
A a;
int *vptr = NULL;
vptr = &a.give()[0];
std::vector<int> b;
return 0;
}
this code compile but during the execution a segmentation fault is given
This constructor
A(){
for(int i = 0; i < 5; i++)
v[i] = i;
}
is invalid because the vector has no yet elements that you could use the subscript operator. More correctly would be to write
A() : v( 5 ) {
for(int i = 0; i < 5; i++)
v[i] = i;
}
As for your question then std::vector has member function data that returns the address of its internal buffer with elements.
So you could write
int * give(){ return v.data(); }
or
const int * give() const { return v.data(); }
In main you could write
int *vptr = a.give();
You have several big problems here.
First, in the constructor you're trying to access elements of the vector that don't yet exist:
A(){
for(int i = 0; i < 5; i++)
v[i] = i;
}
vector isn't the same as map where if the element you are looking for with operator[] doesn't exist a new one is created. In vector if you use operator[] with an index that doesn't exist you get Undefined Behavior, and a verly likely crash. You should be inserting items here. On way is push_back:
A(){
for (int i = 0; i < 5; ++i)
v.push_back (i);
}
Next, give() returns a vector by-value:
std::vector<int> give(){
return v;
}
Which makes a copy of v and returns that copy. Well that's technically OK, but when you call give() you don't assign the return value to a variable. What ends up happening is the copy of the vector becomes a temporary object. As soon as the so-called "full-expression" where the temporary was created is done, the temporary is destroyed.
That full-expression is this:
vptr = &a.give()[0];
So, vptr was assigned the address of the first element of that temporary vector, but as soon as the end of this expression is reached that vector is destroyed and vptr now points to hyperspace. If you attempt to dereference this wild pointer, you get Undefined Behavior and a very likely segmentation fault.
To fix this, you could let A tell you the address of the first element of the vector:
class A
{
// [...]
public:
void* give() { return v.data(); }
};
I have the next classes:
class A {
};
class B : public A {
int num;
};
in my main I have:
int main() {
A* vec; // A is a class with pure virtual functions
vec = new B[2]; // want to create a vector of B
}
vec[0] is defined correctly, but vec[1] is NULL. why didn't it allocate me a fit memory?
I don't want to change the lines of the main. just make it working.
(I know I can change the main into: B* vec = new B[2] but I don't want)
any help appreciated!
You cannot treat arrays polymorphically, the C++ language does not support it. The expression vec[x] uses pointer arithmetic to determine the location of the element. If you are accessing it through a base class pointer it will not work if the size of the objects vary in any way.
For example, you have base class that is 4 bytes in size and the subclass is 8 bytes in size.
base *a = new child[4];
When you access a[1] the compiler calculates the offset using the size of the base class. In this case the offset is 4 bytes which ends up pointing to the middle of the first element.
I recommend using a std::vector or std::array of pointers with an appropriate smart pointer.
// For arrays that needs to be resized (requires delete for each new)
std::vector<A*> vec(5, NULL);
for(int i = 0; i < vec.size(); i++)
{
vec[i] = new B();
}
// for arrays that are fixed in size (requires delete for each new)
std::array<A*, 5> vec;
for(int i = 0; i < vec.size(); i++)
{
vec[i] = new B();
}
// for arrays that are fixed in size with smart pointers
// no delete needed
std::array<std::unique_ptr<A>, 5> vec;
for(int i = 0; i < vec.size(); i++)
{
vec[i].reset(new B());
}
if you would like it to be polymorphic just create an array of pointers
new A*[array_size]
This code snippet illustrates the problem you are having.
#include <iostream>
using namespace std;
class A {
};
class B : public A {
int num;
};
int main() {
A* vec; // A is a class with pure virtual functions
vec = new B[2]; // want to create a vector of B
cout << sizeof(vec) << endl;
cout << sizeof(*vec) << endl;
cout << sizeof(vec[2]) << endl;
cout << sizeof(new B()) << endl;
}
In pointer arrithmetic, the size of the type of the pointer you allocated is what is used for incrementing, not the size of the true type of the object it is pointing to. More simply, the language does not support polymorphic arrays. This is simply an explanation of why.