I would like to reallocate an array of string with a function. I write a very simple program to demonstrate here. I expect to get the letter "b" to be output but I get NULL.
void gain_memory(char ***ptr) {
*ptr = (char **) realloc(*ptr, sizeof(char*) * 2);
*ptr[1] = "b\0";
}
int main()
{
char **ptr = malloc(sizeof(char*));
gain_memory(&ptr);
printf("%s", ptr[1]); // get NULL instead of "b"
return 0;
}
Thank you very much!
The [] operator has high priority than *, so change the code like this will work right.
(*ptr)[1] = "b";
P.S. "\0" is unnecessary.
You should put parentheses around *ptr in gain_memory:
(*ptr)[1] = "b\0";
You're not allocating any memory for the actual strings in your array of strings, you need to do something like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void gain_memory(char ***ptr, int elem) {
*ptr = (char**)realloc(*ptr, 2*elem*sizeof(char*));
(*ptr)[1] = "b";
}
int main()
{
//How many strings in your array?
//Lets say we want 10 strings
int elem = 10;
char **ptr = malloc(sizeof(char*) * elem);
//Now we allocate memory for each string
for(int i = 0; i < elem; i++)
//Lets say we allocate 255 characters for each string
//plus one for the final '\0'
ptr[i] = malloc(sizeof(char) * 256);
//Now we grow the array
gain_memory(&ptr, elem);
printf("%s", ptr[1]);
return 0;
}
Related
I have been trying to solve this issue for whole day, and could not do it on my own. Searching the internet didn't help me solve it either
So, this the function prototype:
void invert(char **arr, int n);
First argument is an array of strings, and the second one is number of strings in an array.
This is my code:
#include <stdio.h>
#include <string.h>
void invert(char** arr, int n)
{
int i, j, len;
for(j=0;j<n;j++)
{
len=strlen(arr[j]);
for(i=0;i<len/2;i++)
{
char tmp = arr[j][i];
arr[j][i] = arr[j][len - i - 1];
arr[j][len - i - 1] = tmp;
}
}
}
int main()
{
int n=3, i;
char **arr;
arr[0]="John";
arr[1]="Doe";
arr[2]="Programmer";
invert(arr, n);
for(i=0;i<3;i++)
{
printf("%s ",arr[i]);
}
}
The code breaks when it reaches the line:
arr[j][i] = arr[j][len - i - 1];
and I can't figure out why.
The function receives an array of strings perfectly (tested it with some printf statements for characters of specific strings), and the char tmp succesfully recieves a correct character, but the program crashed when it reaches the line mentioned earlier. Printf statements after that line don't work.
Did I miss anything? Can someone explain what am I doing wrong? Thank you!
For starters this code snippet
char **arr;
arr[0]="John";
arr[1]="Doe";
arr[2]="Programmer";
invokes undefined behavior because the pointer arr is uninitialized and has an indeterminate value.
Moreover this approach in any case is wrong because you may not change string literals.
What you need is to declare a two-dimensional array as for example
enum { N = 11 };
//...
char arr[3][N] =
{
"John", "Doe", "Programmer"
};
In this case the function declaration will look like
void invert( char arr[][N], int n );
The enumeration must be declared before the function declaration.
Instead of the two-dimensional array you could declare an array of pointers like
char s1[] = "John";
char s2[] = "Doe";
char s3[] = "Programmer";
char * arr[3] = { s1, s2, s3 };
In this case the function declaration may be as shown in your question
void invert(char** arr, int n)
So what you need to do with minimal changes is to substitute this code snippet
char **arr;
arr[0]="John";
arr[1]="Doe";
arr[2]="Programmer";
for this code snippet
char s1[] = "John";
char s2[] = "Doe";
char s3[] = "Programmer";
char * arr[3] = { s1, s2, s3 };
To begin with, what you have here:
char **arr;
is a pointer to pointer to char.
Secondly, even if you had an array of pointers to char, like so :
char *arr[3];
And then assigning each string literal :
arr[0]="John";
arr[1]="Doe";
arr[2]="Programmer";
would still invoke Undefined behavior, since you are attempting to modify a string literal which is read only.
What you need is, either a 2D array of chars :
char arr[][100] = {"John", "Doe", "Programmer"};
and also change the function signature to :
void invert(char arr[][100], int n)
or you have to dynamically allocate memory and use a function like strcpy(), strdup(), memcpy() etc :
char **arr;
arr = malloc(n * sizeof(char *)); // or sizeof(*arr)
if (arr == NULL) {
fprintf(stderr, "Malloc failed to allocate memory\n");
exit(1);
}
arr[0] = strdup("John"); // good idea to also check if strdup returned null
arr[1] = strdup("Doe");
arr[2] = strdup("Programmer");
invert(arr, n);
for(i=0;i<3;i++)
{
printf("%s ",arr[i]);
}
for (i = 0; i < 3; i++) {
free(arr[i]);
}
free(arr);
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
I am working a function that needs to be re-entrant - the function is given a memory buffer as an argument and should use such buffer for all its memory needs. In other words, it can't use malloc, but rather should draw the memory the supplied buffer.
The challenge that I ran into is how to overlay an array of strings over a char array of given size (the buffer is supplied as char *), but my result is array of strings (char **).
Below is a repro:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFFER_SIZE 100
#define INPUT_ARRAY_SIZE 3
char *members[] = {
"alex",
"danny",
"max"
};
int main() {
// this simulates a buffer that is presented to my func
char *buffer = malloc(BUFFER_SIZE);
char *orig = buffer;
memset(buffer, NULL, BUFFER_SIZE);
// pointers will be stored at the beginning of the buffer
char **pointers = &buffer;
// strings will be stored after the pointers
char *strings = buffer + (sizeof(char *) * INPUT_ARRAY_SIZE);
for(int i = 0; i < INPUT_ARRAY_SIZE; i++) {
strncpy(strings, members[i], (strlen(members[i]) + 1));
// Need to store pointer to string in the pointers section
// pointers[i] = strings; // This does not do what I expect
strings += ((strlen(members[i]) + 1));
}
for (int i=0; i < BUFFER_SIZE; i++) {
printf("%c", orig[i]);
}
// Need to return pointers
}
With the problematic line commented out, the code above prints:
alex danny max
However, I need some assistance in figuring out how to write addresses of the strings at the beginning.
Of course, if there an easier way of accomplishing this task, please, let me know.
Here take a look at this.
/* conditions :
*
* 'buffer' should be large enough, 'arr_length','arr' should be valid.
*
*/
char ** pack_strings(char *buffer, char * arr[], int arr_length)
{
char **ptr = (char**) buffer;
char *string;
int index = 0;
string = buffer + (sizeof(char *) * (arr_length+1)); /* +1 for NULL */
while(index < arr_length)
{
size_t offset;
ptr[index] = string;
offset = strlen(arr[index])+1;
strcpy(string,arr[index]);
string += offset;
++index;
}
ptr[index] = NULL;
return ptr;
}
usage
char **ptr = pack_strings(buffer,members,INPUT_ARRAY_SIZE);
for (int i=0; ptr[i] != NULL; i++)
puts(ptr[i]);
I am learning C and trying to make a function that would create an array of arrays of strings.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void parse(char ***aoa)
{
char *string = calloc(9, sizeof(char)); //create a string of size 8+1
strcpy(string, "hi world"); // put text in that array
char **array = calloc(10, sizeof(char *)); //create an array of strings
aoa = calloc(10, sizeof(char *)); //create and array of arrays
aoa[0] = array; //assign string array to the 0th elements of new array
array[0] = string; //assign our string to 0th element of string carry
printf("%s\n", aoa[0][0]); //print 0th element of 0th array.
}
int main()
{
char ***array = NULL;
parse(array);
printf("%s\n", array[0][0]);
return 1;
}
The aoa (array of arrays) is on the heap so it should be the same for both methods. It does print "hi world" in the parse function, but gives Segmentation Fault in main, what is the problem with my code?
Obviously I need to free() everything and do error checking, but I removed it to show the gist of the problem
You reserve space for a local variable in parse(), you need to pass a pointer to array from main and then dereference inside the function:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void parse(char ****aoa)
{
char *string = calloc(9, sizeof(char)); //create a string of size 8+1
strcpy(string, "hi world"); // put text in that array
char **array = calloc(10, sizeof(char *)); //create an array of strings
*aoa = calloc(10, sizeof(char *)); //create and array of arrays
*(aoa[0]) = array; //assign string array to the 0th elements of new array
array[0] = string; //assign our string to 0th element of string carry
printf("%s\n", *(aoa[0][0])); //print 0th element of 0th array.
}
int main()
{
char ***array = NULL;
parse(&array);
printf("%s\n", array[0][0]);
return 1;
}
The problem tragically lies in your main. You send your variable array to the parse function as a value, hence, it is not changed by parse and stays NULL in the MAIN. The prototype of your function parse should be
void parse(char ****aoa)
and you should send it to the main like this :
parse(&array)
But you are overcomplicating the code. Try to initiate your array of arrays elsewhere instead of passing a pointer to it in the function. for example :
char ***getArray(void)
{
char ***ret; //the array you return
char *string = calloc(9, sizeof(char)); //create a string of size 8+1
strcpy(string, "hi world"); // put text in that array
char **array = calloc(10, sizeof(char **)); //create an array of strings
ret = calloc(10, sizeof(char ***)); //create and array of arrays
ret[0] = array; //assign string array to the 0th elements of new array
array[0] = string; //assign our string to 0th element of string carry
return (ret);
}
int main()
{
char ***array = getArray();
printf("%s\n", array[0][0]);
return (0);
}
And by the way, always verify that your allocations did not fail.
There is a sound rule of thumb saying that if you ever find yourself in need of more than two levels of indirection, then your program design is bad. ("Three star programming")
You are also using pointer-based lookup-tables rather than arrays. This causes segmentation and prevents you to regard the allocated result as a chunk of contiguous memory.
On top of that, your code has several other issues.
I would consider rewriting it from scratch and actually use multi-dimensional arrays instead. Here is a working example:
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
bool create_string_array2d (size_t x,
size_t y,
size_t max_length,
char (**string_array)[x][y][max_length]);
void print_string_array2d (size_t x,
size_t y,
size_t max_length,
char string_array[x][y][max_length]);
static void fill_with_junk (size_t x,
size_t y,
size_t max_length,
char string_array[x][y][max_length]);
int main()
{
const size_t X = 9;
const size_t Y = 10;
const size_t MAX_CHARS = sizeof("hi world xx yy");
char (*array)[X][Y][MAX_CHARS];
bool result;
result = create_string_array2d(X, Y, MAX_CHARS, &array);
if(result == false)
{
printf("out of memory, halt & catch fire");
return 0;
}
fill_with_junk(X, Y, MAX_CHARS, *array);
print_string_array2d(X, Y, MAX_CHARS, *array);
free(array);
return 0;
}
bool create_string_array2d (size_t x,
size_t y,
size_t max_length,
char (**string_array)[x][y][max_length])
{
*string_array = calloc(1, sizeof(char[x][y][max_length]));
return string_array != NULL;
}
void print_string_array2d (size_t x,
size_t y,
size_t max_length,
char string_array[x][y][max_length])
{
for(size_t i=0; i<x; i++)
{
for(size_t j=0; j<y; j++)
{
printf("%s\n", string_array[i][j] );
}
printf("\n");
}
}
static void fill_with_junk (size_t x,
size_t y,
size_t max_length,
char string_array [x][y][max_length])
{
for(size_t i=0; i<x; i++)
{
for(size_t j=0; j<y; j++)
{
char junk [sizeof("hi world xx yy ")] = "hi world ";
char num [sizeof("xx ")];
sprintf(num, "%.2d ", (int)i);
strcat(junk, num);
sprintf(num, "%.2d", (int)j);
strcat(junk, num);
strcpy(string_array[i][j], junk);
}
}
}
You never dereference aoa inside the function, so you just overwrite the argument's local value. Function arguments are passed by value in C. You also shouldn't need to alllocate two string arrays, that doesn't seem right.
There's a function. It is add_lexem and adds an element (char *) in the end of specified array and. If no memory left, it allocates some extra memory (100 * sizeof(char *)). That function causes segfault, which is the problem.
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
void add_lexem(char **lexems, int *lexemsc, int *lexem_n, const char *lexem)
{
if (*lexem_n >= *lexemsc) {
lexems = realloc(lexems, sizeof(char *) * (*lexemsc + 100));
*lexemsc += 100;
}
char *for_adding = malloc(sizeof(char) * strlen(lexem));
strcpy(for_adding, lexem);
lexems[*lexem_n] = for_adding;
(*lexem_n)++;
}
int main(void)
{
char **D = malloc(sizeof(char *) * 2);
int lexemsc = 2;
int lexem_n = 0;
add_lexem(D, &lexemsc, &lexem_n, "MEOW");
printf("%s\n", D[0]);
add_lexem(D, &lexemsc, &lexem_n, "BARK");
printf("%s\n", D[1]);
// in this place lexem_n becomes equal lexemsc
add_lexem(D, &lexemsc, &lexem_n, "KWARK");
printf("%s\n", D[2]);
return 0;
}
The output must be
MEOW
BARK
KWARK
but it is
MEOW
BARK
Segmentation fault (core dumped)
You're passing your lexeme parameter by value, when it should be by address:
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
// removed unused void ccat(char *str, char c)
void add_lexem(char ***lexems, int *lexemsc, int *lexem_n, const char *lexem)
{
if (*lexem_n >= *lexemsc) {
*lexems = realloc(*lexems, sizeof(char *) * (*lexemsc + 100));
*lexemsc += 100;
}
char *for_adding = malloc(sizeof(char) * strlen(lexem)+1);
strcpy(for_adding, lexem);
(*lexems)[*lexem_n] = for_adding;
(*lexem_n)++;
}
int main(void)
{
char **D = malloc(sizeof(char *) * 2);
int lexemsc = 2;
int lexem_n = 0;
add_lexem(&D, &lexemsc, &lexem_n, "MEOW");
printf("%s\n", D[0]);
add_lexem(&D, &lexemsc, &lexem_n, "BARK");
printf("%s\n", D[1]);
// in this place lexem_n becomes equal lexemsc
add_lexem(&D, &lexemsc, &lexem_n, "KWARK");
printf("%s\n", D[2]);
return 0;
}
Output
MEOW
BARK
KWARK
Note: Triple indirection (i.e. a 3-start-programming) is not something to enter into lightly, though it actually fits what you appear to be trying to do here. Read the above code carefully and make sure you understand how it works.
Edit: added terminator space for added string. (don't know why I missed it, since it was what everyone else seemed to be catching on first-review, duh).
Note: See #wildplasser's answer to this question. Honestly it is the best way to do this, as it tightens the relationship between the string pointer array and the magnitude of said-same. If it is possible to retool your code to use that model, you should do so, and in-so-doing select that answer as the the "correct" solution.
Alternative to avoid the three-star-programming: put everything you need inside a struct:
struct wordbag {
size_t size;
size_t used;
char **bag;
};
void add_lexem(struct wordbag *wb, const char *lexem)
{
if (wb->used >= wb->size) {
wb->bag = realloc(wb->bag, (wb->size+100) * sizeof *wb->bag );
wb->size += 100;
}
wb->bag[wb->used++] = strdup(lexem);
}
The main problem is that you are passing D to the function by value: the assignment
lexems = realloc(...);
has no effect on D. In cases when realloc performs reallocation, D becomes a dangling pointer, so dereferencing it becomes undefined behavior.
You need to pass D by pointer in the same way that you pass lexemsc and &lexem_n, so that the realloc's effect would be visible inside the main function as well.
In addition, your add_lexem does not allocate enough memory for the string being copied: strlen does not count the null terminator, so these two lines
char *for_adding = malloc(sizeof(char) * strlen(lexem));
strcpy(for_adding, lexem);
write '\0' one byte past the allocated space.
The problem may come from :
char *for_adding = malloc(sizeof(char) * strlen(lexem));
strcpy(for_adding, lexem);
try char *for_adding = malloc(sizeof(char) * (strlen(lexem)+1)); to leave some space for the '\0 character.
Edit : and #WhozCraig seems to be right !
Bye,