How to handle the assignment operation for structures with pointers? - c

I have a structure definition which I need to control the assignment operator for. Here is some example code that doesn't work how I want yet.
struct jsonobject {
struct jsonobject* children[100];
};
int main(int argc, const char * argv[]) {
struct jsonobject obj;
struct jsonobject obj2;
obj2 = obj; // Problem is here.
return 0;
}
The issue is that the line obj2 = obj performs a shallow copy of the obj when I need it to perform a deep copy. How am I supposed to handle cases like this?

memcpy(&obj2, &obj, sizeof obj) like obj2 = obj in this example creates a shallow copy (if your object contains pointers those are copied not whatever they pointer to).
You will need a (custom) function to create a deep copy. You will probably want to store number of children in your struct unless you use a sentinel (NULL).
JSON libraries ship with suitable functions, and usually, a mechanism to share data via reference counts.

You can write your own function which performs the deep copy recursively. Here is a demonstration which allocates memory for the copy using malloc:
#include <stdio.h>
#include <stdlib.h>
#define MAX_CHILDREN 5
struct jsonobject {
struct jsonobject* children[MAX_CHILDREN];
};
void deep_print( const struct jsonobject *obj );
struct jsonobject *deep_copy( const struct jsonobject *src );
void deep_free( struct jsonobject *obj );
int main( void )
{
//construct jsonobject with two layers of children
struct jsonobject child3child1 =
{
{
NULL,
NULL,
NULL,
NULL,
NULL
}
};
struct jsonobject child1 =
{
{
NULL,
NULL,
NULL,
NULL,
NULL
}
};
struct jsonobject child3 =
{
{
NULL,
&child3child1,
NULL,
NULL,
NULL
}
};
struct jsonobject root =
{
{
NULL,
&child1,
NULL,
&child3,
NULL
}
};
//deep print the source data
printf( "Deep print of source data: \n\n" );
deep_print( &root );
//perform the deep copy
struct jsonobject *copy = deep_copy( &root );
//deep print the copied data
printf( "\n\nDeep print of copied data: \n\n" );
deep_print( copy );
//cleanup
deep_free( copy );
}
void deep_print( const struct jsonobject *obj )
{
// NOTE: This function is not thread-safe, because of the use
// of this static variable.
static int depth = 0;
for ( int i = 0; i < MAX_CHILDREN; i++ )
{
//add indentation according to depth
for ( int j = 0; j < depth; j++ )
printf( "----" );
if ( obj->children[i] == NULL )
{
printf( "children[%d] is NULL\n", i );
}
else
{
printf(
"children[%d] points to %p which contains the following:\n",
i, (void*)obj->children[i]
);
depth++;
deep_print( obj->children[i] );
depth--;
}
}
}
struct jsonobject *deep_copy( const struct jsonobject *src )
{
//allocate memory for new struct
struct jsonobject *new_struct = malloc( sizeof *new_struct );
if ( new_struct == NULL )
{
fprintf( stderr, "Memory allocation failure!\n" );
exit( EXIT_FAILURE );
}
//recursively copy all children
for ( int i = 0; i < MAX_CHILDREN; i++ )
{
if ( src->children[i] == NULL )
{
new_struct->children[i] = NULL;
}
else
{
//perform a recursive deep copy
new_struct->children[i] = deep_copy( src->children[i] );
}
}
return new_struct;
}
void deep_free( struct jsonobject *obj )
{
for ( int i = 0; i < MAX_CHILDREN; i++ )
{
if ( obj->children[i] != NULL )
{
deep_free( obj->children[i] );
}
}
free( obj );
}
For demonstration purposes, I reduced MAX_CHILDREN from 100 to 5. For the code in your question, you want MAX_CHILDREN to have the value 100.
This demonstration program has the following output:
Deep print of source data:
children[0] is NULL
children[1] points to 0x7ffc58161500 which contains the following:
----children[0] is NULL
----children[1] is NULL
----children[2] is NULL
----children[3] is NULL
----children[4] is NULL
children[2] is NULL
children[3] points to 0x7ffc58161550 which contains the following:
----children[0] is NULL
----children[1] points to 0x7ffc581614b0 which contains the following:
--------children[0] is NULL
--------children[1] is NULL
--------children[2] is NULL
--------children[3] is NULL
--------children[4] is NULL
----children[2] is NULL
----children[3] is NULL
----children[4] is NULL
children[4] is NULL
Deep print of copied data:
children[0] is NULL
children[1] points to 0x604000000050 which contains the following:
----children[0] is NULL
----children[1] is NULL
----children[2] is NULL
----children[3] is NULL
----children[4] is NULL
children[2] is NULL
children[3] points to 0x604000000090 which contains the following:
----children[0] is NULL
----children[1] points to 0x6040000000d0 which contains the following:
--------children[0] is NULL
--------children[1] is NULL
--------children[2] is NULL
--------children[3] is NULL
--------children[4] is NULL
----children[2] is NULL
----children[3] is NULL
----children[4] is NULL
children[4] is NULL
As you can see, the structure of the source data was successfully copied. Also, the addresses of the children of the copied data are different to those of the source data. This proves that a deep copy, not a shallow copy, was performed.

Related

Why does my struct disapear after printing it's content?

I'am working on a project for my programming class (teoretically in C++ but our professor isn't big fan of C++ solutions and C is better viewed by him). The project is to do simple queue with type given by user and the problem is with the following code:
#include <cstdlib>
#include <cstring>
#include <stdio.h>
typedef struct
{
int nKey;
int* pTab;
}Usertype;
Usertype* AllocateUsertype( );
void PrintUsertype( Usertype* pItem );
int main()
{
Usertype *pItem = AllocateUsertype();
printf( "nKey: %d, pTab: %d %d", pItem->nKey, pItem->pTab[0], pItem->pTab[1] );
pItem->nKey = 3;
PrintUsertype( pItem );
}
Usertype* AllocateUsertype( )
{
Usertype* pItem = NULL;
int* t = NULL;
t = (int*)malloc( 2*sizeof( int ) );
if( !t ) return NULL;
memset( t, 0, 2*sizeof( int ) );
Usertype Item = { 0,t };
pItem = &Item;
return pItem;
}
void PrintUsertype( Usertype* pItem )
{
printf( "nKey: %d, pTab: %d %d", pItem->nKey, pItem->pTab[0], pItem->pTab[1] );
}
When I allocate usertype it works well and the pItem is created as expected, but after I printf it it's seemes like pItem is no longer there and there's just garbage nKey number and there isn't any tab.
Is this problem because im allocating this data struct in memory wrongly and somehow t as a local variable for AllocateUsertype disapears at random moment? If yes can someone give me idea how to do it correctly?
As pointed out in the comments, the problem is that inside AllocateUsertype() you are returning a pointer to a local variable that won't exists anymore once the function returns.
The solution is to allocate a Usertype using malloc, just like you did for t, and then return its pointer.
Usertype* AllocateUsertype( )
{
Usertype* pItem = NULL;
pItem = (Usertype*)malloc(sizeof(Usertype));
if (!pItem) return NULL;
int* t = NULL;
t = (int*)malloc( 2*sizeof( int ) );
if( !t ) return NULL;
memset( t, 0, 2*sizeof( int ) );
pItem->nKey = 0;
pItem->pTab = t;
return pItem;
}

Why does this sscanf modify some data i don't want to be modified?

I'm reading and saving strings from a formatted file and for some reason I found out sscanf() changes testa_e->ident contents.
I have put some printf around and I found that the problem occurs right after sscanf(); I have also checked addresses of temp2, temp5 and testa_e by printing them but they are different.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define string 30
#define line 100
typedef const char *identifier;
struct nodo_id {
identifier ident;
struct nodo_id *next;
};
typedef struct nodo_id nodo_id;
nodo_id *testa_e = NULL;
void addent(const char *id_ent) {
if (testa_e == NULL) {
testa_e = malloc(sizeof(nodo_id));
testa_e->ident = id_ent;
testa_e->next = NULL;
} else {
nodo_id *curs = testa_e;
while (curs != NULL) {
curs = curs->next;
}
curs = malloc(sizeof(nodo_id));
curs->ident = id_ent;
curs->next = NULL;
}
}
int main() {
char temp[line];
char temp2[string];
char temp5[string];
fgets(temp, line, stdin);
while (strncmp(temp, "end", 3) != 0) {
if (strncmp(temp, "addent", 6) == 0) {
if (testa_e != NULL)
printf("\nbefore sscanf: %s\n", testa_e->ident);
sscanf(temp, "%s %s", temp5, temp2);
if (testa_e != NULL)
printf("\nafter sscanf: %s\n", testa_e->ident);
addent(temp2);
}
fgets(temp, line, stdin);
}
}
This code here reproduces the exact same problem; after launch write addent firstword and addent secondwordon terminal and right around sscanf it should show you that testa_e->ident content has changed, I'd like to know why and how to fix this, because I seriously have no idea...
In the function addent this loop
while(curs!=NULL){
curs=curs->next;
}
iterates until curs is equal to NULL.
Then you are changing the pointer
curs=malloc(sizeof(nodo_id));
curs->ident=id_ent;
curs->next=NULL;
The list itself was not changed. You changed only the local variable curs.
Change the loop the following way
while ( curs->next != NULL ){
curs = curs->next;
}
and then
curs->next = malloc( sizeof( nodo_id ) );
curs->next->ident = id_ent;
curs->next->next = NULL;
The other problem is that you are using pointers to a local array
char temp2[string];
//...
addent(temp2);
So the last that will be stored in the array will be pointed to by all nodes. You need to allocate dynamically memory for each string that will be stored in the list and assign the address to the data member ident. In this case you have to remove the qualifier const from its declaration.
Take into account that it is a bad idea to make a function depends on a global variable,
A better definition of the function addent can look the following way
struct nodo_id{
char *ident;
struct nodo_id* next;
};
typedef struct nodo_id nodo_id;
int addent( nodo_id **head, const char *id_ent )
{
nodo_id *new_nodo_id = malloc( sizeof( nodo_id ) );
int success = new_nodo_id != NULL;
if ( success )
{
new_nodo_id->ident = malloc( strlen( id_ent ) + sizeof( ( char )'\0' ) );
success = new_nodo_id->ident != NULL;
if ( ! success )
{
free( new_nodo_id );
}
else
{
strcpy( new_nodo_id->ident, id_ent );
new_nodo_id->next = NULL;
while ( *head != NULL ) head = &( *head )->next;
*head = new_nodo_id;
}
}
return success;
}
And the function can be called like
addent( &testa_e, temo2 );
Why in the function there is used the pointer to pointer to the head?
First of all if we want to change the original head we need to pass it by reference. Secondly in the loop
while ( *head != NULL ) head = &( *head )->next;
again the data member next of the last node is pointed to by the pointer. So we are changing not the local variable curs as in your implementation of the function but the data member next of the last node. So we are changing the list itself.
Pay attention to that defining such a typedef as
typedef const char* identifier;
is a bad practice.
The main problem is (besides the one #VladFromMoscow addressed in his answer), that in addent() you only store the pointer id_ent in your struct:
curs->ident=id_ent;
But that is nothing else but the address of temp2, so if you copy something else into temp2 by calling sscanf() you'll see the new value in testa_e->ident too.
Change the line form above to
curs->ident=strdup(id_ent);
to create a copy.
And don't forget to call free(curs->ident) when before you are freeing curs

Find key in hash table C

I need help to write a function called find inside my program, which takes a key and a reference to an integer, fills in the reference with the appropriate value (if found), and returns 1 (true). If the key is not found, return 0 (false). You may assume that p already points to a valid location. So:
int find( char *key, int *p );
Can be called like this:
char *name = "Bob";
int data;
if( find( name, &data ))
printf( "Found %s. (S)he's %i\n\n", name, data );
else
printf( "\nCouldn't find %s\n\n", name );
This is the program I have:
#define TABLE_SIZE 7
#define NUM_INPUTS 7
int hash( char *s )
{
return strlen( s ) % TABLE_SIZE ;
}
typedef struct entry
{
char *key;
int val;
struct entry *next;
} entry;
entry* table[ TABLE_SIZE ] = { NULL };
void insert( char *s, int v )
{
int h = hash( s );
entry *t = (entry*) malloc( sizeof( entry ));
t->key = s;
t->val = v;
t->next = table[h];
table[h] = t;
}
int main()
{
char* keyList[] = { "Jaga", "Jesse", "Cos", "Kate", "Nash", "Vera",
"Bob" };
int valList[] = { 24, 78, 86, 28, 11, 99, 38 };
int i;
for( i=0; i<NUM_INPUTS; ++i )
insert( keyList[i], valList[i] );
/* Probably call function find right here */
return( 0 );
}
int find( char *key, int *p_ans )
{
}
Thank you.
Firstly you need to write a keycomp function prototype
int keycomp (struct data left,struct data right);
Return 0 for equals, -1 for L>R and 1 for R>L.
From the origin in your data space chain through comparing the test key to the stored keys.
Because you have a lot I control, ie knowing when a collision happens.
You could just try to store the test key if it goes straight in you know test key has no value stored. Should it collide you need to then do a keycomp against all colliding keys. Note key {2,rhubarb} may have the same hash as {37,dog}.
Remember to delete this newly created dummy test record.

How should I approach the implementation of this C if statement given the requirements?

I am currently attempting to create a source code that meets these following requirements:
Requirements:
// Get a deep copy of a portion of array from index first to index last
// inclusive. If successful, return a pointer to a newly-allocated intarr_t containing a copy of the specfied section.
// If an error occurs, i.e. array is null, first or last are out of bounds, last < first, or memory allocation fails, return a null pointer.
I have completed the first two requirements, but it seems that my logic for the third one is faulty, and no matter how much I test it, I can't seem to find the corner case that occurs.
My code:
intarr_t* intarr_copy_subarray( intarr_t* array,
unsigned int first,
unsigned int last )
{
intarr_t *tmp;
tmp = malloc((last-first)*sizeof(intarr_t));
// it seems that my if statement is not meeting the requirement in bold.
if(first>=0 && last<= array->len && array != NULL && first > last && tmp != NULL)
{
//perform copying here
return tmp; // pointer to new array containing the copied stuff
}
else
{
return NULL;
}
}
typedef struct {
int* data;
unsigned int len;
} intarr_t;
There seems to be a case where it returns NULL when it is not supposed to and it has to do with my if statement. Note that I am passing my function a typedef structure that contains a pointer to an array and the length, but that isn't the problem. Should I be using OR statements rather than AND statements?
First of all check for NULL first:
if( !array )
return NULL ;
Then don't allocate just yet, but check if you arguments are in bounds:
if( first>=0 && last< array->len && first <= last )
{
Then allocate memory and if successful copy the array to the new subarray.
Your example allocation isn't correct given that intarr_t ( _t is a reserved identifier by the way ), holds a pointer to the int array. You should allocate one intarr_t and then the array it points to:
intarr_t tmp = malloc(sizeof(intarr_t)) ;
if( tmp )
{
tmp->data = malloc( sizeof( int ) * ( last - first + 1 ) ) ;
//check if malloc succeeded again
tmp->len = last - first + 1 ;
...
The function can look like
intarr_t* intarr_copy_subarray( const intarr_t *array,
unsigned int first,
unsigned int last )
{
if ( array == NULL || array->data == NULL || last < first || array->len <= last )
{
return NULL;
}
intarr_t *tmp = malloc( sizeof( intarr_t ) );
if ( tmp )
{
tmp->data = malloc( ( last - first + 1 ) * sizeof( int ) );
if ( tmp->data == NULL )
{
free( tmp );
tmp = NULL;
}
else
{
tmp->len = last - first + 1;
for ( unsigned int i = 0; i < tmp->len; i++ )
{
tmp->data[i] = array->data[first + i];
}
}
}
return tmp;
}

What is the correct way to pass a struct to a function?

I have a struct, which i'd like to pass to a function. Obviousley i am making somethin wrong, since the output is always the same : (null)
Could someone please help me.
Thanks!
typedef struct
{
char * Value1;
char * Value2;
} TESTSTRUCT, *PTESTSTRUCT;
BOOL MyFunction(PTESTSTRUCT TestStruct);
BOOL QueryData(char * String, char * Name);
int main ()
{
TESTSTRUCT myStruct;
ZeroMemory( &myStruct, sizeof( myStruct) );
MyFunction( &myStruct );
printf( "\nmyStruct.Value1 = %s", myStruct.Value1);
printf( "\nmyStruct.Value2 = %s", myStruct.Value2 );
if ( myStruct.Value1 != NULL ) LocalFree( myStruct.Value1);
if ( myStruct.Value2 != NULL ) LocalFree( myStruct.Value2 );
}
BOOL MyFunction(PTESTSTRUCT TestStruct)
{
//.....
QueryData(TestStruct->Value1, TestStruct->Value2);
//.....
}
QueryData(char * String, char * Name)
{
// do some stuff
LPTSTR szName = NULL;
szName = ( LPTSTR )LocalAlloc( LPTR, dwData * sizeof( TCHAR ) );
String = AllocateMemory(szName);
LocalFree( szName );
szName = NULL;
// do some more work
szName = ( LPTSTR )LocalAlloc( LPTR, dwData * sizeof( TCHAR ) );
Name = AllocateMemory(szName);
LocalFree( szName );
}
LPSTR AllocateMemory( LPSTR inputString )
{
LPSTR outputString = NULL;
outputString = ( LPSTR )LocalAlloc( LPTR, ( strlen( inputString ) + 1 ) * sizeof( CHAR ) );
if (outputString != NULL)
{
lstrcpyA( outputString, inputString );
}
return outputString;
}
The problem is that you assign to the pointer in QueryData. Since C passes all arguments by value, the pointer is copied, and you only modify the local copy inside the QueryData function.
You need to emulate pass by reference (I say "emulate" because C doesn't have pass by reference), by passing a pointer, in your case passing a pointer to the pointer.
Something like:
BOOL QueryData(char ** String, char ** Name)
{
...
*String = AllocateMemory(...);
...
*Name = AllocateMemory(...)
...
}
Call it like
QueryData(&TestStruct->Value1, &TestStruct->Value2);
Pointers in C are passed by value: the String and Name arguments in QueryData are local variables that are initially copies of the (null) pointers in the caller's TestStruct. Changing the local variables within QueryData does not change the values stored in the structure.
You need to pass QueryData either a pointer to the structure itself (i.e. QueryData(TestStruct)) or pointers to the two char* values stored within the structure (i.e. QueryData(&TestStruct->Value1, &TestStruct->Value2)), so that QueryData can change the contents of the structure itself.

Resources