memcpy() not working as expected when copying pointers - c

I want to copy the content of a char **pointer to another pointer. My attempt was:
void test(char **arr, int len) {
printf("1: ");
printArr(arr, len);
char ***res = malloc(sizeof(char **));
res[0] = malloc(sizeof(char *) * len);
memcpy(&res[0], arr, len);
printArr(res[0], len);
Here I just wanted to copy the contents of arr, which holds several strings, to r[0] whereby len denotes the number of elements in arr. However, when inspecting res[0] I realised that it only stores two times null. As one can tell I'm a very beginner and have been learning C since a few days, so onc can expect simple mistakes.

char ***res = malloc(sizeof(char **));
res[0] = malloc(sizeof(char *) * len);
memcpy(&res[0], arr, len);
The first line allocates space for a single char ** and makes res point at it
The second line allocates space for an array of len pointers to char and makes res[0] point at it.
The third line copies len byes from arr over the top of the memory pointed at by res, overwriting the result of the second malloc call and then scribbling over memory after the block allocated by the first malloc call.
You probably actually want something like
mempy(res[0], arr, len * sizeof(char*));
which will copy an array of len pointers (pointed at by arr) into the memory allocated by the second malloc call.

If this is an array of C strings that you need deep copied:
char** array_deep_copy(char **arr, int len) {
// calloc() makes "allocation of N" calculations more clear
// that this is N allocations of char* becoming char**
char **res = calloc(len, sizeof(char*));
for (int i = 0; i < len; ++i) {
// Use strdup() if available
res[i] = strdup(arr[i]);
}
return res;
}
Note that this needs a proper release function that will go through and recursively free() those cloned strings or this leaks memory.
If you only need a shallow copy:
char** array_shallow_copy(char **arr, int len) {
char **res = calloc(len, sizeof(char*));
// memcpy(dest, src, size) is usually more efficient than a loop
memcpy(res, arr, sizeof(char*) * len);
return res;
}
This one doesn't need a recursive free(), you can just free() the top-level pointer and you're done. This one shares data with the original, so if any of those pointers are released before this structure is then you'll have invalid pointers in it. Be careful!

Related

Malloc returns the same pointer twice [duplicate]

This question already has answers here:
How to change value of variable passed as argument?
(4 answers)
Closed 3 years ago.
The community reviewed whether to reopen this question 5 months ago and left it closed:
Original close reason(s) were not resolved
I allocate dynamic array in main like this:
char *arr = malloc(sizeof(char));
then in a random function I reallocate that array to n elements like:
arr = realloc(arr, n * sizeof(char));
then I do a random stuff with the array, and in another function I want to allocate one more array with n elements like this:
char *arr2 = malloc(n * sizeof(char));
but this malloc returns the same adress as arr. I tried everything, but still returns the same adress, so arr2 is pointing to arr. What am I doing wrong?
If i allocate new array again lets say arr3 with the same method, now it works and it gives me new adress.
Edit:
void reallocate(char *arr, int newLength) {
arr = realloc(arr, newLength * sizeof(char));
}
void fc1 (char *arr, int *length) {
char *temp = malloc(*length * sizeof(char));
strcpy(temp, arr);
int d;
scanf("%d", &d);
char *arr2 = malloc(d * sizeof(char)); //there it gives me the same adress
scanf("%s", arr2);
}
int main(void) {
char arr = malloc(sizeof(char));
int *length = malloc(sizeof(int));
*length = 10;
reallocate(arr, 10);
fc1(arr, length);
return 0;
}
We'd need to see the code to be sure, but here's two ways it can happen:
void func2(char *s)
{
do_something(s);
free(s); // we are done with it
}
void func1(void)
{
char * array = some_func();
func2(array); // Oops, func2 frees it
char * array2= malloc (...); // could get the same pointer
}
Here, func1 passes the pointer to func2 which frees it. It is an error for func1 to do anything with array after that point as it can be re-assigned.
And the second way:
void get_data(char *s)
{
char *data = // code to get some data from somewhere
s = realloc (s, strlen(data) + 1);
strcpy(s, data);
}
void func1(void)
{
char *ptr = malloc(12);
get_data(ptr);
// oops, ptr has the old value
char *ptr2 = malloc(12); // could get the same pointer
}
Here, get_data calls realloc but the caller still has the old pointer which realloc might free.
But we'd need to see the code to be sure.
Update:
I guessed right. This is the second way in my example above:
void reallocate(char *arr, int newLength) {
arr = realloc(arr, newLength * sizeof(char));
}
This looks exactly like my get_data function above. This function does not pass the new value of arr back to the caller, so the caller still has the old value which may be freed.
reallocate(arr, 10);
fc1(arr, length);
This looks exactly like my second func1 above. You pass arr to reallocate which may invalidate it, but then you pass the old value of arr to fc1. But reallocate may have freed it. There's a reason realloc returns the new value, but your reallocate function doesn't.

memory leak when concatenating strings into array

I have one function, alloc_str, which takes a string pointer and an array of pointers. It dynamically increases the size of the array by one and appends the string into the array. I have run a GDB debugger and highlighted my memory leak and const error below.
My expected input/output:
array = alloc_str(array, "test_1");
array = alloc_str(array, "test_2");
array = alloc_str(array, "test_3");
--> ["test_1", "test_2", "test_3"]
My alloc_str function:
char **alloc_str(char **existing, const char *add)
{
int length = 0; //find the length of the array
for (; existing[length]; length++)
{
}
//allocate memory to copy array array
char **existing_c = (char **)calloc(length + 2, sizeof(char *));
for (int i = 0; i < length; i++) //copy original array into new array
{
existing_c[i] = existing[i];
}
//possible memory leak error
strncat(existing_c, add, sizeof(existing_c) - strlen(existing_c) - 1);
existing_c[sizeof(existing_c)-1] = '\0';
//possible memory leak error
strncpy(existing, existing_c, sizeof(existing - 1));
s_copy[sizeof(destsize)-1] = '\0'; //error here
free(existing);
return existing_c;
}
void free_array(char **strings) //free's data in array, should be fine
{
int length = 0;
for (; strings[length]; length++)
{
}
strings = (char **)calloc(length + 2, sizeof(char *));
}
My main function:
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
int main(){ //should be fine
char **array = NULL;
char **test;
array = (char **)calloc(1, sizeof(char *)); //array has no strings yet
array = alloc_str(array, "test_1");
array = alloc_str(array, "test_2");
array = alloc_str(array, "test_3");
for (test = array; *test; test++)
{
printf("%s\n", *test);
}
free_array(array);
}
My error:
Subscript of pointer to function type 'void (const void *, void *, size_t)' (aka 'void (const void *, void *, unsigned long)')
There are multiple problems:
char **alloc_str(char **existing, const char *add)
{
int length = 0; //find the length of the array
for (; existing[length]; length++)
{
}
//allocate memory to copy array array
char **existing_c = (char **)calloc(length + 2, sizeof(char *));
for (int i = 0; i < length; i++) //copy original array into new array
{
existing_c[i] = existing[i];
}
////////////////////////////////////
//possible memory leak error
strncat(existing_c, add, sizeof(existing_c) - strlen(existing_c) - 1);
existing_c[sizeof(existing_c)-1] = '\0';
//possible memory leak error
strncpy(existing, existing_c, sizeof(existing - 1));
s_copy[sizeof(destsize)-1] = '\0'; //error here
////////////////////////////////////
free(existing);
return existing_c;
}
The part marked with //////////////////////////////////// does not make much sense.
You allocated an array of pointers. Don't treat it like a string. It is no string.
Instead simply assign the new pointer to the end of the array and add terminator again.
existing_c[length] = add;
existing_c[length+1] = NULL;
With that terminater you could use normal malloc instead of calloc because you assign all elements of the array anyway.
Besides the problem with allocation, you have another memory leak:
void free_array(char **strings) //free's data in array, should be fine
{
int length = 0;
for (; strings[length]; length++)
{
}
strings = (char **)calloc(length + 2, sizeof(char *));
}
You pass a pointer to an array of pointers. This array takes some memory that you allocated with calloc earlier.
Then you allocate a bit more memory and assign the address to local variable string.
This has two problems:
The memory that was allocated earlier is not freed.
The memory you allocate in this function is not accessible outside of that function.
In the end, your free_array function does not free anything but consumes more memory.
Another problem might be present with the strings that you store in that array.
In your example you use string literals. These are static objects and there is no need to free them.
If you will use your functions to store pointers to dynamically allocated string as well, you will need to take care about allocating and freeing the strings as well.
strncat() works on a memory buffer containing a NUL-terminated (aka "C") string:
char buf[10] = {'a', 'b', 'c', '\0'};
strncat(buf, "def", sizeof(buf) - strlen(buf) - 1);
assert(strcmp(buf, "abcdef") == 0); // buf now equals to "abcdef"
https://ideone.com/fWXk8C
(Well, the use of strlen() kinda killed the benefit of strncat() over good ol' strcat() but that's another story...)
So it's very different from what you want to do in your exercise. You actually don't need either of strncat() or strncpy().

String Arrays: Delete original array and then return copy

I am new with C and I am trying to understand allocating strings.
I am trying to create a function called adding_string. It takes in an array of zero or more strings that has a null in the final location. Next, it makes a shallow copy of the array that is + 1 location bigger, then appends a copy of the string str onto the array. Finally, it deletes the original array and returns the new copy
This is what I have so far:
char **adding_string(char **array, const char *str)
{
size_t num = strlen(str) + 1;
char *final= (char *)malloc(num);
strncpy(final, str, num);
free(array);
//The above code would create a copy of the string "str".
//Then it puts that into the array.
//Not sure if free(array); would be the right method
//Having issues with returning final too
return final;
}
In the main function, you would have something like:
char **array = NULL;
char **lines;
array = (char **)calloc(1, sizeof(char *));
array = adding_string(array, "help");
array = adding_string(array, "plz");
array = adding_string(array, "thanks");
for (lines = array; *lines; lines++)
{
printf("%s\n", *lines);
}
I'm not sure if free(array) would be the right method to use to delete the original array, and I'm having issues with returning the new copy.
When I try returning the new copy, I get:
warning: return from incompatible pointer type
which is because of:
return final;
Your adding_string makes no sense, you make a copy of str, free the memory
from array and return the new copy. The function should return a double pointer to char,
you are passing a single-pointer to char. All other values are lost, you are
leaking memory like crazy.
I'd rewrite your adding_string like this:
char **adding_string(char **array, const char *str)
{
char **tmp;
if(str == NULL)
return NULL;
// first make copy
size_t len = strlen(str);
char *strcopy = malloc(len+1);
if(strcopy == NULL)
return NULL;
// you've allocated enough memory for the copy
// no need of strncpy here
strcpy(strcopy, str);
// get the number of strings saved
size_t size = 0; // number of strings saved
if(array)
{
tmp = array;
while(*(tmp++))
size++;
}
// reallocate memory for array of strings
tmp = realloc(array, (size+2) * sizeof *tmp);
if(tmp == NULL)
{
// something went wrong, free the copy
free(strcopy);
return NULL;
}
tmp[size] = strcopy;
tmp[size+1] = NULL;
return tmp;
}
Note that in this version, if array is NULL, the function allocates the memory for the
array of strings. That's only a design choice, you could as well check that
array is not NULL and pass to adding_string a pre-allocated array of
strings. I think (and that's only my opinion) that is more elegant that
adding_string will create the first array. In this way, the code that
allocates memory is in one place only.
Now in your main
char **array = NULL;
char **lines;
// adding_string will allocate the memory for array when it's NULL
array = adding_string(array, "help");
array = adding_string(array, "plz");
array = adding_string(array, "thanks");
for (lines = array; *lines; lines++)
{
printf("%s\n", *lines);
}
Note that I do
tmp = realloc(array, (size+2) * sizeof *tmp);
size has the number of strings saved, that means that array
holds size+1 spaces, because the last one points to NULL. You are appending
one more strings, so you have to reallocate size+1+1 spaces, which is
size+2.
Please don't forget to free the memory afterwards.
The program below strictly follows your needs and intentions.
The array array is resized every time a new string is added. At the end of the program the proper cleanup of all allocated memory is done.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char ** adding_string(char **array, const char *str)
{
size_t num = strlen(str) + 1;
char *final = (char *)malloc(num); // allocate memory for the string `str`
strncpy(final, str, num); // create the copy of the `str`
int i=0;
for(i=0; array[i] !=NULL; i++) {} // find how many elements do we have in the array
array[i] = final; // add final to the first empty spot in the `array`
i++;
char ** new_array = calloc(1+i, sizeof(char *)); // allocate a new array 1 size bigger
memcpy(new_array, array, sizeof(char*)*i); // copy all the pointers
free (array); // no need for the old array
return new_array; // return a pointer to the new bigger array
}
int main(void)
{
char **array = NULL;
char **lines;
array = (char **)calloc(1, sizeof(char *)); // allocate array for 4 poiters if type (char *)
array = adding_string(array, "help");
array = adding_string(array, "plz");
array = adding_string(array, "thanks");
for (lines = array; *lines; lines++)
{
printf("%s\n", *lines);
free(*lines);
}
free (array);
return 0;
}
Output:
help
plz
thanks
This is different approach where
char *adding_string(const char *str)
returns a pointer (char *) to the copy of the string. The array has already preallocated memory to accommodate all string pointers.
A small program to demonstrate the concept:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *adding_string(const char *str)
{
size_t num = strlen(str) + 1;
char *final= (char *)malloc(num); // allocate memory for the string str
strncpy(final, str, num); // crreate the copy
return final; // return a pointer to created copy
}
int main(void)
{
char **array = NULL;
array = (char **)calloc(4, sizeof(char *)); // allocate array for 4 pointers if type (char *)
array[0] = adding_string("help");
array[1] = adding_string("plz");
array[2] = adding_string("thanks");
for (int i=0; i<3; i++ )
{
printf("%s\n", array[i]);
free(array[i]);
}
free (array);
return 0;
}
Output:
help
plz
thanks

Reading file to char** using fgets and memcpy

I'm trying to read a set of lines from a file to an array. I'm doing this to learn malloc and realloc.
#define MAX_LINE 301
char** read_file_lines(char* filename) {
char** ptr = NULL;
int max = 5;
int i = 0;
FILE *fp = fopen(filename, "r");
if(fp != NULL) {
char line[MAX_LINE];
while(fgets(line, MAX_LINE, fp) != NULL) {
/* allocate some extra memory for some more lines */
if(i == max) {
int new_max = max * 2;
int nr_bytes = new_max * sizeof(char) * MAX_LINE;
char **ptr2 = realloc(ptr, nr_bytes);
if(ptr2 != NULL) {
ptr = ptr2;
ptr2 = NULL;
max = new_max;
}
}
// ptr[i] = line;
// strcpy(ptr[i], line);
memcpy(ptr[i], line, strlen(line));
i++;
}
fclose(fp);
}
else {
printf("Error opening file %s\n", filename);
}
return ptr;
}
The code compiles. However, when it is executed, an error occurs (the program crashes).
I did some debugging and determined that the problem is in up in the memcpy () instruction. I had previously tried using strcpy, which also gives a similar problem.
I went to check memcpy ()'s protocolo and it is as followS:
void * memcpy ( void * destination, const void * source, size_t num );
Now, if ptr is char**, isn't ptr[i] equivalent to a char* ?
Thanks for your comments.
It looks like ptr isn't initialized to point to any memory at all. Also, you're not allocating any memory for the individual lines.
To initialize ptr, change the declaration to:
int max = 5;
char** ptr = malloc(max * sizeof(char*));
Try adding this before the call to memcpy:
ptr[i] = malloc(strlen(line) + 1);
and change the calculation for the realloc call:
int nr_bytes = new_max * sizeof(char*);
EDIT: To explain in more detail: ptr is a pointer to an array of pointers. You have to allocate memory for ptr (that is, enough memory just to store individual pointers). In addition to this, you also have to allocate each individual array of characters that the individual elements of ptr will point to.
The first change I suggested ensures that ptr always points to enough memory to hold 5 pointers (or more, once it's been realloc'd.)
The second change ensures that each member of ptr always points to valid memory before you try to access it as a pointer.
And the third change is required because ptr points to elements that are pointers to char, not char.
Nah. Arrays are not pointers. Pointers to pointers are not arrays of arrays. If you want a two-dimensional dynamic array, then you have to allocate memory for 1. the array of pointers that point to the individual lines, and 2. for the lines themselves too.
Problem is that at first execution memory is not allocated: i is 0, max is 5, the if condition is false and the realloc is never executed.

Resizing an array in C

Say I assigned an array like so:
char* array[]={"This"};
And then later I wanted to assign array[ ] a new value so that it stores "This" and "That," is there a way that I could change the size of array so that it could hold a new number of values?
No, you can't change the size of an array. You could use a dynamically allocated list of char* instead and realloc() as required:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
int main()
{
char** array = malloc(1 * sizeof(*array));
if (array)
{
array[0] = "This";
printf("%s\n------\n", array[0]);
char** tmp = realloc(array, 2 * sizeof(*array));
if (tmp)
{
array = tmp;
array[1] = "That";
printf("%s\n", array[0]);
printf("%s\n", array[1]);
}
free(array);
}
return 0;
}
See online demo: https://ideone.com/ng00k.
There is no way to resize an array. You can simply create a new array of size 2, then copy all the data from the previous one to the new one. realloc does it for you with dynamic memory. The better way is to use data structures such as LinkedLists or Vectors which you can find more about online.
You cannot resize array objects.
You would have to dynamically allocate the memory for array and extend it using realloc. Example:
size_t current_size = 0;
char **array = malloc((current_size + 1) * sizeof *array);
if (array)
{
array[current_size++] = "This";
}
...
/**
* If realloc cannot extend the buffer, it will return NULL and leave
* the original buffer intact; however, if we assign NULL back to array,
* we lose our handle to the original buffer, causing a memory leak, so
* we assign the result to a temporary variable.
*/
char **tmp = realloc(array, (current_size + 1) * sizeof *array)
if (tmp)
{
array = tmp;
array[current_size++] = "That";
}
else
{
// realloc failed to extend the buffer; original buffer
// is left intact.
}
Caveats:
realloc is a relatively expensive call, so you (generally) don't want to extend your buffer one element at a time like I did here. A more common strategy is to pick an initial starting size that covers most cases, and if you need to extend the buffer, double its size.
You could abstract the resize operation into a separate function, like so:
int addItem(char ***arr, char *newElement, size_t *count, size_t *bufSize)
{
if (*count == *bufSize)
{
// we've run out of room; extend the buffer
char **tmp = realloc(**arr, 2 * *bufSize * sizeof **arr);
if (tmp)
{
*arr = tmp;
*bufSize *= 2;
}
else
{
// could not extend the buffer; return failure code
return 0;
}
}
(*arr)[(*count)++] = newElement;
}
and call it as
#define N ... // initial array size
char **array = malloc(N * sizeof *array);
size_t bufSize = N;
size_t count = 0;
...
if (addItem(&array, "This", &count, &bufSize))
printf("# elements = %zu, buffer size = %zu\n", count, bufSize);
if (addItem(&array, "That", &count, &bufSize))
printf("# elements = %zu, buffer size = %zu\n", count, bufSize);
This is all untested and off the top of my head; no warranties express or implied. But it should be enough to point you in the right direction.
This is not possible. You can allocate an array of char*, though:
char **array = calloc(2, sizeof(char *));
array[0] = "This";
array[1] = "That";

Resources