Realloc pointer in C++ that was malloced() in C - c++

I'm writing a library in C++ that is supposed to interop with other C code/libraries.
If a client passes an array that was malloc()'d in C into a library function, can the C++ function safely call realloc() on that memory block and return it (leaving it up to the C caller to free() it?
Or does C++ use a different implementation and bookkeeping than C and they are incompatible.
Here's some example code:
C code:
c_header.h:
struct my_list {
int count;
int capacity;
long* array;
};
c_code.c:
#include "c_header.h"
#include "ccp_header.h"
int some_method() {
struct my_list list1;
list1.count = 0;
list1.array = malloc(sizeof(long)*5);
list1.capacity = 5;
int ret = cpp_extend_list(&list1) // todo: error checking etc...
free(list1.array);
}
C++ code:
#include "c_header.h"
int cpp_extend_list(struct my_list *list) {
long* temp = realloc(list->array, sizeof(long) * (list->capacity + 10));
if (!temp) return -1;
list->array = temp;
list->capacity+=10;
return 0;
}

Related

How to return a pointer to a ragged array through a function argument?

I need to modifiy an existing API and basically the only option I have is returning a pointer to a ragged array through a function argument (this would normally not be me personal preference). I can't get my head around doing this and keep getting a segmentation fault at this part of the code:
void getMxByArg(int ***pppX) {
*pppX = m_ppMx; // SEGMENTATION FAULT HERE
}
I've provided an example below which doesn't have any external dependencies and encapsulates the problem.
#include <stdint.h>
#include <iostream>
using namespace std;
class Mx {
public:
Mx() {
int *buff01 = (int*)malloc(3 * sizeof(int));
int *buff02 = (int*)malloc(3 * sizeof(int));
buff01[0] = 0;
buff01[1] = 1;
buff01[2] = 3;
buff02[0] = 4;
buff02[1] = 5;
buff02[2] = 6;
m_n = 2;
m_ppMx = (int**)malloc(m_n * sizeof(int*));
m_ppMx[0] = buff01;
m_ppMx[1] = buff02;
}
~Mx() {
for (int i=0; i<m_n; ++i) {
free(m_ppMx[i]);
}
free(m_ppMx);
}
int** getMx() {
return m_ppMx;
}
void getMxByArg(int ***pppX) {
*pppX = m_ppMx; // SEGMENTATION FAULT HERE
}
private:
int **m_ppMx;
int m_n;
};
int main()
{
Mx mx;
// SUCCESS
int **ppX = mx.getMx();
// FAILURE, Results in segmentation fault in getMxByArg
int ***pppX;
mx.getMxByArg(pppX);
return 0;
}
In the posted code, you are dereferencing an uninitialized pointer. That's cause for undefined behavior.
The solution is:
Create a variable of type int**.
Pass the address of that variable to the function.
int **ppX2;
mx.getMxByArg(&ppX2);
Another option is to change the argument type to int**&.
void getMxByArg(int**& ppX) {
ppX = m_ppMx;
}
Then, you can use:
int **ppX2;
mx.getMxByArg(ppX2);

C functions in c++ [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
#include <iostream>
#include <sstream>
#include "blocknode.h"
using namespace std;
class MemoryManager
{
public:
MemoryManager(unsigned int memsize);
unsigned char * malloc(unsigned int request);
void free(unsigned char * blockptr);
blocknode *getFirstPtr();
friend ostream & operator<<(ostream & out,const MemoryManager &M);
private:
unsigned int memsize;
unsigned char *baseptr;
blocknode * firstBlock;
void mergeForward(blocknode *p);
void splitBlock(blocknode *p,unsigned int chunksize);
};
Here is the BLOCKNODE.h file
#include <iostream>
using namespace std;
struct blocknode
{
unsigned int bsize;
bool free;
unsigned char *bptr;
blocknode *next;
blocknode *prev;
blocknode(unsigned int sz,unsigned char *b,bool f=true,blocknode
*p=0,blocknode *n=0):
bsize(sz),free(f),bptr(b),prev(p),next(n) {}
};
CPP FILE
#include <cassert>
#include <iostream>
#include <sstream>
#include <string>
#include "MemoryManager.h"
using namespace std;
ostream & operator<<(ostream & out,const MemoryManager &M)
{
blocknode *tmp = M.firstBlock;
assert(tmp);
while(tmp)
{
out << "[" << tmp->bsize << ",";
if (tmp->free)
out << "free] ";
else
out << "allocated] ";
if (tmp->next)
out << " -> ";
tmp = tmp->next;
}
return out;
}
MemoryManager::MemoryManager(unsigned int memtotal): memsize(memtotal)
{
baseptr = new unsigned char[memsize];
firstBlock = new blocknode(memsize,baseptr);
}
blocknode *MemoryManager::getFirstPtr()
{
return firstBlock;
}
unsigned char * MemoryManager::malloc(unsigned int request)
// Finds the first block in the list whose size is >= request
// If the block's size is strictly greater than request
// the block is split, with the newly create block being free.
// It then changes the original block's free status to false
{
blocknode * tmp = this->firstBlock;
assert(tmp);
while (tmp){
if (tmp->bsize >= request){
if (tmp->bsize > request){
splitBlock(tmp, request);
return tmp->bptr;
}
tmp->free = false;
return tmp->bptr;
}
tmp = tmp->next;
}
}
void MemoryManager::splitBlock(blocknode *p, unsigned int chunksize)
// Utility function. Inserts a block after that represented by p
// changing p's blocksize to chunksize; the new successor node
// will have blocksize the original blocksize of p minus chunksize and
// will represent a free block.
// Preconditions: p represents a free block with block size > chunksize
// and the modified target of p will still be free.
{
if (p->free == false || p->bsize <= chunksize) {
cout << "Error splitting memory....exiting with error code 1" << endl;
exit(1);
}
blocknode * heap = new blocknode(p->bsize,p->bptr + chunksize,true,0,0);
heap->bsize = p->bsize - chunksize;
heap->prev = p;
p->bsize = chunksize;
p->next = heap;
}
void MemoryManager::mergeForward(blocknode *p)
// merges two consecutive free blocks
// using a pointer to the first blocknode;
// following blocknode is deleted
{
blocknode * tmp = p->next;
p->bsize += p->next->bsize;
p->next = tmp->next;
tmp->next->prev = p;
delete tmp;
}
void MemoryManager::free(unsigned char *blockptr)
// makes the block represented by the blocknode free
// and merges with successor, if it is free; also
// merges with the predecessor, it it is free
{
blocknode * tmp = this->firstBlock->next;
assert(tmp);
while (tmp) {
if (tmp->bptr == blockptr) {
tmp->free = true;
if (tmp->free == true && tmp->next->free == true) {
mergeForward(tmp);
}
if (tmp->free == true && tmp->prev->free == true) {
mergeForward(tmp->prev);
}
}
}
}
The goal of this program is to pretty much simulate the C heap manager which deals with malloc() and free(). I am having trouble with the last four functions of the memory manager cpp file. (refer to the comments) The code compiles however my program crashes during runtime, it says that there is an unhanded exception at memory location XxXXXXXXX does anyone know what is causing this? Line 110 ("if(tmp->next->free == true)") is where the program breaks
When MemoryManager::free() calls mergeForward() (the first call to mergeForward()) as a result of what happens in mergeForward(), it looks like the tmp pointer used by free() will no longer be valid, because mergeForward() deleted it.
The derefence of tmp, immediately afterwards, will result in undefined behavior.
This is in addition to the other bug in free() that I noted in the comments.

How do I initialize this variable-size struct?

How do I initialize the variable-size C++ struct "StructB" that is declared below:
#include <iostream>
#include <string>
typedef struct {
char* a;
int b;
} StructA;
typedef struct {
StructA* pointers[];
int pcount;
int numbers[];
int ncount;
} StructB;
int main()
{
StructB *sb = new StructB; // I need 'sb' to be allocated in the heap
sb->pcount = 5;
sb->ncount = 3;
sb->pointers = new StructA*[sb->pcount];
sb->numbers = int[sb->ncount];
}
I got these compiler errors. What do these error means and how do I fix them? THank you.
In function 'int main()':
21:43: error: incompatible types in assignment of 'StructA**' to 'StructA* [0]'
22:19: error: expected primary-expression before 'int'
22:19: error: expected ';' before 'int'
listen this is not the good c++ way, you are doing C in a C++ environment
which is OK if it gives you some advantage (IO, typechecking, etc.) or for learning.
Dynamic allocation of memory in C style is entirely manual. So is deallocation.
Since you are using typedef, typedef can make things clearer for you and for the compiler.
Here is a tortured example which I think does what you want.
Notice that it is done "The Hard Way" which is actually the easy way in this case of
C style coding in C++;
#include <iostream>
#include <string>
typedef struct {
char* a;
int b;
} StructA;
typedef StructA* A_p; // A_p is "pointer to structA" (contains address of struct)
typedef A_p* A_p_Array; // A_p_Array is "pointer to A_p" (aka StructA**) contains address of A_p
typedef int* int_Array; // int_Array is "pointer to int" contains address of integer
typedef struct {
A_p_Array A_pointers;
int pcount;
int_Array numbers;
int ncount;
} StructB;
int main()
{
int i;
StructA *sa = new StructA;
StructB *sb = new StructB;
sb->pcount = 5;
sb->ncount = 3;
A_p_Array tmp_A_array;
A_p tmp;
for ( i = 0 ; i < sb->pcount; i++)
{
tmp_A_array = new A_p; // create a StructA pointer
tmp = new StructA; // create a StructA
tmp_A_array = &tmp; // put address of tmp in mp_A_array
tmp_A_array++; // increment array address
}
sb->A_pointers = tmp_A_array; // A_pointers now contains address of dynamically created array
tmp_A_array = NULL; // clear pointer but do NOT delete!
int_Array tmp_i_array;
for ( i = 0 ; i < sb->ncount; i++)
{
tmp_i_array = new int(0); // c++ can create ints with initial values
tmp_i_array++;
}
sb->numbers = tmp_i_array;
tmp_i_array = NULL; // clear pointer but do NOT delete!
/****** USE Structs A & B *****/
// clean up the heap
A_p Ap;
for ( i = 0 ; i < sb->pcount; i++)
{
Ap = sb->A_pointers[i];
delete Ap; // each struct released separately
}
int* ip;
for ( i = 0 ; i < sb->ncount; i++)
{
ip = & sb->numbers[i];
delete ip; //each int released separately
}
delete sb;
return 0;
} // main
There are better ways to do the above
which is one reason c++ and higher languages were invented.
When you get to classes it will be somewhat easier
but you can run in to the same types of problems there
so getting pointers and pointers-to-pointers down
now will serve you in good stead later.

A shared pointer query from a novice user. I have code below that crashes when using a container of class with member shared pointers

//this is my main Method ,this was an experiment to understand shared pointer usage
#include <iostream>
#include "shared_ptrtestA.h"
int main(int argc, const char * argv[]) {
// declare a shared pointer to the class
sharedptr_testA* A = new sharedptr_testA(5);
//class has a vector , push back a new instance into the vector
A->mvect.push_back(sharedptr_testA::Aptr(new sharedptr_testA::testA(
sharedptr_testA::sharedptr_testB::Create(1) , sharedptr_testA::sharedptr_testC::Create(1)
)));
//class has a vector , push back a new instance into the vector
A->mvect.push_back(sharedptr_testA::Aptr(new sharedptr_testA::testA(
sharedptr_testA::sharedptr_testB::Create(2),sharedptr_testA::sharedptr_testC::Create(2)
)));
//iterate the vector populated above
for(std::vector<sharedptr_testA::Aptr>::iterator it = A->mvect.begin() ;
it!= A->mvect.end() ; it++)
{
// get members from the vector iterator
sharedptr_testA:: sharedptr_testB::Bptr B = (*it)->mb;
sharedptr_testA:: sharedptr_testC::Cptr C = (*it)->mc;
// print contents of members
for(int i = 0 ; i < B->m_size ; i++)
{
std::cout<<B->bytes[i]<<'\t';
}
std::cout <<std::endl;
for(int i = 0 ; i < C->m_size ; i++)
{
std::cout<<C->bytes[i]<<'\t';
}
std::cout <<std::endl;
}
}
//this was the main method above and the expected output was
B
C
BB
CC
The structure of the classes used are
//Header File
#ifndef shared_ptrtest_shared_ptrtestA_h
#define shared_ptrtest_shared_ptrtestA_h
#include <memory>
#include <functional>
#include <vector>
class sharedptr_testA
{
public:
// constructor and destructor
sharedptr_testA(int vsize);
~sharedptr_testA();
// an internal class member defined
class sharedptr_testB
{
public:
typedef std::shared_ptr<sharedptr_testB> Bptr;
//static create method
static Bptr Create(int msize)
{
return Bptr(new sharedptr_testB(msize));
}
//members
int m_size;
char *bytes;
//private contructor
private:
sharedptr_testB(int size)
{
m_size = size;
bytes = new char[size];
for(int i = 0 ; i < size ; i++)
bytes[size]= 'B';
}
};
//class c has same structure as class B above
class sharedptr_testC
{
public:
typedef std::shared_ptr<sharedptr_testC> Cptr;
static Cptr Create(int msize)
{
return Cptr(new sharedptr_testC(msize));
}
int m_size;
char *bytes;
private:
sharedptr_testC(int size)
{
m_size = size;
bytes = new char[size];
for(int i = 0 ; i < size ; i++)
bytes[size]= 'C';
}
};
// struct containing shared pointers to classes defined above
struct testA
{
testA(sharedptr_testB::Bptr B, sharedptr_testC::Cptr C)
{
mb = B;
mc = C;
}
sharedptr_testB::Bptr mb;
sharedptr_testC::Cptr mc;
};
typedef std::shared_ptr<testA> Aptr;
std::vector<Aptr> mvect;
};
#endif
//The short cpp file for the above class contains only constructor and destructor
#include "shared_ptrtestA.h"
sharedptr_testA::sharedptr_testA(int vsize)
:mvect(vsize)
{
}
sharedptr_testA::~sharedptr_testA()
{
}
What is wrong in the above code ? I wrote this to understand shared pointer usage
You have two bugs in your program:
The loops in constructors of sharedptr_testB and sharedptr_testC use size instead of i for indexing. It should be:
sharedptr_testB(int size)
{
m_size = size;
bytes = new char[size];
for(int i = 0 ; i < size ; i++)
bytes[i]= 'B';
}
(DTTO) for sharedptr_testC)
You start with a vector of size 5, which means it stores five null pointers. Then you append two elements to it (size 7, five nulls + two valid pointers). The you iterate over it, dereferencing each pointer. This of course crashes, since there are nulls at the beginning. Simply initialise the vector as empty.
sharedptr_testA* A = new sharedptr_testA(0);
With these two fixes, the code works.
Side notes 1 (C++):
The code is next to impossible to read. I strongly suggest you use a better naming scheme.
sharedptr_testB and sharedptr_testC leak memory. I understand it's just a learning excercise, I'd just like to point it out. You'd be better off with std::vector<char> in them instead of char*.
Side notes 2 (Stack Overflow):
If you have a crashing program, you should generally try to debug it yourself before asking an SO question. Stepping through the program through a debugger would easily have uncovered both the issues.

How can I prevent segmentation faults in my program?

I have a C assignment. It is a lot longer than the code shown below, and we are given the function prototypes and instructions only. I have done my best at writing code, but I am stuck with segmentation faults. When I compile and run the program below on Linux, at "735 NaN" it will terminate, indicating a segfault occurred. Why? What am I doing wrong? Basically, the program does not let me access table->list_array[735]->value and table->list_array[735]->key. This is of course the first segfault. There might be more following index 735.
#include <stdio.h>
#include <stdlib.h>
typedef struct list_node list_node_t;
struct list_node
{
char *key;
int value;
list_node_t *next;
};
typedef struct count_table count_table_t;
struct count_table {
int size;
list_node_t **list_array;
};
count_table_t* table_allocate(int size)
{
count_table_t *ptr = malloc(sizeof(count_table_t));
ptr->size = size;
list_node_t *nodes[size];
int k;
for(k=0; k<size; k++){
nodes[k] = NULL;
}
ptr->list_array = nodes;
return ptr;
}
void table_addvalue(count_table_t *table)
{
int i;
for(i=0; i<table->size; i++)
{
table->list_array[i] = malloc(sizeof(list_node_t));
table->list_array[i]->value = i;
table->list_array[i]->key = "NaN";
table->list_array[i]->next = NULL;
}
}
int main()
{
count_table_t *table = table_allocate(1000);
table_addvalue(table);
int i;
for(i=0; i<table->size; i++)
printf("%d %s\n", table->list_array[i]->value, table->list_array[i]->key);
return 0;
}
You're point ptr->list_array at a local variable (nodes) in table_allocate, which goes away when that function returns, leaving a dangling pointer. You probably want
list_node_t **nodes = malloc(size * sizeof(list_node_t *));
I recommend the routine use of valgrind(1) to prevent such problems from occurring.