Using pointers for string manipulation in c - c

Here I'm taking a sentence a checking if it is a palindrome or not.I'm doing this in the process of learning stacks.
Is there a way i can use pointers instead of char array 'sent' so that the number of input characters need not be constrained to 20 in the following code?
The code is working fine, but should there be any improvements in terms of performance or anything else?
is there anything important about pointers i should remember while using stacks, like initializing it to NULL?
Thanks
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
typedef struct node
{
char data;
struct node *link;
}StackNode;
void insertData(StackNode **);
void push(StackNode **, char);
void checkData(StackNode **);
bool pop(StackNode **,char *);
char sent[20] = "";
void main()
{
StackNode *stackTop;
stackTop = NULL;
insertData(&stackTop);
checkData(&stackTop);
printf("\n");
return;
}
void insertData(StackNode **stackTop)
{
char c;
int len;
printf("Enter the Sentence\n");
while( ( ( c = getchar() ) != '\n'))
{
if( ( ( c>='a' &&c<='z') || (c>='A' && c<='Z')))
{
if((c>='A' && c<='Z'))
{
int rem;
rem = c-'A';
c='a' + rem;
}
push(stackTop,c);
len = strlen(sent);
sent[len++]=c;
sent[len]='\0';
}
}
printf("Letters are %s\n\n",sent);
}
void push(StackNode **stackTop,char c)
{
StackNode *pNew;
pNew = (StackNode*) malloc(sizeof(StackNode));
if(!pNew)
{
printf("Error 100:Out of memory\n");
exit(100);
}
pNew->data = c;
pNew->link = *stackTop;
*stackTop = pNew;
}
void checkData(StackNode **stackTop)
{
char c;
int i=0;
while(pop(stackTop,&c))
{
if( c !=sent[i++])
{
printf("Not palindrome");
return;
}
}
printf("Palindrome");
}
bool pop(StackNode **stackTop,char *c)
{
StackNode *pNew;
pNew = *stackTop;
if(pNew == NULL)
return false;
*c = pNew->data;
*stackTop = pNew->link;
printf("char poped %c\n",*c);
free(pNew);
return true;
}

As far as I know, there is no way to have an "infinite array" or an array with no limitations. However, if you use malloc you can produce a section of memory large enough that you won't need to worry about the limitations as much. I see that later on in the code you have used malloc, so I assume you know how it works. However, I would use something like this;
char * sent = malloc(sizeof(char) * 100);
if(sent == NULL){
printf("OUT OF MEMORY!");
return 1;
}
Where 100 is the buffer size you wish to have. I have used a size up to 10000 and had no problems at runtime, so that may be what you need.

In C, arrays are really pointers to statically allocated memory. It is pretty straightforward to create a pointer to an array, or any element in an array. For example, suppose we have you array char sent[20]. If we wanted to create a pointer that pointed to the exact same memory as sent, we can declare char *sentP = sent. We can now replace any use of sent with sentP. We can even create a pointer to the middle of sent: char *sentMidP = sent + 9. Now, sentMidP[0] is the same as sent[9] and sentMidP[-9] is the same as sent[0].
However, unlike sent, we can change where sentP and sentMidP point (think of sent as a constant pointer char * const, which you can't change). Thus, if you had another array char sent2[100]'. You can set the value ofsentPtosent2. What's cool about this is that you can do it *at runtime*, which effectively means that you can change the size ofsentP` depending on the size of your input.
However, there is no need to limit yourself to statically allocated input. C provides the malloc function (see here) to allocate memory at runtime. Thus, if you don't know the size of your sentence at compile time, but you will know it at runtime (say in a variable called sentenceLength), you can allocate `sentP' like the following.
char *sentP = malloc(sizeof(char) * (sentenceLength + 1)); // Plus one for the NUL termination byte in C strings
if (sentP == NULL) {
fprintf(stderr, "No more memory :(");
exit(EXIT_FAILURE);
}
Note how we now have to handle out-of-memory errors. In general, dynamic allocation introduces more overhead, because there is a possibility that we run out of memory, a requirement that we ensure we only access what was allocated, and a need to release the memory with free once we're done.
When you're done with the sentP pointer, be sure to free it with:
free(sentP);
That's it! You can use the sentP pointer we made in your code, and everything should work great. Good luck!

Related

using free() function causes runtime error

I have a struct called Person, that contains two attributes - first and last name.
After successfully dynamic allocation of memory for a variable of Person type, giving values to the attributes I would like to free the memory, but I keep getting a runtime error (the program window just crashes)
this it the code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct {
char firstName[15];
char lastName[15];
} Person;
void main(){
int len = 0;
char firstName[]="danny", lastName[]="johnes";
Person *temp = (Person*)malloc(sizeof(Person));
if (temp == NULL)
return;
len = strlen(firstName);
temp->firstName[len] = (char*)malloc(sizeof(char)*(len));
if (temp->firstName == NULL)
return;
strcpy(temp->firstName, firstName);
len = strlen(lastName);
temp->lastName[len] = (char*)malloc(sizeof(char)*(len));
if (temp->firstName == NULL)
return;
strcpy(temp->lastName, lastName);
freePerson(temp);
system("pause");
return;
}
This is the function I use to free the memory:
void freePerson(Person* ps) {
if (ps != NULL) {
free(ps->firstName);
free(ps->lastName);
free(ps);
}
}
All I want the code to do - is to store the name in a dynamically allocated structure, and free it.
Later on, I plan to replace the hard-coded names with values inputed from file.
Any ideas about the error? Thank you.
You have already space allocated for firstName, so you have to copy the name within the size constraits (15 bytes). You can do this best with snprintf like this:
snprintf(temp->firstName, sizeof(temp->firstName), "%s", firstName);
Same goes for lastName. Mind that both might be truncated if the length exceeds the size of the field.
The other option is to allocate the fields dynamically. Then your struct members should be pointers, not char arrays:
typedef struct {
char *firstName;
char *lastName;
} Person;
You can then allocate and assign the names like this:
temp->firstName = strdup(firstName); // (same for lastName)
But mind that you have to free these fields seperately if you want to free the whole item.
If you don't want to specify a maximum size for the names in the structure, you need to declare them as pointers, not arrays.
typedef struct {
char *firstName;
char *lastName;
} Person;
Then you should assign the result of malloc() to the member, without indexing it. You also need to add 1 to strlen(firstName), to make space for the null terminator.
temp->firstName = malloc(strlen(firstName)+1);
if (temp->firstName == NULL) {
return;
}
strcpy(temp->firstName, firstName);
This is how I would write this:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define FIRSTNAME_MAXLEN 15
#define LASTNAME_MAXLEN 15
typedef struct
{
char firstName[FIRSTNAME_MAXLEN+1];
char lastName[LASTNAME_MAXLEN+1];
} person_t;
void freePerson(person_t *ps) {
if (ps) {
free(ps); ps=NULL;
}
}
int main(){
const char *firstName="danny";
const char *lastName="johnes";
person_t *temp = calloc(1, sizeof(person_t));
if (!temp) return 1;
strncpy(temp->firstName, firstName, FIRSTNAME_MAXLEN);
strncpy(temp->lastName, lastName, LASTNAME_MAXLEN);
printf("test: firstname: %s\n", temp->firstName);
printf("test: lastname: %s\n", temp->lastName);
freePerson(temp);
return 0;
}
You allocate enough room on the heap and cleanup things with calloc(), then you copy your string with strncpy() limiting to the bytes reserved and avoiding buffer overflow. At the end you need to free() the memory returned by calloc().
Since you allocated char firstName[] and char lastName[] inside your struct you don't need to reserve other memory with malloc() for those members, and you also don't need to free() them.
At least 5 issues:
To duplicate a string, insure allocation includes enough room for the characters including the null character.
Otherwise the strcpy() writes outside the allocation which is undefined behavior (UB).
len = strlen(firstName);
// temp->firstName[len] = (char*)malloc(sizeof(char)*(len ));
temp->firstName = (char*)malloc(sizeof(char)*(len + 1));
// + 1
...
strcpy(temp->firstName, firstName);
Same for lastName.
Also assign to the pointer, not the char. #Barmar
Person members are arrays. For dynamic allocation, they should be pointers. #NthDeveloper
typedef struct {
// char firstName[15];
// char lastName[15];
char *firstName;
char *lastName;
} Person;
2nd test is wrong
// if (temp->firstName == NULL)
if (temp->lastName == NULL)
int vs. size_t.
int len = 0; assumes the string length fits in a int. Although this is exceedingly common, the type returned from strlen() is size_t. That unsigned type is right-sized for array indexing and sizing - not too wide, not too narrow. Not a key issue in this learner code.
// int len = 0;
size_t len = 0;
Tip: cast not needed. Allocate to the referenced object, not the type. Easier to code right, review and maintain.
// Person *temp = (Person*)malloc(sizeof(Person));
Person *temp = malloc(sizeof *temp);
// temp->firstName[len] = (char*)malloc(sizeof(char)*(len + 1));
temp->firstName = malloc(sizeof *(temp->firstName) * (len + 1));
Tip: Although not C standard, many platforms provide strdup() to allocated and copy strings. Sample strdup() code.
temp->firstName = strdup(firstName);
Tip: Likely the most valuable one: A good compiler with warnings well enabled should have warned about temp->firstName[len] = (char*)malloc(sizeof(char)*(len)); as it is a questionable type mis-match in the assignment. These warnings save you and us all time. Insure your next compilation has all warning enabled.

Realloaction of memory in C - Arrays

I'm starting with an array array[0] let's say.
As I loop through a text file and find keywords I'd like to store those words in that array.
So the first run though I'd assing the first keyword very simply
array[0] = "Word"
However I'm not sure how to increment that array to 1, and 2, etc.
I've read a few posts on memory allocaiton but that seemed to be specific to strings; perhaps I'm misunderstanding the concept.
I'd like to preserve the current array's contents, and increment it.
I've rigged it by setting my array[10], but I'd prefer to learn the correct way to do this.
I've included the code below so far (without any memory allocation)
#include <stdio.h>
#include <memory.h>
#include "tables.h"
int main() {
insertVarbleTble("Name","CSTRING",1,0,"");
return 0;
}
int insertVarbleTble(char *ident, char *type, int local, int constVar, char *constVal){
int successful;
int sizeArry;
sizeArry = sizeof(varible)/ sizeof(varible[0]);
if(sizeArry <= 0){
varible[sizeArry]== ident;
}else{
successful = (searchVarbleTble(ident,sizeArry)==1;
}
if(successful ==0){
varible[sizeArry+1]==ident;
}else{
printf("Already exists");
}
}
void realocMem(int size){
varible[size];
}
int searchVarbleTble(char * ident, int arrySize){
int i;
int results = 0;
for(i=0;i<arrySize;i++){
if(!strcmp(varible[i],ident)){
results= 1;
}
}
return results;
}
Header file contains the array I'm using they are:
char varible[0];
int insertVarbleTble(char *, char *, int, int , char *);
int searchVarbleTble(char *, int);
Would a potential solution be to first count the number of keywords that exist, and then dimension the array?
Okay generally what you want to do is not possible with arrays. Your code is really hard to understand but I will try to give you an example on how it is done.
This is not directly what you want, but should help you find your own solution. If you want to work with strings it gets a bit more complicated because strings are arrays in itself so you have to make sure that you always have enough memory for your current string available.
#include <stdio.h>
#include <stdlib.h>
typedef struct data_container_{
int *mem;
int size;
} data_container;
void add_to_memory(data_container *data, int pos, int value)
{
if (pos+1 > data->size) //check if memory for this position is allocated
{
int *dummy = realloc(data->mem, pos+1); // call realloc to get more memory
if(dummy == NULL) //check if reallocation was succesful
{
puts("Memory reallocation failed");
exit(1); //terminate program
}
else
{
data->size = pos+1; //set size to the newly allocated size
data->mem = dummy; //if succesful point to the new memory location
}
}
data->mem[pos] = value;
}
int main(void)
{
data_container data; // create stuct
data.size = 2;
data.mem = malloc(sizeof (int) * data.size); //allocate memory, similar to an array but dynamic
if(data.mem == NULL) //check if allocation was succesful
{
puts("Memory allocation failed");
exit(1); //terminate program
}
add_to_memory(&data, 0, 3); //pass the address of the struct
add_to_memory(&data, 1, 6);
add_to_memory(&data, 2, 8); //now it uses realloc, pos would be out of the allocated range
for(int i =0; i<data.size; i++)
{
printf("%d\n",data.mem[i]); //a pointer can be accessed similar to an array
}
free(data.mem); //free the allocated memory
}
as Pablo said you should read about malloc and realloc especially you should keep in mind that the memory allocated is not initialized. calloc initializes with 0.
And always remember to free allocated space when it is not used anymore.

Why strcpy doesn't work but direct assign works?

In the following code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct
{
char** tab;
int n;
}slist;
void print(slist* p);
void add(slist* p, const char* s);
void add(slist* p, const char* s)
{
if(p->n==0)
{
p->tab=(char**)malloc(sizeof(char**));
}
strcpy(p->tab[p->n],s);
p->n=p->n+1;
}
void print(slist* p)
{
int i;
printf("[");
for(i=0;i<p->n;i++)
printf(" %s",p->tab[i]);
printf(" ]");
}
int main()
{
char s1[25] = "Picsou";
char s2[25] = "Flairsou";
slist* p = (slist*)malloc(sizeof(slist));
p->n=0;
p->tab=NULL;
add(p,s1);
add(p,s2);
print(p);
return 0;
}
the function add() doesn't work, but if I change it to:
void add(slist* p, const char* s)
{
if(p->n==0)
{
p->tab=(char**)malloc(sizeof(char**));
}
p->tab[p->n]=s;
p->n=p->n+1;
}
it seems to work perfectly well. In the first case the output is only " [";
in the second case it is what is should be: " [ Picsou Flairsou ] ".
I cannot understand why.
I also tried this :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct
{
char** tab;
int n;
}slist;
void print(slist* p);
void add(slist* p, const char* s);
void print(slist* p)
{
int i;
printf("[");
for(i=0;i<p->n;i++)
printf(" %s",p->tab[i]);
printf(" ]");
}
void add(slist* p, const char* s)
{
slist* tmp = (slist*)malloc(sizeof(slist));
tmp->tab=(char**)malloc(sizeof(char*)*(p->n+1));
int i;
for(i=0;i<p->n;i++)
tmp->tab[i]=(char*)malloc(sizeof(char));
strcpy(tmp->tab[p->n],s);
tmp->n=p->n+1;
p = tmp;
}
int main()
{
char* s1 = "Picsou";
char* s2 = "Flairsou";
slist* p = (slist*)malloc(sizeof(slist));
p->n=0;
p->tab=NULL;
add(p,s1);
add(p,s2);
print(p);
return 0;
}
Lots of errors here, which is common with people who are new to dealing with pointers. Where to begin... I'll just go in the order things appear in code.
This is not a "list".
It's an array... sort of. If you say "list" to a C programmer, they will think you mean a linked-list.
Incorrect allocation of array.
if(p->n==0)
{
p->tab=(char**)malloc(sizeof(char**));
}
Here you have allocated enough to store a single pointer. The second time you call add, you're going to access memory off the end. You have also incorrectly cast the result (in C, you don't cast the return value from malloc). Additionally, you have given confusing information to the reader, because you intend to allocate an array that will hold elements of type char*, NOT char**.
You must either allow the array to expand dynamically when required (not appropriate for your abilities right now - maybe try that in a few days), or set a maximum size. Let's do that.
const int MAX_SIZE = 100;
if( p->n==0 )
{
p->tab = malloc( MAX_SIZE * sizeof(char*) );
}
else if( p->n == MAX_SIZE )
{
printf( "Maximum size exceeded!\n" );
return;
}
You could use calloc instead of malloc if you like. It will zero-initialise the block after allocating it: calloc( MAX_SIZE, sizeof(char*) )
Copying to an uninitialized pointer.
strcpy(p->tab[p->n],s);
You allocated memory for tab, but you did not allocate memory that is pointed to by its elements, and here you have undefined behaviour (most likely resulting in a segmentation fault, but could do anything).
Make sure you have a valid pointer, and the location it points has enough storage reserved for the data you are copying into it:
p->tab[p->n] = malloc( strlen(s) + 1 );
strcpy( p->tab[p->n], s );
Storing potentially invalid pointer.
Your alternative that "works perfectly well" uses:
p->tab[p->n]=s;
However, the only reason this works is because those pointers remain valid for the whole time that you use the "list" (but actually the program does not "work" because of reasons I highlighted in number 2.
Sometimes we desire this behaviour in a program, and design our data structures to index pointers that they do not own. But more often, and especially for a beginner, you are better off copying the data (instead of simply copying the pointer). And so you'll instead use the approach I've suggested in number 3 above.
No comment!!
There are so many things wrong with the following code, that I'm not going to pull it apart or explain them.
void add(slist* p, const char* s)
{
slist* tmp = (slist*)malloc(sizeof(slist));
tmp->tab=(char**)malloc(sizeof(char*)*(p->n+1));
int i;
for(i=0;i<p->n;i++)
tmp->tab[i]=(char*)malloc(sizeof(char));
strcpy(tmp->tab[p->n],s);
tmp->n=p->n+1;
p = tmp;
}
However, it appears like you were attempting to do something similar to realloc. This is the option I mentioned in number 2 that I said you're maybe not ready for. But read up on it anyway: realloc

qsort Segmentation Fault structs

So, my first question here, please be patient with me:
My task is to sort an array of structs (name, surname and another struct for the birthday, which consists of the year, month, day). I have to sort by birthdate and by using qsort.
My problem is, I looked up everything about qsort but i am not quite sure if my implementation is correct since I am new to C. I can create the executable program but it is not giving my any result only Segmentation Fault.
Here is my Code:
#include <stdio.h>
#include <stdlib.h>
typedef int (*compfn) (const void*, const void*);
typedef struct {
unsigned year, month, day;
} date_t;
typedef struct {
char name[32];
char surname[32];
date_t birthday;
}person_t;
typedef struct {
unsigned n;
unsigned cap;
person_t *arr;
} persons_t;
int compare(person_t *a, person_t *b){
if(a->birthday.year!=b->birthday.year){
return a->birthday.year-b->birthday.year;
}else{
if(a->birthday.month!=b->birthday.month){
return a->birthday.month-b->birthday.month;
}else{
return a->birthday.day-b->birthday.day;
}
}
}
int main(int argc, char* argv[])
{
if (argc <= 1) {
fprintf(stderr, "syntax: %s <inputfile>\n", argv[0]);
return 1;
}
FILE* f = fopen(argv[1], "rt");
if (f == NULL) {
fprintf(stderr, "cannot open file %s\n", argv[1]);
return 1;
}
persons_t persons;
persons.n = 0;
persons.cap = 0;
persons.arr = NULL;
person_t p;
while (fscanf(f, "%s %s %4u-%2u-%2u", p.name, p.surname,
&p.birthday.year, &p.birthday.month, &p.birthday.day) == 5) {
if (persons.n == persons.cap) {
persons.cap = persons.cap == 0 ? 1 : 2 * persons.cap;
persons.arr = realloc(persons.arr, persons.cap * sizeof(persons.arr[0]));
}
persons.arr[persons.n++] = p;
}
int nitems = persons.cap*sizeof(persons.arr[0]);
int size = sizeof(persons.arr[0]);
qsort(persons.arr, nitems, size, (compfn)compare);
for (unsigned i = 0; i < persons.n; i++) {
person_t *p = persons.arr + i;
printf("%s %s %4u-%2u-%2u\n",
p->name, p->surname,
p->birthday.year, p->birthday.month, p->birthday.day);
}
fclose(f);
return 0;
}
I hope someone can help me,
Thanks in advance ;)
As far as _t-suffixed identifiers go, according to the C standard they're reserved for the implementation (e.g. your compiler, and/or your standard library). It's very possible that your implementation already has a date_t type, and your code might be causing some kind of mischief. If you wish to avoid subtly and dangerously clashing identifiers wreaking all sorts of havoc, it's probably best to avoid them. Not to worry, you could always use '_s' to denote a struct type instead!
Whenever you're declaring a variable that represents an index within an array, use size_t as the type!
int compare(person_t *a, person_t *b){
...
qsort(persons.arr, nitems, size, (compfn)compare);
According to the qsort manual, the argument given as the comparator function should be an int (*compar)(const void *, const void *), and that's what you've given since you've cast to (compfn). As far as qsort is aware that function accepts two const void * arguments, which might differ in representation to person_t * arguments. This could certainly cause segfaults. Don't lie about the type of compare. Change it to look more like:
int compare(const void *x, const void *y) {
const person_s *a = x, *b = y;
/* ... */
}
... and you won't need the cast or the typedef.
Next, onto return values for that function. I have used implementations where-by lexically illogical return values cause segmentation faults. For example, if a <= b and b <= c, then a <= c, but your code doesn't guarantee this. In fact, using your code it is possible that a <= b, b <= c and a > c. I recommend making sure your code guarantees correspondence between the return value and lexical order. You can do so by returning 1 for greater than, 0 for equal to or -1 for less than.
#define lexical_order(x,y) ((x > y) - (x < y))
int compare(const void *x, const void *b){
const person_s *a = x, *b = y;
return a->birthday.year != b->birthday.year ? lexical_order(a->birthday.year, b->birthday.year)
: a->birthday.month != b->birthday.month ? lexical_order(a->birthday.month, b->birthday.month)
: lexical_order(a->birthday.day, b->birthday.day);
}
I'm sure you're aware that you should be checking the return value of realloc... For example:
void *temp = realloc(persons.arr, persons.cap * sizeof(persons.arr[0]));
if (temp == NULL) { /* If we don't check return value prior *
* to assigning to persons.arr, we *
* might leak some memory... */
puts("Error in realloc");
free(persons.arr);
exit(-1);
}
persons.arr = temp;
Finally, and most importantly (this is probably your error), are you sure about this?
int nitems = persons.cap*sizeof(persons.arr[0]);
If you mean to pass this as the number of items to qsort (which is usual), then I think that should be:
size_t nitems = persons.n;
P.S. In case you missed it the second time, you should probably audit your code to make sure you're using size_t to store array indexes only.
P.P.S. Don't forget to free(persons); at the end of your program, so you don't end up with reports of memory leaks when you use valgrind...
P.P.P.S. valgrind is awesome!
So you are allocating our array by doubling its size whenever needed, using persons.cap, but you are not filling all its elements, are you?
From your code, the actual number of persons is nitems = persons.n, not persons.cap. What if you retry your code with nitems=persons.n?
If you have unfilled elements in your array, it means the strings inside them are arbitrary (i.e person.name), so probably not null-terminated, and the crash will occur when you try to display them.

C Dynamically creating array of structs which include variable sized 2d array [duplicate]

I know how to create an array of structs but with a predefined size. However is there a way to create a dynamic array of structs such that the array could get bigger?
For example:
typedef struct
{
char *str;
} words;
main()
{
words x[100]; // I do not want to use this, I want to dynamic increase the size of the array as data comes in.
}
Is this possible?
I've researched this: words* array = (words*)malloc(sizeof(words) * 100);
I want to get rid of the 100 and store the data as it comes in. Thus if 76 fields of data comes in, I want to store 76 and not 100. I'm assuming that I don't know how much data is coming into my program. In the struct I defined above I could create the first "index" as:
words* array = (words*)malloc(sizeof(words));
However I want to dynamically add elements to the array after. I hope I described the problem area clearly enough. The major challenge is to dynamically add a second field, at least that is the challenge for the moment.
I've made a little progress however:
typedef struct {
char *str;
} words;
// Allocate first string.
words x = (words) malloc(sizeof(words));
x[0].str = "john";
// Allocate second string.
x=(words*) realloc(x, sizeof(words));
x[1].FirstName = "bob";
// printf second string.
printf("%s", x[1].str); --> This is working, it's printing out bob.
free(x); // Free up memory.
printf("%s", x[1].str); --> Not working since its still printing out BOB even though I freed up memory. What is wrong?
I did some error checking and this is what I found. If after I free up memory for x I add the following:
x=NULL;
then if I try to print x I get an error which is what I want. So is it that the free function is not working, at least on my compiler? I'm using DevC??
Thanks, I understand now due to:
FirstName is a pointer to an array of char which is not being allocated by the malloc, only the pointer is being allocated and after you call free, it doesn't erase the memory, it just marks it as available on the heap to be over written later. – MattSmith
Update
I'm trying to modularize and put the creation of my array of structs in a function but nothing seems to work. I'm trying something very simple and I don't know what else to do. It's along the same lines as before, just another function, loaddata that is loading the data and outside the method I need to do some printing. How can I make it work? My code is as follows:
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <ctype.h>
typedef struct
{
char *str1;
char *str2;
} words;
void LoadData(words *, int *);
main()
{
words *x;
int num;
LoadData(&x, &num);
printf("%s %s", x[0].str1, x[0].str2);
printf("%s %s", x[1].str1, x[1].str2);
getch();
}//
void LoadData(words *x, int * num)
{
x = (words*) malloc(sizeof(words));
x[0].str1 = "johnnie\0";
x[0].str2 = "krapson\0";
x = (words*) realloc(x, sizeof(words)*2);
x[1].str1 = "bob\0";
x[1].str2 = "marley\0";
*num=*num+1;
}//
This simple test code is crashing and I have no idea why. Where is the bug?
You've tagged this as C++ as well as C.
If you're using C++ things are a lot easier. The standard template library has a template called vector which allows you to dynamically build up a list of objects.
#include <stdio.h>
#include <vector>
typedef std::vector<char*> words;
int main(int argc, char** argv) {
words myWords;
myWords.push_back("Hello");
myWords.push_back("World");
words::iterator iter;
for (iter = myWords.begin(); iter != myWords.end(); ++iter) {
printf("%s ", *iter);
}
return 0;
}
If you're using C things are a lot harder, yes malloc, realloc and free are the tools to help you. You might want to consider using a linked list data structure instead. These are generally easier to grow but don't facilitate random access as easily.
#include <stdio.h>
#include <stdlib.h>
typedef struct s_words {
char* str;
struct s_words* next;
} words;
words* create_words(char* word) {
words* newWords = malloc(sizeof(words));
if (NULL != newWords){
newWords->str = word;
newWords->next = NULL;
}
return newWords;
}
void delete_words(words* oldWords) {
if (NULL != oldWords->next) {
delete_words(oldWords->next);
}
free(oldWords);
}
words* add_word(words* wordList, char* word) {
words* newWords = create_words(word);
if (NULL != newWords) {
newWords->next = wordList;
}
return newWords;
}
int main(int argc, char** argv) {
words* myWords = create_words("Hello");
myWords = add_word(myWords, "World");
words* iter;
for (iter = myWords; NULL != iter; iter = iter->next) {
printf("%s ", iter->str);
}
delete_words(myWords);
return 0;
}
Yikes, sorry for the worlds longest answer. So WRT to the "don't want to use a linked list comment":
#include <stdio.h>
#include <stdlib.h>
typedef struct {
char** words;
size_t nWords;
size_t size;
size_t block_size;
} word_list;
word_list* create_word_list(size_t block_size) {
word_list* pWordList = malloc(sizeof(word_list));
if (NULL != pWordList) {
pWordList->nWords = 0;
pWordList->size = block_size;
pWordList->block_size = block_size;
pWordList->words = malloc(sizeof(char*)*block_size);
if (NULL == pWordList->words) {
free(pWordList);
return NULL;
}
}
return pWordList;
}
void delete_word_list(word_list* pWordList) {
free(pWordList->words);
free(pWordList);
}
int add_word_to_word_list(word_list* pWordList, char* word) {
size_t nWords = pWordList->nWords;
if (nWords >= pWordList->size) {
size_t newSize = pWordList->size + pWordList->block_size;
void* newWords = realloc(pWordList->words, sizeof(char*)*newSize);
if (NULL == newWords) {
return 0;
} else {
pWordList->size = newSize;
pWordList->words = (char**)newWords;
}
}
pWordList->words[nWords] = word;
++pWordList->nWords;
return 1;
}
char** word_list_start(word_list* pWordList) {
return pWordList->words;
}
char** word_list_end(word_list* pWordList) {
return &pWordList->words[pWordList->nWords];
}
int main(int argc, char** argv) {
word_list* myWords = create_word_list(2);
add_word_to_word_list(myWords, "Hello");
add_word_to_word_list(myWords, "World");
add_word_to_word_list(myWords, "Goodbye");
char** iter;
for (iter = word_list_start(myWords); iter != word_list_end(myWords); ++iter) {
printf("%s ", *iter);
}
delete_word_list(myWords);
return 0;
}
If you want to dynamically allocate arrays, you can use malloc from stdlib.h.
If you want to allocate an array of 100 elements using your words struct, try the following:
words* array = (words*)malloc(sizeof(words) * 100);
The size of the memory that you want to allocate is passed into malloc and then it will return a pointer of type void (void*). In most cases you'll probably want to cast it to the pointer type you desire, which in this case is words*.
The sizeof keyword is used here to find out the size of the words struct, then that size is multiplied by the number of elements you want to allocate.
Once you are done, be sure to use free() to free up the heap memory you used in order to prevent memory leaks:
free(array);
If you want to change the size of the allocated array, you can try to use realloc as others have mentioned, but keep in mind that if you do many reallocs you may end up fragmenting the memory. If you want to dynamically resize the array in order to keep a low memory footprint for your program, it may be better to not do too many reallocs.
This looks like an academic exercise which unfortunately makes it harder since you can't use C++. Basically you have to manage some of the overhead for the allocation and keep track how much memory has been allocated if you need to resize it later. This is where the C++ standard library shines.
For your example, the following code allocates the memory and later resizes it:
// initial size
int count = 100;
words *testWords = (words*) malloc(count * sizeof(words));
// resize the array
count = 76;
testWords = (words*) realloc(testWords, count* sizeof(words));
Keep in mind, in your example you are just allocating a pointer to a char and you still need to allocate the string itself and more importantly to free it at the end. So this code allocates 100 pointers to char and then resizes it to 76, but does not allocate the strings themselves.
I have a suspicion that you actually want to allocate the number of characters in a string which is very similar to the above, but change word to char.
EDIT: Also keep in mind it makes a lot of sense to create functions to perform common tasks and enforce consistency so you don't copy code everywhere. For example, you might have a) allocate the struct, b) assign values to the struct, and c) free the struct. So you might have:
// Allocate a words struct
words* CreateWords(int size);
// Assign a value
void AssignWord(word* dest, char* str);
// Clear a words structs (and possibly internal storage)
void FreeWords(words* w);
EDIT: As far as resizing the structs, it is identical to resizing the char array. However the difference is if you make the struct array bigger, you should probably initialize the new array items to NULL. Likewise, if you make the struct array smaller, you need to cleanup before removing the items -- that is free items that have been allocated (and only the allocated items) before you resize the struct array. This is the primary reason I suggested creating helper functions to help manage this.
// Resize words (must know original and new size if shrinking
// if you need to free internal storage first)
void ResizeWords(words* w, size_t oldsize, size_t newsize);
In C++, use a vector. It's like an array but you can easily add and remove elements and it will take care of allocating and deallocating memory for you.
I know the title of the question says C, but you tagged your question with C and C++...
Another option for you is a linked list. You'll need to analyze how your program will use the data structure, if you don't need random access it could be faster than reallocating.
Your code in the last update should not compile, much less run. You're passing &x to LoadData. &x has the type of **words, but LoadData expects words* . Of course it crashes when you call realloc on a pointer that's pointing into stack.
The way to fix it is to change LoadData to accept words** . Thi sway, you can actually modify the pointer in main(). For example, realloc call would look like
*x = (words*) realloc(*x, sizeof(words)*2);
It's the same principlae as in "num" being int* rather than int.
Besides this, you need to really figure out how the strings in words ere stored. Assigning a const string to char * (as in str2 = "marley\0") is permitted, but it's rarely the right solution, even in C.
Another point: non need to have "marley\0" unless you really need two 0s at the end of string. Compiler adds 0 tho the end of every string literal.
For the test code: if you want to modify a pointer in a function, you should pass a "pointer to pointer" to the function. Corrected code is as follows:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
typedef struct
{
char *str1;
char *str2;
} words;
void LoadData(words**, int*);
main()
{
words **x;
int num;
LoadData(x, &num);
printf("%s %s\n", (*x[0]).str1, (*x[0]).str2);
printf("%s %s\n", (*x[1]).str1, (*x[1]).str2);
}
void LoadData(words **x, int *num)
{
*x = (words*) malloc(sizeof(words));
(*x[0]).str1 = "johnnie\0";
(*x[0]).str2 = "krapson\0";
*x = (words*) realloc(*x, sizeof(words) * 2);
(*x[1]).str1 = "bob\0";
(*x[1]).str2 = "marley\0";
*num = *num + 1;
}
Every coder need to simplify their code to make it easily understood....even for beginners.
So array of structures using dynamically is easy, if you understand the concepts.
// Dynamically sized array of structures
#include <stdio.h>
#include <stdlib.h>
struct book
{
char name[20];
int p;
}; //Declaring book structure
int main ()
{
int n, i;
struct book *b; // Initializing pointer to a structure
scanf ("%d\n", &n);
b = (struct book *) calloc (n, sizeof (struct book)); //Creating memory for array of structures dynamically
for (i = 0; i < n; i++)
{
scanf ("%s %d\n", (b + i)->name, &(b + i)->p); //Getting values for array of structures (no error check)
}
for (i = 0; i < n; i++)
{
printf ("%s %d\t", (b + i)->name, (b + i)->p); //Printing values in array of structures
}
scanf ("%d\n", &n); //Get array size to re-allocate
b = (struct book *) realloc (b, n * sizeof (struct book)); //change the size of an array using realloc function
printf ("\n");
for (i = 0; i < n; i++)
{
printf ("%s %d\t", (b + i)->name, (b + i)->p); //Printing values in array of structures
}
return 0;
}
If you want to grow the array dynamically, you should use malloc() to dynamically allocate some fixed amount of memory, and then use realloc() whenever you run out. A common technique is to use an exponential growth function such that you allocate some small fixed amount and then make the array grow by duplicating the allocated amount.
Some example code would be:
size = 64; i = 0;
x = malloc(sizeof(words)*size); /* enough space for 64 words */
while (read_words()) {
if (++i > size) {
size *= 2;
x = realloc(sizeof(words) * size);
}
}
/* done with x */
free(x);
Here is how I would do it in C++
size_t size = 500;
char* dynamicAllocatedString = new char[ size ];
Use same principal for any struct or c++ class.

Resources