Table of structures (realloc in C) - c

i am trying to write a code in C but i am having some problems with realloc. I had to write a code that will create a stack, and will add to it (dodaj_do_stosu), reamove from it (usun_ze_stosu) and will look at the top thing that is on this stack. I have problem with compiling(it does work for first two words but then it returns (0xC0000374)).
I think i am usining the realloc wrong and the sizeof my structure. If someone could look at my code (especially at the function (dodaj_do_stosu) and tell me what am i doing wrong thx. My code look like this:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
typedef struct {
int n;
char *nazwa;
}element_t;
typedef struct {
int rozmiar;
element_t **tablica;
}stos_t;
void top_of_stack(stos_t *s){
printf("ostatni element stosu:rozmiar = %d nazwa=%s, n=%d\n", s->rozmiar, s->tablica[s->rozmiar]->nazwa, s->tablica[s->rozmiar]->n);
}
void init(stos_t *s)
{
s->rozmiar=0;
s->tablica=malloc(0);
}
void dodaj_do_stosu(stos_t *s, int n, char *name)
{
s->tablica = realloc(s->tablica, (s->rozmiar + 1) * sizeof(s->tablica));
s->tablica[s->rozmiar]->nazwa = name;
s->tablica[s->rozmiar]->n = n;
printf("rozmiar=%d, n=%d , nazwa=%s\n",s->rozmiar, s->tablica[s->rozmiar]->n, s->tablica[s->rozmiar]->nazwa);
s->rozmiar++;
}
void usun_ze_stosu(stos_t *s)
{
s->tablica = realloc(s->tablica, (s->rozmiar - 1) * sizeof(s->tablica[0]));
s->rozmiar--;
}
void rm(stos_t s)
{
free(s.tablica);
}
int main(int argc, char **argv)
{
stos_t s;
init(&s);
int i;
srand(time(0));
if (argc>1)
for(i=1;i<argc;i++){
printf("%s\n", argv[i]);
dodaj_do_stosu(&s, rand() % 10, argv[i]);
}
for(i=0;i<argc-1;i++){
//printf("i=%d, n=%d, nazwa=%s\n",i, s.tablica[i].n, s.tablica[i].nazwa);
}
//top_of_stack(&s);
//usun_ze_stosu(&s);
//top_of_stack(&s);
rm(s);
return 0;
}

A big part of your problem is that tablica is an array of pointers, but you never initialize the pointers themselves.
The dodaj_do_stosu function reallocates the array, but doesn't create the element_t objects. Therefore any dereference of e.g. s->tablica[s->rozmiar] will lead to undefined behavior.
There are two possible solutions:
Allocate a new element_t structure:
s->tablica[s->rozmiar] = malloc(sizeof(element_t));
before you initialize the element_t structure members.
Make tablica an array of structure objects instead of pointers:
element_t *tablica; // tablica is an array of objects, not an array of pointers
I recommend solution 2.

At least the function dodaj_do_stosu is wrong. The data member tablica is declared like
element_t **tablica;
So the expression s->tablica[s->rozmiar] has the type element_t * and an indeterminate value. Thus dereferencing the pointer expression for example like
s->tablica[s->rozmiar]->nazwa
invokes undefined behavior.
You have to allocate memory for objects of the structure type element_t not for pointers of the type element_t *.
So you need to declare the data member like
element_t *tablica;
and within the function to write
s->tablica = realloc(s->tablica, (s->rozmiar + 1) * sizeof( *s->tablica));
Also it is safer to use an intermediate pointer for calls of realloc.
The function can look the following way
int dodaj_do_stosu( stos_t *s, int n, char *name )
{
element_t *tmp = realloc( s->tablica, ( s->rozmiar + 1 ) * sizeof( *s->tablica ) );
int success = tmp != NULL;
if ( success )
{
s->tablica = tmp;
s->tablica[s->rozmiar]->nazwa = name;
s->tablica[s->rozmiar]->n = n;
printf("rozmiar=%d, n=%d , nazwa=%s\n", s->rozmiar, s->tablica[s->rozmiar]->n, s->tablica[s->rozmiar]->nazwa );
++s->rozmiar;
}
return success;
}
Consequently the function should be redefined at least the following way. As is it can for example invoke undefined behavior when s->rozmiar is equal to 0.
int usun_ze_stosu( stos_t *s )
{
int success = s->rozmiar != 0;
if ( success )
{
element_t *tmp = realloc( s->tablica, ( s->rozmiar - 1 ) * sizeof( *s->tablica ) );
success = tmp != NULL;
if ( success )
{
s->tablica = tmp;
--s->rozmiar;
}
}
return success;
}
Also within the function init it will be much better ro write
void init(stos_t *s)
{
s->rozmiar=0;
s->tablica = NULL;
}
Another problem is the function rm
void rm(stos_t s)
{
free(s.tablica);
}
You should pass the original object through a pointer to it and within the function to write
void rm(stos_t *s)
{
free( s->tablica );
s->tablica = NULL;
s->rozmiar = 0;
}

Related

Why is my pointer ("char *item") variable being treated as a regular variable ("char item")?

I have a stack implementation that stores variable: char *items on the stack. But for some reason when I use stack->items[position], it treats it as a regular char (not a pointer) and I am unable to store the full char (it is a URL) on the stack.
I want to give the push function a char * (that is a URL) and I want to take that and put in on my stack, that is either:
p->items[p->pos] = item;
or
strcpy(p->items[p->pos], item);
Here is the part of the code that gives the error:
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "shm_stack.h"
typedef struct int_stack{
int size; /* the max capacity of the stack */
int pos; /* position of last item pushed onto the stack */
char *items; /* stack of stored chars */
} ISTACK;
int is_full(ISTACK *p){
if ( p == NULL ) {
return 0;
}
return ( p->pos == (p->size -1) );
}
int sizeof_shm_stack(int size){
return (sizeof(ISTACK) + sizeof(char) * size);
}
int init_shm_stack(ISTACK *p, int stack_size){
if ( p == NULL || stack_size == 0 ) {
return 1;
}
p->size = stack_size;
p->pos = -1;
p->items = (char *) (p + sizeof(ISTACK));
return 0;
}
ISTACK *create_stack(int size){
int mem_size = 0;
ISTACK *pstack = NULL;
if ( size == 0 ) {
return NULL;
}
mem_size = sizeof_shm_stack(size);
pstack = malloc(mem_size);
if ( pstack == NULL ) {
perror("malloc");
} else {
char *p = (char *)pstack;
pstack->items = (char *) (p + sizeof(ISTACK));
pstack->size = size;
pstack->pos = -1;
}
return pstack;
}
void destroy_stack(ISTACK *p){
if ( p != NULL ) {
free(p);
}
}
int push(ISTACK *p, char *item){
if ( p == NULL ) {
return -1;
}
if ( !is_full(p) ) {
++(p->pos);
//p->items[p->pos] = item;
strcpy(p->items[p->pos], item);
//printf("push method: %d\n", p->items[p->pos]);
return 0;
} else {
return -1;
}
}
The issue is in my push method where I can neither use strcpy() or just assign the char to p->items[p-pos] without it saying something like "assigning char from incompatible type char *", but dereferencing "item" will only get me the first character, and I want the entire "string".
Why is this happening and how can I fix it?
p->items is a char*, so p->items[...] is a char. strcpy expects a char*, so there's a mismatch between what you provide and what's needed.
Not only that, it expects the pointer to point to the first of enough characters to contain the string being copied in. You did not even attempt to get the length of the string pointed by item, much less allocate enough memory for it.
I presume you want a stack of strings. If so, we need a array of pointers (char *items[]) or a pointer to a block of memory for pointers (char **items). The latter is simpler here. As such,
char *items;
should be
char **items;
It would be allocated using
malloc(sizeof(char*) * size)
There are two approaches to adding a string to the stack.
The stack could take ownership of the string provided.
p->items[p->pos] = item;
The stack could make a copy of the string provided.
p->items[p->pos] = strdup(item);
The difference is in who is responsible for freeing the string.

I can't figure out how to use double pointers

I'm trying to use dynamic memory allocation but I can't figure out pointers.
I got the first part down.
void addtext(char **wordarray)
{
char word[N];
char endword[N] = "end";
int i=0;
int words=0;
while (scanf("%19s", word), strcmp(word,endword))
{
words++;
wordarray = realloc(wordarray, words*sizeof(char *));
wordarray[words-1] = malloc (N*sizeof(char));
strcpy(wordarray[words-1], word);
}
for (i=0; i<words; i++)
printf("%s\n", wordarray[i]);
return ;
}
But I'm having trouble when I try to call the same array in a different function.
void savetext(char **wordarray)
{
FILE *savedtext;
int i=0;
savedtext = fopen("Saved Text.txt","wt");
while(wordarray[i][0]!= '\0')
{
fputs(wordarray[i++],savedtext);
fputs(" ",savedtext);
}
return ;
}
My main function looks something like this:
int main (void)
{
char **wordarray;
addtext(wordarray);
savetext(wordarray);
return 0;
}
The second part of the code is obviously wrong, but I'm not sure how to exactly how to call those functions. My previous program didn't use any memory allocation so I didn't bother with pointers.I'm really new to c so any help would be appreciated.
Oh boy. Well, you have two big problems.
First, you never allocated the first wordarray. At the very least malloc it once:
char **wordarray = malloc(1);
Or even better, use malloc instead of realloc the first time (and initialize wordarray with 0!):
wordarray = wordarray ? realloc(wordarray, words * sizeof(char *))
: malloc(words * sizeof(char *));
Second, your addtext function is receiving a copy of this array, and doing stuff with it. Whatever the stuff is, it won't be saved in your wordarray outside, in main. What you need to do is pass a pointer to the array in your function, and edit the main object through that:
void addtext(char ***wordarray)
{
// ...
}
And lastly, you have some very big performance problems, allocating buffers so often. Use a proper growing vector implementation, or if you insist on writing your own at the very least grow it by doubling the size, or even better count the words and allocate the correct size.
Also your end string is arbitrarily allocated of length N, whatever that is. You don't need that, you already know the length. In fact the string is already in the read-only section of your binary, simply get a pointer to it:
const char *endword = "end";
Perhaps refactor your program to make the string creation its own function, and for symmetry, return storage of the string as its own function.
const int STRING_SIZE = 80;
void createString(char ** strPtr, int stringSize);
void freeString(char * strPtr);
int main(int argc, char ** argv) {
char * strValue = NULL;
createString(&strValue, STRING_SIZE);
// ... do stuff ...
freeString(strValue);
}
//
// end of main
//
void createString(char ** strPtr, int stringSize) {
//
// uses pass-by-reference to return *strPtr with allocated storage
//
*strPtr = (char *) calloc(stringSize, sizeof(char));
}
void freeString(char * strPtr) {
if(strPtr == NULL) return;
free(strPtr);
strPtr = NULL;
}
For starters the program has undefined behavior at least because the pointer wordarray was not initialized and has an indeterminate value
char **wordarray;
and this indeterminate value is used in a call of the function realloc in the function addtext
wordarray = realloc(wordarray, words*sizeof(char *));
Moreover the pointer is passed to the function addtext by value. That is the function deals with a copy of the value of the pointer. So changing the copy does not influence on the value stored in the original pointer. The original pointer in main will stay unchanged.
You need to pass the pointer by reference through a pointer to it.
Another problem of the function is that the number of stored strings will not be known outside the function addtext. You need at least append the array with a null pointer that will be used as a sentinel value.
Also this condition in the while loop within the function savetext
while(wordarray[i][0]!= '\0')
does not make a sense because within the function addtext you stop entering strings when the user will enter the string "end".
while (scanf("%19s", word), strcmp(word,endword))
^^^^^^^^^^^^^^^^^^^^
So it is not necessary that the preceding entered string is an empty string.
Here is a demonstrative program that shows how for example the function addtext can be declared and defined.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define N 20
size_t addtext( char ***wordarray )
{
char word[N];
const char *sentinel = "end";
size_t n = 0;
int success = 1;
while ( success && scanf( "%19s", word ) == 1 && strcmp( word, sentinel ) != 0 )
{
char **tmp = realloc( *wordarray, ( n + 1 ) * sizeof( char * ) );
success = tmp != NULL;
if ( success )
{
++n;
*wordarray = tmp;
( * wordarray )[n-1] = malloc( strlen( word ) + 1 );
if ( ( *wordarray )[n-1] ) strcpy( ( *wordarray )[n-1], word );
}
}
return n;
}
int main(void)
{
char **wordarray = NULL;
size_t n = addtext( &wordarray );
for ( size_t i = 0; i < n; i++ )
{
if ( wordarray[i] != NULL ) puts( wordarray[i] );
}
for ( size_t i = 0; i < n; i++ )
{
free( wordarray[i] );
}
free( wordarray );
return 0;
}
If to enter the following sequence of strings
one
two
three
end
then the program output will be
one
two
three
Correspondingly the declaration of the function savetext should be changed. There is not sense in this case to pass the pointer wordarray to the function by reference because the pointer itself is not changed within the function. Also you need to pass the number of elements in the allocated array, So the function declaration can look at least like
void savetext( char **wordarray, size_t n );

2D allocated array (fixed number of columns) as return value of a function

I would like some help with pointers:
in main function I have initialized variable that should point to the array:
int main() {
int n;
double (*array)[3];
array = fillArray(&n);
The function receives an integer argument, which counts number of rows. The return value of the function should be a pointer to the newly created array, which will be saved to the variable 'array' in main function:
double (*)[3] fillArray(int * n) {
double (*array)[3] = NULL;
int allocated = 0;
*n = 0;
while (1)
{
/*scanning input*/
if (allocated <= *n)
{
allocated += 10;
array = (double(*)[3]) realloc (array, sizeof(*array) * allocated)
}
array[*n][0] = value1;
array[*n][1] = value2;
array[*n][2] = value3;
(*n)++;
}
return array;
}
However, the type of return value isn't right and I am kinda lost. Can anyone tell me what is wrong in this code?
Thank you in advance :)
Your code has an unrelated syntax error and some undeclared variables, but the problem you asked about has to do with the form of the declaration of function fillArray(). This alternative works for me:
double (*fillArray(int * n))[3] {
double (*array)[3] = NULL;
/* ... */
return array;
}
Note the similarity in form to the declarations of your variables of the same type.
The problem is that although double (*)[3] is a perfectly valid type designator for use, say, in a cast, it is incorrect to use it quite as you tried to do to declare the type of an object.
given some guessing about items not mentioned in the question.
I think this is what you are looking for.
Notice the checking for success of the call to realloc()
Notice the #define of the magic numbers
#include <stdlib.h> // realloc(), exit(), EXIT_FAILURE
#define ALLOCATION_INCREMENT (10)
#define NUM_DOUBLES (3)
struct tagArray
{
double arrayEntry[ NUM_DOUBLES ];
};
struct tagArray *fillArray(int *n);
int main( void )
{
int n = 0;
struct tagArray *array;
if( NULL == (array = fillArray(&n) ) )
{ // then array generation failed
exit( EXIT_FAILURE );
}
// implied else, array generation successful
....
free( array );
return 0;
} // end function: main
struct tagArray *fillArray(int *n)
{
struct tagArray *array = NULL;
int allocated =0;
while( 1 )
{
/* scanning input,
* to acquire 'value1, value2, value3'
* with some key input causes execution of 'break;'
* */
if( allocated <= *n )
{
allocated += ALLOCATION_INCREMENT;
struct tagArray *temp = realloc (array, sizeof( struct tagArray) * allocated );
if( !temp )
{ // then realloc failed
free( array );
return( NULL );
}
array = temp;
}
array[*n][0] = value1;
array[*n][1] = value2;
array[*n][2] = value3;
(*n)++;
}
return array;
} // end function: fillArray

C : double pointer passed as parameters comes back empty [duplicate]

This question already has answers here:
malloc-ating multidimensional array in function
(7 answers)
Closed 7 years ago.
At first, it may sound normal if I free the allocation within the function, but it's not the case. As I'm writting these lines, I've found a work around, but I would like to keep a certain homogeneity in my code and would prefer to keep it the way it ways but you know working properly, so is there another solutions or my workaround is the only option?
The main function:
void main(void)
{
SHead head; // Custom struct
unsigned char **array = NULL; // pointer to 2D array
allocArray2D(&head, array) // the function signature: (SHead*, unsigned char**)
// here, the array pointer is still NULL (0x0)
//...
return EXIT_SUCCESS;
}
The allocation function malloc a very small amount of memory around 21 unsigned char* and for each simple pointer 21 unsigned char.
Within the function, the pointer is fine and points to a proper address.
So my work around was modify the function from:
void allocArray(SHead* h, unsigned char** arr)
{
int x, y, i;
getsize(head, *x, *y);
arr = (unsigned char**)malloc(sizeof(unsigned char*)*y);
if(arr)
printf(">> Erro allocating memory\n"), return;
for(i =0; i<y; i++)
{
arr[i] = (unsigned char)malloc(sizeof(unsigned char)*x);
}
}
to the following:
unsigned char** allocArray(SHead*)
{
int x, y, i;
unsigned char **arr;
getsize(head, *x, *y);
arr = (unsigned char**)malloc(sizeof(unsigned char*)*y);
if(arr)
printf(">> Erro allocating memory\n"), return;
for(i =0; i<y; i++)
{
arr[i] = (unsigned char)malloc(sizeof(unsigned char)*x);
}
return arr; // returning the address
}
As I said earlier, I'd like to keep homogeneity within my code and would prefer to keep a function signature similar to other function I have. My workaround work properly. And I'm wondering if it is the only solution, or perhaps I am missing something.
EDIT: Following the comments I added more code.
Thank you,
Alex.
You have to pass a pointer to the 2dimensional array to your function, to write in the function to the value behind the pointer:
SHead head; // Custom struct
unsigned char **array = NULL; // pointer to 2D array
allocArray2D(*head, &array)
// ^ address of array
-
void allocArray(SHead* head, unsigned char*** pArray)
// ^ pointer to char** because its an output parameter
{
int x, y, i;
getsize( head, &x, &y );
*pArray = malloc( y * sizeof( unsigned char * );
// ^ assigne somtething to the variable array refered by the pointer pArray
if( *pArray == NULL )
{
printf(">> Erro allocating memory\n")
return;
}
for ( i = 0; i < y; i ++ )
(*pArray)[i] = malloc( x * sizeof( unsigned char ) );
}
Note, what you did was to pass a NULL-pointe to the function allocArray.
An other solution would be to return the allocated memory by the return value of function allocArray:
SHead head; // Custom struct
unsigned char **array = NULL;
array = allocArray( &head );
-
unsigned char** allocArray( SHead* head )
{
int x, y, i;
getsize( head, &x, &y );
unsigned char** arr = malloc( y * sizeof( unsigned char * );
if( arr == NULL )
{
printf(">> Erro allocating memory\n")
return;
}
for ( int i = 0; i < y; i ++ )
arr[i] = malloc( x * sizeof( unsigned char ) );
return arr;
}
If I have understood you correctly what you need is something like the following
For simplicity I excluded the parameter that refers to the structure.
void allocArray2D( unsigned char ***a, size_t n )
{
*a = malloc( n * sizeof( unsigned char * ) );
for ( size_t i = 0; i < n; i++ ) ( *a )[i] = malloc( n * sizeof( unsigned char ) );
}
//...
unsigned char **array = NULL; // pointer to 2D array
allocArray2D( &array, 21 );
If you want that the original object would be changed after passing it to a function then you have to pass it by reference.
Your call looks very strange.
First, you are passing *head. head seems to be an uninitialised variable, so passing *head seems badly wrong.
Second, the function called has no way of seeing array. Your call is equivalent to allocArray2D (*head, NULL) and not having the array variable at all. This also seems very wrong.

Practice Exam Questions

This is a practice exam question that I am having some difficulty with:
struct bodytp // Is there an error?
{
char *name; // If so, fix the error.
int len;
};
main()
{
struct bodytp person;
keepname(&person , "Waterman");
printf("%s\n", person.name);
}
void keepname(struct bodytp *onept, const char *last)
{
int len;
char *tpt;
for ( len = 0; last[len] != '\0'; )
len++;
char name[len+1];
for ( tpt = name; *tpt++ = *last++; )
;
onept->name = name;
onept->len = len;
}
I have determined that there is an error, as when I run it, I get garbage output from printf. I have also determined that person's name is indeed "Waterman" after the keepname function call. I have tried dereferencing person.name to person -> name, changing the problem from a stack-based question to a heap-based question by eliminating the ampersand operator and malloc-ing the struct, but nothing worked. Can anyone steer me in the right direction? Thank you in advance.
Is there an error?
struct bodytp // Is there an error?
{
char *name; // If so, fix the error.
int len;
};
No there is no error. It is a valid structure definition.
Now errors follow.:)
Function main shall be declared as
int main( void )
Though it is not an error nevertheless it would be better that before the function call there woud be the function prototype
keepname(&person , "Waterman");
The program has undefined behaviour because there is an assignment of a pointer to the structure by the address of a local array that will be destroyed after exiting the function
void keepname(struct bodytp *onept, const char *last)
{
//...
char name[len+1];
//...
onept->name = name;
//...
}
The valid function could be defined like
void keepname(struct bodytp *onept, const char *last)
{
int len = 0;
char *tpt;
while ( last[len] != '\0' ) len++;
char *name = malloc( len + 1 );
for ( tpt = name; *tpt++ = *last++; ) ;
onept->name = name;
onept->len = len;
}
In this case you have to free the alocated memory in main.
Take into account that you coud use standard C functions strlen and strcpy in the function.
You need to allocate the memory for the name in the heap.
void keepname(struct bodytp *onept, const char *last)
{
int len;
char *tpt;
for ( len = 0; last[len] != '\0';len++);
char *name=malloc(len+1);
onept->name = name;
onept->len = len;
for ( ; *name++ = *last++ ; );
}

Resources