I am new to C and I want to create a dynamic array to store strings. I wrote the code below for it but it didn't work. Array elements contain some ASCII chars instead of string.
I want historyArray[0] value to be "foo". How can I do that?
typedef struct {
char *historyCommand;
int usedSize;
int maximumSize;
} HistoryArray;
void CreateHistoryArray(HistoryArray *HistoryArray) {
HistoryArray->historyCommand = (char *) malloc(sizeof(char) * MAX_LEN);
HistoryArray->usedSize = 0;
HistoryArray->maximumSize = INITIAL_SIZE;
}
void ExpandHistoryArray(HistoryArray *HistoryArray, int newSize) {
int *newArray = (char *) malloc(sizeof(char) * newSize);
memcpy(newArray, HistoryArray->historyCommand, sizeof(char) * HistoryArray->maximumSize);
free(HistoryArray->historyCommand);
HistoryArray->historyCommand = newArray;
HistoryArray->maximumSize = newSize;
}
void AddHistoryValue(HistoryArray *HistoryArray, char historyCommand[]) {
strcpy(HistoryArray->historyCommand[HistoryArray->usedSize], historyCommand);
HistoryArray->usedSize++;
if (HistoryArray->usedSize == HistoryArray->maximumSize) {
ExpandHistoryArray(HistoryArray, HistoryArray->maximumSize * 2);
}
}
void freeHistoryArray(HistoryArray *a) {
free(a->historyCommand);
a->historyCommand = NULL;
a->usedSize = 0;
a->maximumSize = 2;
}
HistoryArray historyArray;
There are a number of problems in your code.
char *historyCommand is a pointer to a single string, not an array of strings. For a pointer to an array of strings you should use char **historyCommand.
You don't need to allocate the space for the individual strings when you create the HistoryArray. You can allocate the proper amount of space each time you add to the array, using the length of the string you're adding.
You should use realloc() instead of calling malloc(), memcpy(), and free(). This has the benefit that sometimes it can simply expand the memory it already allocated, so no copying will be needed.
When you're freeing the HistoryArray, you need to free all the strings. You shouldn't free historyCommand, because you set maximumSize = 2, and the other functions assume that this means there's room for 2 items there, which isn't true if you set historyCommand to NULL. So you should resize it to maximumSize to be consistent with the rest of the code.
Here's the new code:
typedef struct {
char **historyCommand;
int usedSize;
int maximumSize;
} HistoryArray;
void CreateHistoryArray(HistoryArray *HistoryArray) {
HistoryArray->historyCommand = malloc(INITIAL_SIZE * sizeof(char *));
HistoryArray->usedSize = 0;
HistoryArray->maximumSize = INITIAL_SIZE;
}
void ExpandHistoryArray(HistoryArray *HistoryArray, int newSize) {
HistoryArray->historyCommand = realloc(HistoryArray->historyCommand, newSize * sizeof(char *));
HistoryArray->maximumSize = newSize;
}
void AddHistoryValue(HistoryArray *HistoryArray, char historyCommand[]) {
historyCommand[HistoryArray->usedSize] = malloc(strlen(historyCommand) + 1);
strcpy(HistoryArray->historyCommand[HistoryArray->usedSize], historyCommand);
HistoryArray->usedSize++;
if (HistoryArray->usedSize == HistoryArray->maximumSize) {
ExpandHistoryArray(HistoryArray, HistoryArray->maximumSize * 2);
}
}
void freeHistoryArray(HistoryArray *a) {
for (int i = 0; i < a->usedSize; i++) {
free a->historyCommand[i];
}
a->usedSize = 0;
a->maximumSize = 2;
a->historyCommand = realloc(a->historyCommand, a->maximumSize * sizeof(char *));
}
HistoryArray historyArray;
Related
I want to initialize array of char arrays in a function:
void myFunction(char*** words)
{
int size = 3;
char** words_ = (char**) malloc(size * sizeof(char*));
for (int i=0; i< size; ++i)
{
// init each word words[i] with some value
}
*words = words_;
}
which I use in that way:
char** multiple_words;
myFunction(&multiple_words);
Is there any other way to write this code better/simpler?
(This code works BTW).
char** myFunction()
{
int size = 3;
char** words_ = (char**) malloc(size * sizeof(char*));
for (int i=0; i< size; ++i)
{
// init each word words[i] with some value
}
return words_;
}
char **words = myFunction();
You can simplify the malloc call itself a bit. You don't need to cast the result of malloc in C1, so that call could be simplified to
char** words_ = malloc(size * sizeof *words); // sizeof *words == sizeof (char *)
Always check the result of a malloc, calloc, or realloc call. Even though the likelihood of the request failing is small, it's not zero.
The words_ variable really serves no purpose, and at first glance looked like you were redeclaring the words function argument. It would be simpler to get rid of it entirely and just write
*words = malloc( sizeof **words * size ); // sizeof **words == sizeof (char *)
leaving us with
void myFunction(char*** words)
{
int size = 3;
*words = malloc(size * sizeof **words);
if ( *words )
{
for (int i=0; i< size; ++i)
{
// init each word words[i] with some value
}
}
}
At least as of the 1989 standard - if you're using an ancient K&R-era implementation or if you're compiling this code as C++, then a cast is required. However, if you're writing C++, then you shouldn't be using malloc anyway.
I would create abstractions depending on what I want to represent:
struct word {
char *letters;
};
struct sentence {
struct word *words;
};
// and then work with those abstractions:
int sentence_create(struct sentence *s) {
const size_t size = 3;
s->words = malloc(size * sizeof(*s->words));
if (s->words == NULL) goto ERR_WORDS;
s->words[0] = strdup("hello");
if (s->words[0] == NULL) goto ERR_0;
s->words[1] = strdup("world");
if (s->words[1] == NULL) goto ERR_1;
s->words[2] = NULL;
// success
return 0;
// goto error handling
free(s->words[1]);
ERR_1:
free(s->words[0]);
ERR_0:
free(s->words);
s->words = NULL;
ERR_WORDS:
return -ENOMEM;
}
int main() {
struct sentence sentence;
if (sentence_create(&sentence) != 0) {
fprintf(stderr, "Oh no!");
abort();
}
// TODO: sentence_destroy(&sentence) to free memory
}
Also see wiki.c2 Three Star Programmer
Not sure 100% whether it is easier or not:
typedef struct {
char **words;
} WordList;
void myFunc(WordList *wordList) {
...
wordList->words = malloc(numberOfWords * sizeof(char *));
// strSource : this string comes from somewhere in your code
...
for (int i = 0; i < numberOfWords; i++) {
int numberOfChars = strlen(strSource);
(wordList->words)[i] = malloc((numberOfChars + 1) * sizeof(char));
(wordList->words)[i][numberOfChars] = '\0';
...
strcpy((wordList->words)[i], strSource)
...
}
...
}
int main() {
WordList wordList;
...
myFunc(&wordList);
...
}
This is gonna get icky no matter how you write it.
A char** is fine to use for pointing at the first item in an array of char*, each pointing at a string of individual length.
It is best if the function can return a char**, but if that isn't possible, then...
We'd have to return a char** through parameters, means we have to write char***. Three levels of indirection is always questionable, but this specific case is about the only valid use for it. Or the lesser evil at least, since...
Some makeshift struct wrapper that does nothing but hiding away the *** isn't making the code any better or more readable. That's very similar to hiding pointers behind typedef, not recommended.
The least messy way to write that might be something like this:
void heard (char*** word, size_t size)
{
char** the_word = malloc( sizeof(char*[size]) );
for(size_t i=0; i<size; i++)
{
const char* str = "bird"; // some random data from somewhere
the_word[i] = malloc (sizeof str); // allocate room for individual strings
strcpy(the_word[i], str);
}
*word = the_word;
}
Then call it as:
char** word;
heard(&word, 3);
for(size_t i=0; i<3; i++)
{
puts(word[i]);
}
The most proper solution is probably to use a complete string container class which handles all of this for you. Then you'd just declare arrays of strings and don't worry about all the details of allocating things manually.
I've a function that returns a list of dynamically allocated arrays that can be looped upon. The caller is freeing the list but still getting leak warnings from valgrind.
I'm using this version so the caller doesn't need to know the length of the array and it can simply loop upon until NULL.
char **getList() {
char **list = (char *[]){"John Doe", "Jane Doe", NULL};
char **values = malloc(sizeof(char*) * 1);
int i = 0;
while(*list) {
values[i] = malloc(sizeof(char) * (strlen(*list) + 1));
strcpy(interests[i++], *list);
list++;
}
values[i] = NULL;
return values;
}
Caller level
int main() {
char **values = getList();
while(*values) {
// do something with the *value
values++;
}
free(values); // free up the values
return 0;
}
values is incremented, so free(values); after that is invalid.
The strings pointed at by elements of values is not freed.
Try this:
int main() {
char **value_list = getList(), **values;
values = value_list;
while(*values) {
// do something with the *value
values++;
}
// free up the values
for (values = value_list; *values; values++) {
free(*values);
}
free(value_list);
return 0;
}
Also note that
char **values = malloc(sizeof(char*) * 1);
in the function getList() should be
char **values = malloc(sizeof(char*) * 3);
to avoid out-of-bounds writes.
So, here's my logic:
This is some text:
char *text;
Then this is array of texts:
char **arr;
Then array of these arrays is:
char ***arr2d;
And if I want a function to modify it, it needs to accept it as:
char ****arr2d;
And within the function use it as:
*arr2d = (e.g. allocate);
So if I want to create 2D array like this and make the first row, first column contain just a letter 'a', then why does this not work?
#define COLUMNS 7
void loadTable(char ****table)
{
*table = (char ***) malloc(sizeof(char**));
if (!*table) {
printf("Allocation error for table rows.");
return;
}
*table[0] = (char**) malloc(COLUMNS * sizeof(char*));
if (!*table[0]) {
printf("Allocation error for table columns.");
return;
}
*table[0][0] = (char*) malloc(2 * sizeof(char));
*table[0][0][0] = (char)(97);
*table[0][0][1] = '\0';
}
int main()
{
char ***table;
loadTable(&table);
return 0;
}
You would need only 3 *** to do what you describe, not 4 ****. Be aware, there are methods to allow you to avoid excessive depth in terms of arrays of arrays of strings. And there are also good reasons to avoid excessively deep orders of arrays, not the least is the need to free(.) everything you allocate. That means exactly one call to free(.) for each and every call to [m][c][re]alloc(.).
But to address your question...
In general, to create new memory for a single array of a previously allocated memory, you could use a function prototyped as:
char * ReSizeBuffer(char **str, size_t origSize);
Where if say the previously allocated buffer were created as:
char *buf = calloc(origSize, 1);
...the usage could look like:
char *tmp = {0};
tmp = ReSizeBuffer(&buf, newSize); //see implementation below
if(!tmp)
{
free(buf);
return NULL;
}
buf = tmp;
///use new buf
...
Then if this works for a single array of char, then the prototype for allocating new memory for a previously allocated array of strings might look like this:
char ** ReSizeBuffer(char ***str, size_t numArrays, size_t strLens);
Where if say the previously allocated 2D buffer were created as:
char **buf = Create2DStr(size_t numStrings, size_t maxStrLen); //see implementation below
...the usage could look like:
char **tmp = {0};
tmp = ReSizeBuffer(&buf, numStrings, maxStrLen);
if(!tmp)
{
free(buf);
return NULL;
}
buf = tmp;
///use new buf
...
Implementations:
Implementation of ReSizeBuffer. This must be expanded if you desire to implement the second prototype:
char * ReSizeBuffer(char **str, size_t size)
{
char *tmp={0};
if(!(*str)) return NULL;
if(size == 0)
{
free(*str);
return NULL;
}
tmp = (char *)realloc((char *)(*str), size);
if(!tmp)
{
free(*str);
return NULL;
}
*str = tmp;
return *str;
}
Implementation of Create2DStr might look like this:
char ** Create2DStr(size_t numStrings, size_t maxStrLen)
{
int i;
char **a = {0};
a = calloc(numStrings, sizeof(char *));
for(i=0;i<numStrings; i++)
{
a[i] = calloc(maxStrLen + 1, 1);
}
return a;
}
I just stumbled upon this old question of mine and spotted the problem immediately. Here is the answer:
The number of asterisks is actually correct, the problem is operator evaluation. Specifically, all lines of code of this form:
*table[0]
should have been written as:
(*table)[0]
I am trying implement a method that adds a given string to an array that ends with a NULL pointer. This is what I have so far but I am getting an error saying that the pointer being realloc'd was not allocated.
int main(void)
{
char **strings = init_array();
strings = add_string(strings, "one");
strings = add_string(strings, "two");
return 1;
}
char **init_array(void)
{
char **array = malloc(sizeof(char *));
array[0] = NULL;
return array;
}
char **add_string(char **array, const char *string)
{
unsigned int size = 0;
while (*array) {
size++;
array++;
}
char **newarr = (char **)realloc(array, sizeof(char *) * (size + 2));
newarr[size] = malloc(strlen(string)+1);
strcpy(newarr[size], string);
newarr[size+1] = NULL;
return newarr;
}
The issue is array++. You have to pass realloc the same value malloc returned (your array argument), but you modify it during the loop, so it'll work only the first time (because *array will immediately false). You could use:
size_t size;
for(size = 0; array[size]; size++);
And leave the rest untouched.
In your while (*array) loop you are incrementing not only the size, but also the array pointer itself. As a result, at the end of the loop size contains the length of the array, and the array pointer points to the last (NULL) element. This pointer was never allocated, (it points within an allocated block,) therefore it is not a valid pointer to reallocate. (And definitely that's not what you intended to do.)
So, just don't do array++ within that loop.
Your loop that calculates the number of strings in the array also advances the variable itself. You could use a temporary variable instead:
char **temp = array;
while (*temp)
...
Or separate the counting into a function.
BTW you don't need a casting when using realloc, for the same reason you don't do the casting with malloc. This is not a bug, but it better be consistent.
Summarizing all other answers given so far, adding some best practise tweaks, the relevant code should look like this:
char **add_string(char **array, const char *string)
{
char ** newarr;
size_t size = 0;
assert (NULL != string); /* Need to include assert.h */
if (NULL != array)
{
while (NULL != array[size])
{
++size; /* Just count, do not touch the pointer value allocated. */
}
}
newarr = realloc(array, (size + 2) * sizeof *newarr);
if (NULL == newarr) /* Test the outcome of reallocation. */
{
perror("realloc() failed"); /* Need to include stdio.h */
return NULL;
}
newarr[size] = malloc(strlen(string) + 1);
if (NULL == newarr[size])
{
perror("malloc() failed"); /* Need to include stdio.h */
/* Might want to clean up here and indicate the failure to the
caller by returning NULL. */
}
else
{
strcpy(newarr[size], string);
}
newarr[size+1] = NULL;
return newarr;
}
Or even tighter:
char **add_string(char **array, const char *string)
{
assert (NULL != string); /* Need to include assert.h */
{
size_t size = 0;
if (NULL != array)
{
while (NULL != array[size])
{
++size; /* Just count, do not touch the pointer value allocated. */
}
}
{
char ** newarr = realloc(array, (size + 2) * sizeof *newarr);
if (NULL == newarr)
{
perror("realloc() failed"); /* Need to include stdio.h */
}
if (NULL != newarr)
{
newarr[size] = malloc(strlen(string) + 1);
if (NULL == newarr[size])
{
perror("malloc() failed"); /* Need to include stdio.h */
}
else
{
strcpy(newarr[size], string);
}
newarr[size+1] = NULL;
}
return newarr;
}
}
}
The easiest way would be to preserve initial array pointer and use it to realloc memory.
int main(void)
{
char **strings = init_array();
strings = add_string(strings, "one");
strings = add_string(strings, "two");
return 1;
}
char **init_array(void)
{
char **array = malloc(sizeof(char *));
array[0] = NULL;
return array;
}
char **add_string(char **array, const char *string)
{
char** cache = array;
unsigned int size = 0;
while (*array) {
size++;
array++;
}
char **newarr = (char **)realloc(cache, sizeof(char *) * (size + 2));
newarr[size] = malloc(strlen(string)+1);
strcpy(newarr[size], string);
newarr[size+1] = NULL;
return newarr;
}
Another note - main function should return 0 on success.
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";