map contains - returns whether or not a key exists inside the map.
mapPut - Gives a specific key a given value and adding it to the map by order, if the key exists, the value is overridden.
mapRemove - Removes a pair of (key, data) elements for which the key matches a given element (by the key compare function).
mapGetFirst - Sets the internal iterator to the first key in the map, and returns it.
MapKeyElement mapGetFirst(Map map){
if(map == NULL){
return NULL;
}
if (map->head == NULL){
return NULL;
}
map->iterator = map->head;
return (map->copyMapKeyElements(map->iterator->key));
}
mapGetNext - Advances the internal iterator to the next key and
returns it.
MapKeyElement mapGetNext(Map map){
if(map == NULL){
return NULL;
}
if((map->iterator->next)== NULL) {
return NULL;
}
map->iterator = map->iterator->next;
return (map->copyMapKeyElements(map->iterator->key));
}
typedef struct MapElements_t{
MapDataElement data;
MapKeyElement key;
struct MapElements_t* next;
} *MapElements;
struct Map_t{
copyMapDataElements copyMapDataElements;
copyMapKeyElements copyMapKeyElements;
freeMapDataElements freeMapDataElements;
freeMapKeyElements freeMapKeyElements;
compareMapKeyElements compareMapKeyElements;
MapElements head;
MapElements iterator;
};
/* ...... */
MapResult mapPut(Map map, MapKeyElement keyElement, MapDataElement dataElement) {
if ((map == NULL) || (keyElement == NULL) || (dataElement == NULL)) {
return MAP_NULL_ARGUMENT;
}
if (mapContains(map, keyElement)) {
mapRemove(map, keyElement);
}
MapElements new_map_element = malloc(sizeof(new_map_element));
if (new_map_element == NULL) {
return MAP_OUT_OF_MEMORY;
}
new_map_element->data = dataElement;
new_map_element->key = keyElement;
new_map_element->next = NULL;
if(map->head == NULL){
map->head = new_map_element;
map->iterator = map->head;
return MAP_SUCCESS;
}
mapGetFirst(map);
if (map->compareMapKeyElements(keyElement, map->iterator->key) < 0){
new_map_element->next = map->iterator;
map->head = new_map_element;
return MAP_SUCCESS;
}
while(map->iterator->next != NULL) {
if (map->compareMapKeyElements(keyElement, map->iterator->next->key) < 0) {
new_map_element->next = map->iterator->next;
map->iterator = new_map_element;
return MAP_SUCCESS;
}
mapGetNext(map);
}
map->iterator->next = new_map_element;
return MAP_SUCCESS;
}
You have typedefs that include a pointer, such as typedef struct MapElements_t{...} *MapElements; which makes the type MapElements a pointer.
This is discouraged and for the following reason:
When you do
MapElements new_map_element = malloc(sizeof(new_map_element));
you are allocating the size of a pointer, not the size of the thing pointed to. In your case you should do:
MapElements new_map_element = malloc(sizeof(*new_map_element));
but preferably you would do:
typedef struct MapElements_t
{
//...
struct MapElements_t* next;
} MapElements;
so you make a variable that is a pointer to the thing have explicitly the *.
MapElements *new_map_element = malloc(sizeof(*new_map_element));
The fault was here; I have to replace this code:
new_map_element->data = dataElement;
new_map_element->key = keyElement;
with this code:
new_map_element->data = map->copyMapDataElements(dataElement);
new_map_element->key = map->copyMapKeyElements(keyElement);
Related
This is my code:
#include <stdlib.h>
typedef struct _node {
int data;
struct _node* next;
} llnode;
llnode* llgetindex(llnode** header, int index){
if(*header == NULL) return NULL;
llnode* i = *header;
llnode* j = *header;
if(index < 0){
// negative indexes
while(index < 0){
if(i == NULL) return NULL;
i = i->next;
index++;
}
while(i != NULL){
i = i->next;
j = j->next;
}
return j;
} else {
//positive indexes
while(index > 0){
if(i == NULL) return NULL;
i=i->next;
index--;
}
return i;
}
}
llnode* push(llnode** header, int data){
if(*header == NULL) {
llnode* item = malloc(sizeof(llnode));
item->data = data;
item->next = NULL;
*header = item;
return item;
} else {
llnode* item = llgetindex(header, (-1));
item->next = malloc(sizeof(llnode));
item->next->data = data;
item->next->next = NULL;
return item->next;
}
}
llnode* pop(llnode** header){
if(*header == NULL) return NULL;
llnode* last = NULL;
// if list is one item long
if((*header)->next == NULL){
last = *header;
*header = NULL;
} else {
llnode* blast = llgetindex(header,(-2));
printf("[0]%lli\n",*header);
printf("[1]%lli\n",(*header)->next);
last = blast->next;
blast->next = NULL;
}
return last;
}
I've traced the problem to the function llgetindex; it somehow is overwriting the pointer to the first element of the list and changing it to what llgetindex should return when given a negative index, but i don't get how.
The strangest thing is that calling push works fine, pop doesn't and calling llgetindex directly also destroys the list.
If you can explain why it happens i would be happy.
To reproduce the problem:
create a llnode** pointer and call push function a few times to populate the list, after call either pop or llgetindex using a negative index and check the list, now the first node of the list should now be either the last if you called pop, or the element indexed by the negative index given to llgetindex.
gcc (Debian 10.2.1-6) 10.2.1 20210110
I'm making a generic map in c (using clion) and in my nodeDestroy function I call a generic freeing function to free the key and data of the node list.
nodeDestroy:
void nodeDestroy(Node node) {
Node current = node;
Node next;
while (current != NULL) {
next = current->next;
current->free_key(current->key);
current->key = NULL;
current->free_data(current->data);
current->data = NULL;
free(current);
current = next;
}
node = NULL;
}
this is the node struct:
struct node_t{
MapKeyElement key;
MapDataElement data;
Node next;
copyMapKeyElements copy_key;
copyMapDataElements copy_data;
freeMapKeyElements free_key;
freeMapDataElements free_data;
};
I am testing the map with integer pointers (the data and key of each node are int*) and this is the integer freeing function:
static void freeInt(MapKeyElement e) {
free((int*)e);
}
free_key and free_data are given to me in nodeCreate and both are set to freeInt. When I check this in valgrind the key and data of each node aren't freed. any idea?
So I noticed the problem occurs only when using another function: mapPut. I'll add implementation of functions required to understand the code and some things I didn't add before.
/** Data element data type for map container */
typedef void* MapDataElement;
/** Key element data type for map container */
typedef void* MapKeyElement;
/** Type of function for copying a data element of the map */
typedef MapDataElement(*copyMapDataElements)(MapDataElement);
/** Type of function for copying a key element of the map */
typedef MapKeyElement(*copyMapKeyElements)(MapKeyElement);
/** Type of function for deallocating a data element of the map */
typedef void(*freeMapDataElements)(MapDataElement);
/** Type of function for deallocating a key element of the map */
typedef void(*freeMapKeyElements)(MapKeyElement);
struct Map_t{
Node iterator;
copyMapKeyElements copy_key;
copyMapDataElements copy_data;
freeMapKeyElements free_key;
freeMapDataElements free_data;
compareMapKeyElements compare_keys;
Node head;
};
bool mapContains(Map map, MapKeyElement element){
if (map == NULL || element == NULL){
return false;
}
mapGetFirst(map);
if (getKey(map->iterator) != NULL && map->compare_keys(getKey(map->iterator)
, element) == 0){
return true;
}
while (mapGetNext(map) != NULL){
if (getKey(map->iterator) != NULL &&
map->compare_keys(getKey(map->iterator), element) == 0){
return true;
}
}
return false;
}
MapResult mapPut(Map map, MapKeyElement keyElement, MapDataElement dataElement){
if (map == NULL || keyElement == NULL || dataElement == NULL){
return MAP_NULL_ARGUMENT;
}
MapDataElement data_copy = map->copy_data(dataElement);
if (data_copy == NULL){
return MAP_OUT_OF_MEMORY;
}
MapResult result = MAP_SUCCESS;
if (mapContains(map,keyElement) == true){
mapGetFirst(map);
while (map->iterator != NULL){
if (map->compare_keys(keyElement, getKey(map->iterator)) == 0) {
map->free_data(getData(map->iterator));
setData(map->iterator, data_copy);
return setIterator(map, MAP_SUCCESS);
}
map->iterator = getNext(map->iterator);
}
}
mapGetFirst(map);
MapKeyElement key_copy = map->copy_key(keyElement);
if (key_copy == NULL){
map->free_data(data_copy);
return setIterator(map, MAP_OUT_OF_MEMORY);
}
if (getKey(map->iterator) == NULL){
setKey(map->iterator, key_copy);
setData(map->iterator, data_copy);
return setIterator(map, MAP_SUCCESS);
}
Node new_node = nodeCreate(map->copy_data, map->copy_key,
map->free_data, map->free_key, &result);
if (result == MAP_OUT_OF_MEMORY) {
map->free_data(data_copy);
map->free_key(key_copy);
return setIterator(map, MAP_OUT_OF_MEMORY);
}
setData(new_node, data_copy);
setKey(new_node, key_copy);
if (map->compare_keys(key_copy, getKey(map->iterator)) < 0){
setNext(new_node,map->iterator);
map->head = new_node;
return setIterator(map, MAP_SUCCESS);
}
while(map->iterator != NULL) {
if (getNext(map->iterator) == NULL){
setNext(map->iterator, new_node);
break;
}
if (map->compare_keys(key_copy, getKey(getNext(map->iterator))) < 0){
Node temp_next = getNext(map->iterator);
setNext(map->iterator, new_node);
setNext(new_node, temp_next);
break;
}
map->iterator = getNext(map->iterator);
}
return setIterator(map, MAP_SUCCESS);
}
static MapResult setIterator(Map map, MapResult result){
map->iterator = NULL;
return result;
}
MapKeyElement mapGetFirst(Map map){
if (map == NULL){
return NULL;
}
map->iterator = map->head;
return getKey(map->head);
}
i am having a method implemented in which i have as arguments a doubly linked list and the data each node has. i want to return a value of the active element in the list.
My code is:
void DLCopy (tDLList *L, int *val) {
tDLElemPtr pointer=NULL;
if(L->Act!=NULL)
{
pointer=(tDLElemPtr) malloc(sizeof(struct tDLElem));
if (pointer == NULL)
{
pointer->data=val;
pointer->rptr = L->Act->rptr;
pointer->lptr = L->Act;
L->Act->rptr = pointer;
L->Act->lptr = NULL;
}
else
{
DLError();
}
}
}
any ideas??
it gives me return value = -2 no matter what.
thanks in advance.
If all you're trying is to get the active element's value it just seems to be simply:
void DLCopy (tDLList *L, int *val)
{
if (L != NULL && L->Act != NULL && val != NULL)
{
*val = L->Act->data;
}
else if (val != NULL)
{
*val = 1234; // Default value if no active element (if desired)
}
}
Note: DLCopy() is not a good name for this function.
In this video (a section/recitation for an online course called CS50), around 1h00m00s, the student instructor goes into pointers-to-pointers and why it is more efficient useful to implement insert on the binary tree this way. At least, that is what I'm getting from the argument.
I did a recursive implementation both ways. I don't see why Option A is better than Option B in the below...perhaps you could help me reason this out or point me in the right direction if I'm misunderstood?
Option A (with pointers-to-pointers)
bool insert(int value, node* tree)
{
node** tmptree = &tree;
// make sure the tree itself isn't null
if(*tmptree != NULL)
{
if(value == (*tmptree)->value)
{
return false;
}
else if(value < (*tmptree)->value)
{
tmptree = &(*tmptree)->left;
// we must be at a null leaf!
if(*tmptree == NULL)
{
// make sure we built a leaf
*tmptree = build_node(value);
if(*tmptree == NULL)
{
return false;
}
return true;
}
else
{
return insert(value, *tmptree);
}
}
else
{
tmptree = &(*tmptree)->right;
if(*tmptree == NULL)
{
*tmptree = build_node(value);
if(*tmptree == NULL)
{
return false;
}
return true;
}
else
{
return insert(value, *tmptree);
}
}
}
return false; // if the tree is null
}
Option B (regular ptrs)
bool insert(int value, node* tree)
{
if(tree != NULL)
{
if(value == tree->value)
{
return false;
}
else if(value < tree->value)
{
if(tree->left == NULL)
{
node* tmp = build_node(value);
if(tmp != NULL)
{
tree->left = tmp;
return true;
}
return false;
}
else
{
return insert(value, tree->left);
}
}
else
{
if(tree->right == NULL)
{
node* tmp = build_node(value);
if(tmp != NULL)
{
tree->right = tmp;
return true;
}
return false;
}
else
{
return insert(value, tree->right);
}
}
}
return false; // if the tree is null
}
The build_node function:
node* build_node(int value)
{
node* node = malloc(sizeof( node ));
if(node == NULL)
return NULL;
node->value = value;
node->left = NULL;
node->right = NULL;
return node;
}
I believe you have misunderstood why there is a pointer-to-pointer in the original code. The "Option a" doesn't make any sense, there is no advantage of using pointer-to-pointer just for the sake of it.
The only reason you would use pointer-to-pointer is because you want to change the pointed-to address and return it to the caller.
For example
void func (int** ptr)
{
*ptr = something;
}
func(&my_ptr);
// is the very thing same as
int* func (int* ptr)
{
return something;
}
my_ptr = func(my_ptr);
You can't use the second version and type ptr = something inside the function, because ptr is a local variable and will cease to exist once you leave the function. Assigning something to it won't affect the original pointer at the caller's side.
The only advantage of the pointer-to-pointer version is that the returned type can be used for something else, like for example returning an error code.
And that seems to be exactly why the guy in that video used it. His function looks like
bool insert (int value, node** tree);
where the *tree is assigned to point at another address from inside the function and the bool is used as a status code, when he reaches the end of a tree branch.
I have the following data types:
formation.h
typedef struct formation_t {
Player players[FORMATION_NUM_PLAYERS];
int numPlayers; /* How many players are in the above array */
int timesPlayed;
int timesWon;
}* Formation;
team.h
typedef struct team_t {
char* name;
char* coachName;
Formation* formations;
int currFormations;
int maxFormations;
}* Team;
And the following functions:
Team teamCreate(char* name, char* coach, int maxFormations)
{
//Check if parameters make sense.
if (name == NULL || coach == NULL || maxFormations < 1)
{
return NULL;
}
//Try allocating memory for name.
char* teamName = malloc(strlen(name) + 1);
if (teamName == NULL)
{
return NULL;
}
strcpy(teamName, name);
//Try allocating memory for coachName.
char* coachName = malloc(strlen(coach) + 1);
if (coachName == NULL)
{
free(teamName);
return NULL;
}
strcpy(coachName, coach);
//Try allocating memory for formations.
Formation* formations = malloc(sizeof(Formation) * maxFormations);
if (formations == NULL)
{
free(teamName);
free(coachName);
return NULL;
}
//Try allocating memory for team.
Team newTeam = malloc(sizeof(struct team_t));
if (newTeam == NULL)
{
free(teamName);
free(coachName);
free(formations);
return NULL;
}
//Initialize newly created team.
newTeam->name = teamName;
newTeam->coachName = coachName;
newTeam->maxFormations = maxFormations;
newTeam->currFormations = 0;
//Return created team.
return newTeam;
}
TeamResult teamAddFormation(Team team, Formation formation)
{
//Check for TEAM_NULL_ARGUMENT.
if (team == NULL | formation == NULL)
{
return TEAM_NULL_ARGUMENT;
}
//Check for TEAM_IS_FULL.
if (team->currFormations == team->maxFormations)
{
return TEAM_IS_FULL;
}
//Add formation.
printf("\n -about to clone- \n");
team->formations[team->currFormations] = formationClone(formation);
printf("\n -clone completed- \n");
team->currFormations = team->currFormations + 1;
return TEAM_SUCCESS;
}
Formation formationClone(Formation formation)
{
if (formation == NULL)
{
return NULL;
}
Formation newFormation = malloc(sizeof(struct formation_t));
if (newFormation == NULL)
{
return NULL;
}
*newFormation = *formation;
return newFormation;
}
When I try to test my work using the following code, I get a segmentation fault right after "about to clone".
Team team = teamCreate("Ac Milan", "Carletto", 2);
Formation formation1 = formationCreate();
ASSERT_NULL_ARGUMENT(teamAddFormation(NULL, formation1));
ASSERT_SUCCESS(teamAddFormation(team, formation1));
In teamCreate() you never set your formations local variable into your Team structure after allocating it.
This is first:
//Try allocating memory for formations.
Formation* formations = malloc(sizeof(Formation) * maxFormations);
if (formations == NULL)
{
free(teamName);
free(coachName);
return NULL;
}
then you do this after allocation the host-object:
//Initialize newly created team.
newTeam->name = teamName;
newTeam->coachName = coachName;
newTeam->maxFormations = maxFormations;
newTeam->currFormations = 0;
//Return created team.
return newTeam;
You never save the formation pointer to the structure member, so that pointer member is indeterminate and using it is invoking undefined behavior.
Add this to the bottom of that stack of assignments:
newTeam->formations = formations;
//Try allocating memory for name.
char* teamName = malloc(strlen(name) + 1);
if (teamName == NULL)
{
return NULL;
}
strcpy(teamName, name);
teamName is pointing to an alocated char array. This array never deleted. This lead to a memory leak.