(C) Using Arrays with Dynamic memory allocation - c

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.

Related

copy floating point numbers from a dynamic array to another dynamic array in c

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);
}
}

After creating an array through dynamic allocation, a problem occurs when changing the memory size through realloc in C

I am practicing C language.
I wanted to use dynamic allocation to use only the size of the string I input as memory and check whether the input string was properly saved.
So, I wrote the following code using malloc and realloc functions.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void str_copy(char* str_array_f) {
void* tmp;
char buf;
unsigned char arr_size = 1;
unsigned char arr_cur = 0;
while ((buf = getchar())) {
if (buf == '\n') {
break;
}
str_array_f[arr_cur++] = (char)buf;
tmp = realloc(str_array_f, ((arr_size++) * sizeof(char)) + sizeof(char));
if (tmp != 0) {
str_array_f = tmp;
}
else {
printf("memory leak error occur! \n");
break;
}
}
str_array_f[arr_size - 1] = 0x00;
}
void main() {
int contiune = 1;
while (contiune) {
char* str_array = malloc(sizeof(char) + sizeof(char));
printf("Please type something : ");
str_copy(str_array);
printf("'str_array' have this : %s \n", str_array);
printf("-------------------------------------------------\n");
if (str_array[0] == '1') {
contiune = 0;
}
free(str_array);
}
}
And, as a result of the performance,
The following problems have occurred.
Strange values sometimes appear from the 5th character of the intermittently printed value
(To reproduce this issue, it is recommended to remove the while loop and try repeatedly)
In the case of repeatedly receiving a value by using the while loop, an error occurs after 4 repetitions.
If the allocated memory of tmp, which is a void type pointer, is released after line 22(e.g., 'free(tmp);'), when executed, no output and an error occurs immediately.
For the above 3 problems, I am not sure what is the cause and how to fix it.
Please let me know if there is a solution.
And, if there is a bad coding method in my code in terms of efficiency or various aspects, I would appreciate it if you let me know.
*Programming execution environment : Visual studio 2019
to explain what you're doing wrong I'm going to use a minimal example here
void change_x(int x) {
x = 2;
}
int main() {
int x = 1;
change_x(x);
printf("%i\n", x); // it'll print 1 not 2
return 0;
}
here the integer x is copied when the function is called and changing it won't really change the x in main. similarly you are doing in your code that str_array_f = tmp; it really won't change the str_array but the copied value. and you're trying to free a pointer that was reallocated before.
the fix for the example above is not to pass the value x instead pass the address of x (which is equivalent to pass by reference in other languages)
void change_x(int* x) {
*x = 2;
}
int main() {
int x = 1;
change_x(&x);
printf("%i\n", x); // it'll print 1 not 2
return 0;
}
and for your code
void str_copy(char** str_array_f) {...} // change the parameter
*str_array_f = tmp; // de reference and use it.
str_copy(&str_array); // call with it's address
And one more thing, don't reallocate more often it's not efficient. instead just just allocate your "array" type with a minimum size and when it's filled reallocate it with the size of 2 times of it (or 1.5 if you like)

how to keep using malloc?

I have a file which stored a sequence of integers. The number of total integers is unknown, so I keep using malloc() to apply new memory if i read an integer from the file.
I don't know if i could keep asking for memory and add them at the end of the array. The Xcode keeps warning me that 'EXC_BAD_EXCESS' in the line of malloc().
How could i do this if i keep reading integers from a file?
int main()
{
//1.read from file
int *a = NULL;
int size=0;
//char ch;
FILE *in;
//open file
if ( (in=fopen("/Users/NUO/Desktop/in.text","r")) == NULL){
printf("cannot open input file\n");
exit(0); //if file open fail, stop the program
}
while( ! feof(in) ){
a = (int *)malloc(sizeof(int));
fscanf(in,"%d", &a[size] );;
printf("a[i]=%d\n",a[size]);
size++;
}
fclose(in);
return 0;
}
Calling malloc() repeatedly like that doesn't do what you think it does. Each time malloc(sizeof(int)) is called, it allocates a separate, new block of memory that's only large enough for one integer. Writing to a[size] ends up writing off the end of that array for every value past the first one.
What you want here is the realloc() function, e.g.
a = realloc(a, sizeof(int) * (size + 1));
if (a == NULL) { ... handle error ... }
Reworking your code such that size is actually the size of the array, rather than its last index, would help simplify this code, but that's neither here nor there.
Instead of using malloc, use realloc.
Don't use feof(in) in a while loop. See why.
int number;
while( fscanf(in, "%d", &number) == 1 ){
a = realloc(a, sizeof(int)*(size+1));
if ( a == NULL )
{
// Problem.
exit(0);
}
a[size] = number;
printf("a[i]=%d\n", a[size]);
size++;
}
Your malloc() is overwriting your previous storage with just enough space for a single integer!
a = (int *)malloc(sizeof(int));
^^^ assignment overwrites what you have stored!
Instead, realloc() the array:
a = realloc(a, sizeof(int)*(size+1));
You haven't allocated an array of integers, you've allocated one integer here. So you'll need to allocate a default array size, then resize if you're about to over run. This will resize it by 2 each time it is full. Might not be in your best interest to resize it this way, but you could also reallocate each for each additional field.
size_t size = 0;
size_t current_size = 2;
a = (int *)malloc(sizeof(int) * current_size);
if(!a)
handle_error();
while( ! feof(in) ){
if(size >= current_size) {
current_size *= 2;
a = (int *)realloc(a, sizeof(int) * current_size);
if(!a)
handle_error();
}
fscanf(in,"%d", &a[size] );;
printf("a[i]=%d\n",a[size]);
size++;
}
The usual approach is to allocate some amount of space at first (large enough to cover most of your cases), then double it as necessary, using the realloc function.
An example:
#define INITIAL_ALLOCATED 32 // or enough to cover most cases
...
size_t allocated = INITIAL_ALLOCATED;
size_t size = 0;
...
int *a = malloc( sizeof *a * allocated );
if ( !a )
// panic
int val;
while ( fscanf( in, "%d", &val ) == 1 )
{
if ( size == allocated )
{
int *tmp = realloc( a, sizeof *a * allocated * 2 ); // double the size of the buffer
if ( tmp )
{
a = tmp;
allocated *= 2;
}
else
{
// realloc failed - you can treat this as a fatal error, or you
// can give the user a choice to continue with the data that's
// been read so far.
}
a[size++] = val;
}
}
We start by allocating 32 elements to a. Then we read a value from the file. If we're not at the end of the array (size is not equal to allocated), we add that value to the end of the array. If we are at the end of the array, we then double the size of it using realloc. If the realloc call succeeds, we update the allocated variable to keep track of the new size and add the value to the array. We keep going until we reach the end of the input file.
Doubling the size of the array each time we reach the limit reduces the total number of realloc calls, which can save performance if you're loading a lot of values.
Note that I assigned the result of realloc to a different variable tmp. realloc will return NULL if it cannot extend the array for any reason. If we assign that NULL value to a, we lose our reference to the memory that was allocated before, causing a memory leak.
Note also that we check the result of fscanf instead of calling feof, since feof won't return true until after we've already tried to read past the end of the file.

Change a dynamic 2D char array through a function in C?

I'm creating this sample run so I can better understand how I can edit dynamic arrays through other functions, but I started running into segfaults once I added the secondary function.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void other_side(char ***funk);
int main()
{
int i;
char *argv[11] = {"fish", "dish", "lags", "fags", "shag", "cool", "bean", "rekt", "noon", "coon", "lolz"};
char **yep, **nop;
yep = malloc(10 * sizeof *yep);
if(!yep) { // <-----------------added check for malloc error
printf("Error: failure to allocate memory\n");
exit(1);
}
printf("10 times %lu\n\n", sizeof *yep);
for(i = 0; i<10; i++) {
yep[i] = strdup(argv[i]);
printf("%s is in yep.\n", *(yep+i));
}
nop = realloc(yep, 11 * sizeof *yep); //you reallocate to the new total size.
if(nop == NULL) {
printf("Error: failure to allocate memory\n")
exit(1);
}
yep = nop;
*(yep+10) = strdup(argv[10]);
printf("Last but certainly not least, %s is in yep.\n", *(yep+10));
printf("Now to send yep over to the other side and have its values changed.\n");
other_side(&yep);
printf("Did it change?\n\n");
for(i=0; i<11; i++)
printf("%s is in yep.\n", *(yep+i));
for(i=0; i<11; i++) { //issue fixed when added strdup() above, previously static
free(*(yep+i));
}
free(yep);
return 0;
}
void other_side(char ***funk)
{
char *arr[11] = {"dude","yeah","gnar","nice","epic","need","more", "word","four","this","test"};
int i;
for(i=0; i<11; i++) {
**(funk+i) = strdup(arr[i]); //added strdup() here as well
printf("%s is currently in yep.\n", **(funk+i));
}
printf("\n");
}
A couple things I noticed with this is that Valgrind notices an unnecessary free when I try to free the 11th block of memory to my array in main(). I'm not sure if that's my issue, but I also noticed that the function will only change two words before it leads to a segmentation fault.
Edit Notes: Since the edit I still get segfaults, but valgrind has been a bit more clear with what is happening. (Bad permissions for mapped region at address 0x400B18)
Your order of precedence regarding operators is important, and you missed a pair of parens to ensure it is done correctly. This: **(funk+i) means this: *(funk[i]), not (*funk)[i] which is what you want.
This:
**(funk+i) = strdup(arr[i]);
printf("%s is currently in yep.\n", **(funk+i));
Should be this:
*((*funk)+i) = strdup(arr[i]);
printf("%s is currently in yep.\n", *((*funk)+i));
and frankly, it is considerably easier to read as:
(*funk)[i] = strdup(arr[i]);
printf("%s is currently in yep.\n", (*funk)[i]);
I leave the rest of the memory management to you to fix. (i.e. the leaks from the dynamic memory pointed to by all the pointers you're overwriting in the above loop code).

Reallocating memory for a struct array in C

I am having trouble with a struct array. I need to read in a text file line by line, and compare the values side by side. For example "Mama" would return 2 ma , 1 am because you have ma- am- ma. I have a struct:
typedef struct{
char first, second;
int count;
} pair;
I need to create an array of structs for the entire string, and then compare those structs. We also were introduced to memory allocation so we have to do it for any size file. That is where my trouble is really coming in. How do I reallocate the memory properly for an array of structs? This is my main as of now (doesn't compile, has errors obviously having trouble with this).
int main(int argc, char *argv[]){
//allocate memory for struct
pair *p = (pair*) malloc(sizeof(pair));
//if memory allocated
if(p != NULL){
//Attempt to open io files
for(int i = 1; i<= argc; i++){
FILE * fileIn = fopen(argv[i],"r");
if(fileIn != NULL){
//Read in file to string
char lineString[137];
while(fgets(lineString,137,fileIn) != NULL){
//Need to reallocate here, sizeof returning error on following line
//having trouble seeing how much memory I need
pair *realloc(pair *p, sizeof(pair)+strlen(linestring));
int structPos = 0;
for(i = 0; i<strlen(lineString)-1; i++){
for(int j = 1; j<strlen(lineSTring);j++){
p[structPos]->first = lineString[i];
p[structPos]->last = lineString[j];
structPos++;
}
}
}
}
}
}
else{
printf("pair pointer length is null\n");
}
}
I am happy to change things around obviously if there is a better method for this. I HAVE to use the above struct, have to have an array of structs, and have to work with memory allocation. Those are the only restrictions.
Allocating memory for an array of struct is as simple as allocating for one struct:
pair *array = malloc(sizeof(pair) * count);
Then you can access each item by subscribing "array":
array[0] => first item
array[1] => second item
etc
Regarding the realloc part, instead of:
pair *realloc(pair *p, sizeof(pair)+strlen(linestring));
(which is not syntactically valid, looks like a mix of realloc function prototype and its invocation at the same time), you should use:
p=realloc(p,[new size]);
In fact, you should use a different variable to store the result of realloc, since in case of memory allocation failure, it would return NULL while still leaving the already allocated memory (and then you would have lost its position in memory). But on most Unix systems, when doing casual processing (not some heavy duty task), reaching the point where malloc/realloc returns NULL is somehow a rare case (you must have exhausted all virtual free memory). Still it's better to write:
pair*newp=realloc(p,[new size]);
if(newp != NULL) p=newp;
else { ... last resort error handling, screaming for help ... }
So if I get this right you're counting how many times pairs of characters occur? Why all the mucking about with nested loops and using that pair struct when you can just keep a frequency table in a 64KB array, which is much simpler and orders of magnitude faster.
Here's roughly what I would do (SPOILER ALERT: especially if this is homework, please don't just copy/paste):
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
void count_frequencies(size_t* freq_tbl, FILE* pFile)
{
int first, second;
first = fgetc(pFile);
while( (second = fgetc(pFile)) != EOF)
{
/* Only consider printable characters */
if(isprint(first) && isprint(second))
++freq_tbl[(first << 8) | second];
/* Proceed to next character */
first = second;
}
}
int main(int argc, char*argv[])
{
size_t* freq_tbl = calloc(1 << 16, sizeof(size_t));;
FILE* pFile;
size_t i;
/* Handle some I/O errors */
if(argc < 2)
{
perror ("No file given");
return EXIT_FAILURE;
}
if(! (pFile = fopen(argv[1],"r")))
{
perror ("Error opening file");
return EXIT_FAILURE;
}
if(feof(pFile))
{
perror ("Empty file");
return EXIT_FAILURE;
}
count_frequencies(freq_tbl, pFile);
/* Print frequencies */
for(i = 0; i <= 0xffff; ++i)
if(freq_tbl[i] > 0)
printf("%c%c : %d\n", (char) (i >> 8), (char) (i & 0xff), freq_tbl[i]);
free(freq_tbl);
return EXIT_SUCCESS;
}
Sorry for the bit operations and hex notation. I just happen to like them in such a context of char tables, but they can be replaced with multiplications and additions, etc for clarity.

Resources