Hey Guys so basically I'm writing a program where you prompt the user how many first names he wants to store. You then create the pointer based on his response. After, you prompt him how many first names he wants to add to the pointer. So basically I'm trying to expand the same array by using realloc but I'm not sure if I'm using it correctly or not(Apparently not since it's not working)
Basically I want the first names that the user already inputted to stay as is, and for the program to start off asking the user for the next first name he wants to input. For example if the first time the user enters the number 2. Then he writes two names and then asks to add 2 more names, the program should keep the first 2 names and be ready to store at firstNames[2]. Thanks!
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void)
{
int amount;
//Asking user for number of records to input
printf("Please indicate the number of records you want to enter:");
scanf("%d", &amount);
printf("\n");
//Declaring dynamic array for first names
char **firstNames = (char**)malloc(amount * sizeof(char));
for (int i = 0; i < amount; i++)
{
firstNames[i] = (char*)malloc(1 * sizeof(char));
}
//Prompting user for first name, last name, and scores
for (int count = 0; count < amount; count++)
{
printf("Enter first name for user %d:", count + 1);
scanf("%s", firstNames[count]);
}
int add;
printf("How many users do you want to add now:");
scanf("%d", &add);
int sum = amount + add;
firstNames = (char**)realloc(firstNames, sum * sizeof(char));
for (int count = amount; count < sum; count++)
{
printf("Enter first name for user %d:", count + 1);
scanf("%s", firstNames[count]);
}
return 0;
}
There are a number of areas where you are having trouble. As mentioned in my original comment, you must first allocate the memory correctly, before worrying about reallocating. Rather than calling realloc and assigning the return to firstNames, it is far better to create a temporary pointer to assign the return from realloc to. In the event realloc fails, it returns NULL. That means all existing data is lost because you just assigned firstNames = NULL. Using the temporary pointer allows your to test if the realloc succeeded before assigning the reallocated space to firstNames.
Going thought your code, there are several additional areas that can be improved. Always initialize your variables (to zero is fine). Why? Attempting to read from an uninitialized variable is Undefined Behavior (bad). Your input routines are critical. Especially using scanf and reading int/char data. You must insure you empty the input buffer before calling scanf again or it will use the newline (that results from pressing [enter]) as your next value causing your program to skip over the scanf statements that follow. You can use scanf to consume the newline to properly flush the input buffer (stdin) with careful choice of the format string.
When you get input validate it! Knucklehead users will enter anything.
When initializing pointer to pointers it is often better to use calloc for the initial allocation instead of malloc. calloc initializes all values to (zero) again preventing a read from an uninitialized variable.
Regarding program flow, you will want to minimize the number of loops and eliminate unnecessary blocks of code. If for no other reason, to lessen that chance of mistake. Think through your loops. You used multiple loops to count to the same number when all your operations could take place in one. This isn't a knock, this is just a suggestion to help you simplify your code.
When you ask for how many more users to enter before reallocating, what happens if 0 is entered? How should you handle it? Better to check and respond the way you want to, than leave it up to what the computer considers potentially undefined.
Those are the main points. I put together an example with those thoughts incorporated. It can always be improved, but I think this will help you handle the input issue and give you an example of simplifying the flow. While not necessary, I created a couple of functions to handle the printing and freeing of memory. Remember, if you allocate memory, you are responsible for keeping track of it and freeing it when your are done. Enough! Here is the example. Let me know if you have questions.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void print_users (char **array, int sz); /* funciton to print users */
void free_memory (char **array, int sz); /* function to free memory */
int main (void)
{
int amount = 0; /* always intialize all variables */
int i = 0;
//Asking user for number of records to input
printf("\nEnter the number of records to enter: ");
scanf("%d%*c", &amount); /* read int, discard newline */
printf("\n");
if (!amount) /* validate records to enter */
{
fprintf (stderr, "\nerror: invalid input.\n\n"); /* if realloc fails, error and exit */
return 1;
}
//Declaring dynamic array for first names
char **firstNames = calloc (amount, sizeof (*firstNames)); /* using your variable for sizeof (x) */
for (i = 0; i < amount; i++) /* prevents mistaken sizeof (wrong) */
{ /* calloc initializes to NULL (zero) */
firstNames[i] = malloc (sizeof (**firstNames)); /* same for malloc */
//Prompting user for first name, last name, and scores
printf (" first name for user %d : ", i + 1);
scanf ("%m[^\n]%*c", &firstNames[i]); /* read string and newline (discarded) */
}
int add = 0;
printf ("\nAny *additional* users to add: ");
scanf ("%d%*c", &add); /* read int, discard newline */
printf ("\n");
if (!add)
{
print_users (firstNames, amount);
free_memory (firstNames, amount);
return 0;
}
int sum = amount + add;
char **tmp = NULL;
tmp = realloc (firstNames, sum * sizeof (*firstNames)); /* never realloc actual pointer to data */
/* if realloc fails pointer set to NULL */
if (!tmp) /* validate realloc succeeded */
{
fprintf (stderr, "\nerror: reallocation failed.\n\n"); /* if realloc fails, error and exit */
return 1;
}
firstNames = tmp; /* now assign to pointer */
for (i = amount; i < sum; i++) /* why new var count, use i */
{
firstNames[i] = malloc (sizeof (**firstNames)); /* allocate new Names, prompt & store */
printf (" first name for user %d : ", i + 1);
scanf ("%m[^\n]%*c", &firstNames[i]);
}
print_users (firstNames, sum);
free_memory (firstNames, sum);
return 0;
}
void print_users (char **array, int sz)
{
printf ("\nUsers firstNames:\n\n"); /* output users collecte in firstNames */
int i = 0;
for (i = 0; i < sz; i++)
printf (" user[%d] %s\n", i, array[i]);
}
void free_memory (char **array, int sz)
{
int i = 0;
for (i = 0; i < sz; i++) /* free allocated memory */
if (array[i])
free (array[i]);
if (array)
free (array);
}
Example Usage:
$ ./bin/reallocptr
Enter the number of records to enter: 2
first name for user 1 : Jack
first name for user 2 : Jill
Any *additional* users to add: 2
first name for user 3 : Mike
first name for user 4 : Moe
Users firstNames:
user[0] Jack
user[1] Jill
user[2] Mike
user[3] Moe
Note: See Addendum below, otherwise 4-bytes of memory will remain un-freed at exit.
Addendum
As was pointed out, only a single char was allocated to each of the firstName[i] pointers which was insufficient for the full strings assigned. Generally, in practice firstName[i] string allocation can be handled automatically with functions such as strdup or scanf with an appropriate format string. To fully handle the strings with scanf, the following changes are required:
//Declaring dynamic array for first names
char **firstNames = calloc (amount, sizeof (*firstNames)); /* using your variable for sizeof (x) */
for (i = 0; i < amount; i++) /* prevents mistaken sizeof (wrong) */
{ /* calloc initializes to NULL (zero) */
// firstNames[i] = malloc (sizeof (**firstNames)); /* let scanf auto-allocate */
//Prompting user for first name, last name, and scores
printf (" first name for user %d : ", i + 1);
scanf ("%m[^\n]%*c", &firstNames[i]); /* read string and newline (discarded) */
}
...
for (i = amount; i < sum; i++) /* why new var count, use i */
{
// firstNames[i] = malloc (sizeof (**firstNames)); /* allocate new Names, prompt & store */
printf (" first name for user %d : ", i + 1);
scanf ("%m[^\n]%*c", &firstNames[i]);
}
changes to free_memory()
void free_memory (char **array, int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
if (array[i])
free (array[i]);
if (array)
free (array);
}
output/verification:
Enter the number of records to enter: 2
first name for user 1 : Jack
first name for user 2 : Jill
Any *additional* users to add: 2
first name for user 3 : Larry
first name for user 4 : Moe
Users firstNames:
user[0] Jack
user[1] Jill
user[2] Larry
user[3] Moe
==13690==
==13690== HEAP SUMMARY:
==13690== in use at exit: 0 bytes in 0 blocks
==13690== total heap usage: 10 allocs, 10 frees, 468 bytes allocated
==13690==
==13690== All heap blocks were freed -- no leaks are possible
char **firstNames = (char**)malloc(amount * sizeof(char));
This is a double pointer and memory needs to be allocated for pointers which points to your information.
So it should be
char **firstNames = malloc(amount * sizeof(char *));/* No need to cast malloc() */
Later you are doing realloc() for this memory and it is necessary for you to take care of realloc() failures.So use a temporary pointer to store the return value of realloc() and assign the address back to your firstNames.
firstNames[i] = (char*)malloc(1 * sizeof(char));
You are allocating a single byte here in each iteration in order to read strings you need more memory than this.
Some corrections in your memory allocations.
#define SIZE 10
char **firstNames = malloc(amount * sizeof(char *));
for (i = 0; i < amount; i++)
{
firstNames[i] = malloc(SIZE * sizeof(char));
}
First malloc is allocating memory for no of entries you want to store.
Since the entry are pointers to another memory, use malloc(amount * sizeof(char *))
The second malloc is nothing but, allocating memory for the actual strings you wan to store.
Where SIZE is the no of bytes for each entry.
So now you want to add more users, so you use realloc.
int sum = amount + add;
firstNames = realloc(firstNames, sum * sizeof(char *));
/* Add the following to your code */
for (i = amount; i < sum; i++)
firstNames[i] = malloc(SIZE * sizeof(char));
printf("Total Names: \n");
for ( i = 0; i < sum; i++)
printf("%s\n", firstNames[i]);
Here the second malloc is used for allocating memory for the newly added users.
It starts from (i = amount), since till amount memory is already allocated.
Related
Initially an array is allocated to hold five temperatures. The user is now prompted for a new temperature and enters a value of -100.0 on completion to terminate the loop. If the user fills the array, the program should use malloc() to dynamically allocate a new array twice its size and copy the old values into the new array. Release the old array and continue reading the new array. The loop that reads the value is a while loop that terminates when reading stops the value -100.0. In loop, you need to check to see if you have reached the end of the currently allocated array and have code to perform the new allocation, copy, and recycle.
First, Let me share some ideas. The overall design structure should follow the following steps: (1) create a new array, (2) copy the old data into the new array, and (3) free the old array.
My initial idea was to use memcpy () to copy floating point numbers from the old array into the new one. But after consulting the data, this method is not feasible. So I changed my mind and tried to implement the function with for loop, but the intelligent prompt said THAT I could not use the names of the dynamic array I created in the while loop outside the while loop, and I fell into this problem.
Here is my code.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
float *temperature = (float *)malloc(sizeof(float));
float arr[5] ={11.5,12.4,14.6,21.5,30.9}; // an array is allocated to hold five temperatures.
printf("Please enter the temperature: \n"); // let the user enter the new temperatures
int arr_index = 0;
scanf("%f", &arr[arr_index]); // get the first number
while (arr[arr_index] != -100.0) {
arr_index++;
if (arr_index >= 5) {
break; // reached the limit
}
int length = sizeof(arr) / sizeof(arr[0]);
float *new_temperature = malloc(sizeof(float) * (length * 2));
memcpy(new_temperature,temperature);
free(temperature);
temperature = new_temperature;
scanf("%f", &arr[arr_index]); // get the next number
}
printf("%.1f\n",temperature);
free(temperature);
return 0;
}
I really don't have any idea how to implement these features.
Thank you all.
Couple of things wrong with your code.
You will , probably, go out of bounds of the arr array.
arr_index++;, this line will keep on increasing arr_index until the user decides to enter -100.0.
So you rely on the user not to enter more than 5 floats. (Don't ever do that)
Instead, check that you don't go out of bounds before asking for new input, you also call scanf() twice, which doesn't really add up. You just need to call it once, outside the loop, and once at the end.
scanf("%f",&arr[arr_index]); // get the first number
while (arr[arr_index] != -100.0) {
arr_index++;
if (arr_index >= 5) break; // reached the limit
int length = sizeof(arr) / sizeof(arr[0]);
float *new_temperature = malloc(sizeof(float)*(length*2));
free(temperature);
temperature = new_temperature;
scanf("%f",&arr[arr_index]); // get the next number
}
You can't access a variable outside of it's scope
while (arr[arr_index] != -100.0) {
scanf("%f",&arr[arr_index]);
arr_index++;
int length = sizeof(arr) / sizeof(arr[0]);
// vvvv Defined in the while scope ({})
float *new_temperature = malloc(sizeof(float)*(length*2));
free(temperature);
temperature = new_temperature;
scanf("%f",&arr[arr_index]);
}
// Can't access it here
If you want to copy the data to that array, you have to do it inside the while loop.
Your logic can be simplified
You statically allocate an array of 5 floats that you never really make use of. Instead, you overwrite the original values.
What you could do is, have one dynamically allocated array, use realloc() if you ever need to expand/shrink.
Although, there is nothing stopping you from calling malloc() again with double size and then using memmcpy() or memmove().
#include <stdio.h>
#include <stdlib.h>
int main(void) {
float *temperature = NULL;
int arr_index = 0, size = 5;
temperature = malloc(size * sizeof(float)); // cast is redundant
if (temperature == NULL) { // malloc failed
fprintf(stderr, "malloc: failed to allocate memory\n");
exit(1);
}
printf("Please enter the temperature: \n"); // let the user enter the new temperatures
if (scanf("%f", &temperature[arr_index]) != 1) { // check the return value of scanf
fprintf(stderr, "scanf: failed to convert to float\n");
exit(1);
}
while (temperature[arr_index] != -100.0f) { // f suffix denotes a floating point number
arr_index++; // increment index
if (arr_index >= size) { // need more space - call realloc
size *= 2; // double the size
float *new_tmp = realloc(temperature, size * sizeof(float));
if (new_tmp == NULL) { // realloc failed
fprintf(stderr, "realloc: failed to allocate memory\n");
// clean up - good practise overall not really necessary here
free(temperature);
exit(1);
}
temperature = new_tmp;
}
// get a new number
if (scanf("%f", &temperature[arr_index]) != 1) {
fprintf(stderr, "scanf: failed to convert to float\n");
exit(1);
}
}
// iterate the array and print each element
for (int i = 0; i < arr_index; i++) {
printf("%.1f\n", temperature[i]);
}
free(temperature);
return 0;
}
Edit:
If you have to use memcpy() or memmove() you have to call malloc() again after doubling the size, then just copy arr_index * sizeof(float) bytes (since arr_index denotes the size of the array, to get the actual size in bytes, multiply that with the size of float).
You have to #include <string.h>
while (temperature[arr_index] !=-100.0f) { // f suffix denotes a floating point number
arr_index++; // increment index
if (arr_index >= size) { // need more space - call malloc
size *= 2; // double the size
float *new_tmp = malloc(size * sizeof(float));
if (new_tmp == NULL) { // malloc failed
fprintf(stderr, "malloc: failed to allocate memory\n");
// clean up - good practise overall not really necessary here
free(temperature);
exit(1);
}
// memcpy syntax is similar
// copy arr_index * sizeof(float) bytes from temp to new_tmp
memmove(new_tmp, temperature, arr_index * sizeof(float));
free(temperature);
temperature = new_tmp;
}
// get a new number
if (scanf("%f", &temperature[arr_index]) != 1) {
fprintf(stderr, "scanf: failed to convert to float\n");
exit(1);
}
}
I want to create a simple array of integers having 10 elements.
I'm using dynamic memory to allocate a space in memory and whenever I exceed that amount It will call realloc to double its size.
Whenever I type 'q' it will exit the loop and print the array.
I know my program is full of bugs, so please guide me into where I'm going wrong.
/* Simple example of dynamic memory allocation */
/* When array reaches 10 elements, it calls
realloc to double the memory space available */
#include <stdio.h>
#include <stdlib.h>
#define SIZE_ARRAY 10
int main()
{
int *a;
int length=0,i=0,ch;
a= calloc(SIZE_ARRAY, sizeof(int));
if(a == NULL)
{
printf("Not enough space. Allocation failed.\n");
exit(0);
}
printf("Fill up your array: ");
while(1)
{
scanf("%d",&ch);
if(ch == 'q') //possible mistake here
break;
a[length]=ch;
length++;
if(length % 10 == 0) //when length is 10, 20, 30 ..
{
printf("Calling realloc to double size.\n");
a=realloc(a, 2*SIZE_ARRAY*sizeof(int));
}
}
printf("You array is: \n");
for(;i<length;i++)
printf("%d ",a[i]);
return 0;
}
Whenever I type 'q' the program crashes. I'm a beginner so I know I'm doing a dumb mistake. Any help would be greatly appreciated.
You should not double the memory every realloc() that can get very large, very quick. You normally expand the memory by small chunks only. realloc() also has the nasty habit to use another part of the memory if the old one cannot be elongated enough. If that fails you loose all of your data in the old memory. This can be avoided by using a temporary pointer to point to the new memory and swap them after a successful allocation. That comes with the cost of just on extra pointer (mostly 4 or 8 bytes) and a swap (needing only a handful of CPU cycles at most. Be careful with x86's xchg it uses a lock in case of multiple processors which is quite expensive!)
#include <stdio.h>
#include <stdlib.h>
// would normally be some small power of two, like
// e.g.: 64 or 256
#define REALLOC_GROW 10
int main()
{
// a needs to be NULL to avoid a first malloc()
int *a = NULL, *cp;
// to avoid complications allocated is int instead of size_t
int allocated = 0, length = 0, i, ch, r;
printf("Fill up your array: ");
while (1) {
// Will also do the first allocation when allocated == length
if (allocated <= length) {
// realloc() might choose another chunk of memory, so
// it is safer to work on copy here, such that nothing is lost
cp = realloc(a, (allocated + REALLOC_GROW) * sizeof(int));
if (cp == NULL) {
fprintf(stderr, "Malloc failed\n");
// but we can still use the old data
for (i = 0; i < length; i++) {
printf("%d ", a[i]);
}
// that we still have the old data means that we need to
// free that memory, too
free(a);
exit(EXIT_FAILURE);
}
a = cp;
// don't forget to keep the amount of memory we've just allocated
allocated += REALLOC_GROW;
}
// out, if user typed in anything but an integer
if ((r = scanf("%d", &ch)) != 1) {
break;
}
a[length] = ch;
length++;
}
printf("Your array is: \n");
// keep informations together, set i=0 in the loop
for (i = 0; i < length; i++) {
printf("%d ", a[i]);
}
fputc('\n', stdout);
// clean up
free(a);
exit(EXIT_SUCCESS);
}
If you play around a bit with the start-value of allocated, the value of REALLOC_GROW and use multiplication instead of addition in the realloc() and replace the if(allocated <= length) with if(1) you can trigger the no-memory error and see if it still prints what you typed in before. Now change the realloc-on-copy by using a directly and see if it prints the data. That still might be the case but it is not guaranteed anymore.
I wrote a code for managing a library; the compilation is done but during the simulation I obtained an Allocation error (case2) and I don't know why.
The first case works correctly but if I entered more than one name in the first case, the second case doesn't work.
What did I do wrong? I hope I was clear enough.
typedef struct {
char name[80];
char **books;
int books_num;
} Subscription;
int main() {
// Variables declaration:
int option = 0, subs_num = 0, i = 0, books_num = 0;
Subscription *subs_library;
char **books;
char subs_new_name[80], book_new_name[80];
printf("Choose an option\n");
do {
scanf("%d", &option);
switch (option) {
case 1:
printf("Case 1: enter a new name\n");
scanf("%s", subs_new_name);
if (subs_num == 0) {
subs_library = malloc(sizeof(Subscription));
} else {
subs_library = realloc(subs_library, sizeof(Subscription));
}
strcpy(subs_library[subs_num].name, subs_new_name);
subs_library[subs_num].books_num = 0;
subs_num++;
printf("ADDED\n");
break;
case 2:
printf("Case 2: enter the book name\n");
scanf("%s", book_new_name);
if (books_num == 0) {
books = malloc(sizeof(char*));
books[books_num] = malloc(80 * sizeof(char));
} else {
books = realloc(books, sizeof(char*));
books[books_num] = malloc(80 * sizeof(char));
}
if (books[books_num] == NULL) {
printf("Allocation Error\n");
exit(1);
}
strcpy(books[books_num], book_new_name);
books_num++;
printf("ADDED\n");
break;
}
} while (option != 7);
return 0;
}
Your code to reallocate the arrays is incorrect. You do not allocate enough room for the new array sizes. When you reallocate these arrays, you pass the size of a single element, therefore the array still has a length of 1 instead of subs_num + 1. The size passed to realloc should be the number of elements times the size of a single element in bytes.
Initialize subs_library and books to NULL and change your array reallocations:
if (subs_num == 0) {
subs_library = malloc(sizeof(Subscription));
} else {
subs_library = realloc(subs_library, sizeof(Subscription));
}
Into this:
subs_library = realloc(subs_library, (subs_num + 1) * sizeof(*subs_library));
And do the same for books, change:
if (books_num == 0) {
books = malloc(sizeof(char*));
books[books_num] = malloc(80 * sizeof(char));
} else {
books = realloc(books, sizeof(char*));
books[books_num] = malloc(80 * sizeof(char));
}
To this:
books = realloc(books, (books_num + 1) * sizeof(*books));
books[books_num] = malloc(80 * sizeof(char));
Or simpler:
books = realloc(books, (books_num + 1) * sizeof(*books));
books[books_num] = strdup(book_new_name);
I guess the problem is with scanf reading a string only until a separator, in your case - a whitespace separating multiple names entered. The characters after separator remain in the input buffer and get immediately processed by other calls to scanf.
You should consider using getline for reading name(s) and checking return values from other calls to scanf.
The problem is your reallocation calls. For example you do
realloc(books,sizeof(char*))
This reallocates the memory pointed to be books to be one pointer to character in size, which is exactly what you already have. This will lead you to index out of bounds of the allocated memory, something which is undefined behavior.
If you want to allocate more than one element you need to multiply the base type size with the number of elements you want to allocate, e.g.
realloc(books, (books_num + 1) * sizeof(char *))
Your reallocation realloc(books, sizeof(char *)) only allocates the size of one pointer char *, not the size of the enlarged array that you need:
books=realloc(books,sizeof(char*));
You need to multiply the size of a pointer (char *) by the number of books you plan on storing in the array. You maintain the number of books in books_num.
As Joachim Pileborg said, with every allocation/reallocation, you want this to be one more than the current size. For the first allocation (malloc()), you want to allocate for one book, which is 1 times sizeof(char *). This happens to be equivalent to your existing code, which is fine. But the reallocation (realloc()) reallocates for the same size every time (only enough for one pointer), so you're not enlarging the allocation. You need to multiply the size required for one pointer (sizeof(char *)) by the number of pointers you want, which is books_num + 1. As in Joachim's answer, this is
books = realloc(books, (books_num + 1)*sizeof(char *));
This will enlarge the allocation of the array books by one more pointer. Then, on the next line you correctly allocate a string of size 80.
Your subs_library has the same reallocation issue.
Less frequent reallocation
You might want to resize an allocation less frequently. In this situation, you are reallocating every time you add an entry. One simple technique to reduce the number of reallocations is to double the allocation size every time it gets full. But you have to maintain the allocation size (capacity) and check for it whenever you add something. For example:
char **buffer; /* buffer of pointers to char */
int capacity = 1; /* number of elements allocated for */
int size = 0; /* number of elements actually used */
Then the initial allocation is
/* Initial allocation */
buffer = malloc(capacity*sizeof(*buffer));
And to add some char *new_item to the buffer
/* When adding an element */
if ( size == capacity ) {
/* Double allocation every time */
capacity *= 2;
/* Reallocate the buffer to new capacity */
realloc(buffer, capacity*sizeof(*buffer));
}
/* Item will fit, add to buffer */
buffer[size++] = new_item;
Notice that I have used sizeof(*buffer) instead of sizeof(char *). This makes the compiler figure out what the type and size is. That way, if I change the type of buffer for some reason, I don't have to change more places in the code. Another thing that I left out for brevity is that you should always check the return values to make sure they are not NULL.
I need to create program that takes input from stdin in this format:
abcde //number of characters in word = number of words => square shape
fghij
klmno
pqrst
uvwxy
// \n separates first half from second
word1word //any amount of characters, any amount of words
word
word2
sdf
// \n to end input
My code works, but only about 50% of the time. I have couple of example inputs, that I use for testing, but for some of them my readwords function fails.
Here is my function, that reads words. Since I have no idea how many words or how long they are going to be, I use dynamic arrays and getchar() function.
void readWords(char **p,int *n,int w) /* before calling: n = 50; w = 20; p = 50x20 char array */
{
int i = 0,j = 0,x;
char tmp,prevtmp;
while (1)
{
prevtmp = tmp;
tmp = getchar();
if ((prevtmp == '\n' && tmp == '\n') || feof(stdin))
break; /* no more words to read */
if (tmp == '\n') /* end of word */
{
p[i][j] = '\0'; /* add \0 to create string format */
i++;
j = 0;
if (i == *n) /* if there is more words than there is space for them, double the size */
if (realloc(p,*n*2) != NULL)
*n*=2;
continue;
}
p[i][j] = tmp;
j++;
if (j == w) /* if width of word is larger than allocated space, double it */
{
for (x = 0; x < *n;x++);
if(realloc (p[x],w*2) != NULL);
w=w*2;
}
}
*n = i;
}
This is example of input for which this works (note:this function only reads second half after line with only \n):
dsjellivhsanxrr
riemjudhgdffcfz
<skipping>
atnaltapsllcelo
ryedunuhyxhedfy
atlanta
saltlakecity
<skipping 15 words>
hartford
jeffersoncity
And this is input that my function doesn't read properly:
<skipping>
...oywdz.ykasm.pkfwb.zazqy...
....ynu...ftk...zlb...akn....
missouri
delaware
<skipping>
minnesota
southdakota
What my function reads from this input:
e
yoming
xas
florida
lvania
ana
ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ
There is no difference between those two inputs (except different words and different amount and length of words), the first half gets read properly no matter what, but only the second half bugs out. How do I fix this?
P.S. sorry for long post, in case you want to see full input without skipped bytes, here is pastebin: http://pastebin.com/hBGn2tej
realloc() returns the address of the newly allocated memory, it does not update the argument passed into it. So this (and the other use of realloc()) is incorrect:
if (realloc(p,*n*2) != NULL)
and will results in the code accessing memory incorrectly, causing undefined behaviour. Store the result of realloc() to a temporary variable and check for non-NULL before updating p. The argument to realloc() also indicates the number of bytes, not the number of elements so the size argument calculation is incorrect as p is an array of char* so it should be realloc(p, sizeof(char*) * (*n * 2));. However, the change to p would not be visible to the caller. Also note that the only legal arguments to realloc() are pointers obtained from a previous call to malloc(), realloc() or calloc(). The comment p = 50x20 char array in the code suggests this is not the case.
Here is a small example that allocates an array of char* which should be helpful:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void f(char*** p)
{
/* Allocate space for two 'char*' elements.
Add a NULL pointer element as sentinel value
so caller knows where to find end of list. */
*p = malloc(sizeof(**p) * 3);
/* Allocate space for the two strings
and populate. */
(*p)[0] = malloc(10);
(*p)[1] = malloc(10);
strcpy((*p)[0], "hello");
strcpy((*p)[1], "world");
(*p)[2] = NULL;
/* Add a third string. */
char** tmp = realloc(*p, sizeof(**p) * 4);
if (tmp)
{
*p = tmp;
(*p)[2] = malloc(10);
strcpy((*p)[2], "again");
(*p)[3] = NULL;
}
}
int main()
{
char** word_list = 0;
f(&word_list);
if (word_list)
{
for (int i = 0; word_list[i]; i++)
{
printf("%s\n", word_list[i]);
free(word_list[i]);
}
}
free(word_list);
return 0;
}
Additionally:
prevtmp has an unknown value upon its first use.
getchar() actually returns an int and not a char.
gcc 4.5.1 c89
I have written this source code for my better understanding of malloc and calloc.
I understand, but just have a few questions.
dev = malloc(number * sizeof *devices);
is equal to this calloc. I am not concerned about clearing the memory.
dev = calloc(number, sizeof *devices);
What is that exactly, compared to doing this 5 times in a while loop:
dev = malloc(sizeof *devices);
I guess the first one and the second is creating a pointer to 5 struct device. And the third is creating a single pointer to a struct device?
My program illustrates this 3 different methods compiled and ran with valgrind --leak-check=full.
Many thanks for any advice.
#include <stdio.h>
#include <stdlib.h>
struct Devices {
#define MAX_NAME_SIZE 80
size_t id;
char name[MAX_NAME_SIZE];
};
struct Devices* create_device(struct Devices *dev);
void destroy_device(struct Devices *dev);
int main(void)
{
size_t num_devices = 5;
size_t i = 0;
struct Devices *device = NULL;
struct Devices *dev_malloc = NULL;
struct Devices *dev_calloc = NULL;
for(i = 0; i < num_devices; i++) {
device = create_device(device);
/* Assign values */
device->id = i + 1;
sprintf(device->name, "Device%zu", device->id);
/* Print values */
printf("ID ----- [ %zu ]\n", device->id);
printf("Name --- [ %s ]\n", device->name);
/* Test free */
destroy_device(device);
}
printf("\n");
dev_malloc = malloc(num_devices * sizeof *dev_malloc);
for(i = 0; i < num_devices; i++) {
/* Assign values */
dev_malloc->id = i + 1;
sprintf(dev_malloc->name, "dev_malloc%zu", dev_malloc->id);
/* Print values */
printf("ID ----- [ %zu ]\n", dev_malloc->id);
printf("Name --- [ %s ]\n", dev_malloc->name);
}
/* Test free */
destroy_device(dev_malloc);
printf("\n");
dev_calloc = calloc(num_devices, sizeof *dev_calloc);
for(i = 0; i < num_devices; i++) {
/* Assign values */
dev_calloc->id = i + 1;
sprintf(dev_calloc->name, "dev_calloc%zu", dev_calloc->id);
/* Print values */
printf("ID ----- [ %zu ]\n", dev_calloc->id);
printf("Name --- [ %s ]\n", dev_calloc->name);
}
/* Test free */
destroy_device(dev_calloc);
return 0;
}
struct Devices* create_device(struct Devices *dev)
{
/* Not checking for memory error - just simple test */
return dev = malloc(sizeof *dev);
}
void destroy_device(struct Devices *dev)
{
if(dev != NULL) {
free(dev);
}
}
calloc(a,b) and malloc(a*b) are equivalent except for the possibility of arithmetic overflow or type issues, and the fact that calloc ensures the memory is zero-byte-filled. Either allocated memory that can be used for an array of a elements each of size b (or vice versa). On the other hand, calling malloc(b) a times will results in a individual objects of size b which can be freed independently and which are not in an array (though you could store their addresses in an array of pointers).
Hope this helps.
malloc(n) allocates n bytes plus padding and overhead.
calloc(m, n) allocates m*n bytes plus padding and overhead, and then zero's the memory.
That's it.
edited for clarity
I guess the first one and the second is creating a pointer to 5 struct device. And the third is creating a single pointer to a struct device?
The first one malloc(number * sizeof(*devices)) would allocate enough memory to store number of Devices. As others have mentioned, you can treat this block like an array of Device. The pointer you get back will point to the beginning of the block.
int number = 5;
Device *ptr = malloc(number * sizeof(*ptr));
/* stuff */
free(ptr);
The second one that uses calloc does the same thing, while also initializing the memory to 0. Again, you can use treat the block like an array of Device.
int number = 5;
Device *ptr = calloc(number, sizeof(*ptr));
/* stuff */
free(ptr);
The third one, looping 5 times, would result in 5 different pointers to 5 different blocks large enough to store one Device each. This also means each of the 5 pointers has to be free'ed individually.
Device *ptrs[5];
for(int i = 0; i < 5; ++i)
{
ptrs[i] = malloc(sizeof(*ptrs[i]));
}
/* stuff */
for(int i = 0; i < 5; ++i)
{
free(ptrs[i]);
}
The first two create an array of 5 devices in contiguous memory. The last malloc, done five times, will create 5 individual devices which are not guaranteed to be in contiguous memory.
All three loops in your program use only one struct Devices object at a time. The later ones allocate extra memory as though they are going to use multiple objects, but then keep overwriting the beginning of that memory. If you tried to use the object with ID 1 after setting up the object with ID 2, you would find there is no longer any object with ID 1.
Instead, you could do something like this to treat the allocated memory as an array of structs:
dev_malloc = malloc(num_devices * sizeof *dev_malloc);
for (i=0; i<num_devices; i++) {
/* Assign values */
dev_malloc[i].id = i + 1;
sprintf(dev_malloc[i].name, "dev_malloc%zu", dev_malloc[i].id);
/* Print values */
printf("ID ----- [ %zu ]\n", dev_malloc[i].id);
printf("Name --- [ %s ]\n", dev_malloc[i].name);
}
free(dev_malloc);
Look at an implementation of calloc to see the differences. It's probably something like this:
// SIZE_MAX is defined in stdint.h from C99
void *calloc( size_t N, size_t S)
{
void *ret;
size_t NBYTES;
// check for overflow of size_t type
if (N > SIZE_MAX / S) return NULL;
NBYTES = N * S;
ret = malloc( NBYTES);
if (ret != NULL)
{
memset( ret, 0, NBYTES);
}
return ret;
}
As you point out, calloc zeroes out the memory is allocates, while malloc doesn't.
Your examples 1 & 2 each allocate a single contiguous block of five structs (and returns a pointer to that block), whereas example 3 allocates five separate blocks of one struct each (and gives you five pointers unrelated to one another.)