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";
Related
I'm trying to store a list of every mount point on a Linux system in a string array with C. I'm focused on this piece of code.
int i = 0;
char **mountslist = malloc(1024 * sizeof(char *));
/*
* Make sure that the number entries in the array are less than the allocated
* 1024 in the absurd case that the system has that many mount points.
*/
while (i < 1024 && (ent = getmntent(mounts))) {
/*
* The output of getmntent(mounts) goes down to
* the next mount point every time it is called.
*/
mountslist[i] = strdup(ent->mnt_dir);
i++;
}
I was wondering how I could dynamically allocate the number of entries in the mountslist array (currently statically set at 1024) to avoid that limit and wasting memory. If I had the final value of i when declaring mountslist I could use char *mountslist[i]; or char **mountslist = malloc(i * sizeof(char *));
You can use realloc to change the size of an allocated memory block:
int i = 0;
int len = 10;
char **mountslist = malloc(len * sizeof(char *));
while ((ent = getmntent(mounts)) != NULL) {
if (i >= len) {
len *= 2;
char **tmp = realloc(mountslist, len * sizeof(char *));
if (tmp) {
mountslist = tmp;
}
}
mountslist[i] = strdup(ent->mnt_dir);
i++;
}
As shown above, a good rule is to double the amount of space you allocate when you run out of space. That prevents excessive calls to realloc which may move the allocated memory each time.
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
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 have to write a function that adds the given string to array of strings.
char **add_string(char **array, const char *string)
{
array = realloc(array, (sizeof(string)) * sizeof(char *));
int i = 0;
while (array[i] != NULL){
i++;
}
array[i] = malloc((strlen(string) + 1));
strcpy(array[i], string);
array[i + 1] = NULL;
return array;
}
At the moment I get memory leaks according to Valgrid. I didn't use malloc correctly, but I can't figure out how to fix it. I would really appreciate your help.
The realloc allocates a new size given its second argument but you are not increasing the size, so basically every time you call your function it allocates the same size not increasing the area allocated.
you would need to pass the current number of strings in the array to add_string and then increment it in the add_string
char** add_string(char** array, int* size, const char* string)
{
char* newArray = realloc(array, (*size + 1) *sizeof(char*) );
newArray[*size] = malloc(strlen(string)+1);
strcpy(newArray[*size], string);
*size += 1;
...
}
You should also check if realloc succeeds or not by checking the return value.
Normally the above method is not a very effective way to handle increasing sizes since you are calling realloc every time and that is time consuming. Instead you should allocate in chunks then keep track of how much of the chunk you have used up, when the chunk is used up realloc a new one.
Your re-allocation of array is broken, it needs to search to figure out how long the array is, assuming it's always "tightly" allocated (with the last allocated element being the only one that is NULL).
char **add_string(char **array, const char *string){
int i = 0;
while (array[i] != NULL){
i++;//First, count the elements and find NULL element
}
array[i] = malloc(strlen(string) + 1);//or strdup(string);
strcpy(array[i], string);
char **temp;
temp = realloc(array, (i+1+1) * sizeof(char *));
if(temp == NULL){
perror("realloc at add_string");
} else {
array = temp;
array[i + 1] = NULL;
}
return array;
}
I'm used to PHP, but I'm starting to learn C. I'm trying to create a program that reads a file line by line and stores each line to an array.
So far I have a program that reads the file line by line, and even prints each line as it goes, but now I just need to add each line to an array.
My buddy last night was telling me a bit about it. He said I'd have to use a multidimensional array in C, so basically array[x][y]. The [y] part itself is easy, because I know the maximum amount of bytes that each line will be. However, I don't know how many lines the file will be.
I figure I can make it loop through the file and just increment an integer each time and use that, but I feel that there might be a more simple way of doing it.
Any ideas or even a hint in the right direction? I appreciate any help.
To dynamically allocate a 2D array:
char **p;
int i, dim1, dim2;
/* Allocate the first dimension, which is actually a pointer to pointer to char */
p = malloc (sizeof (char *) * dim1);
/* Then allocate each of the pointers allocated in previous step arrays of pointer to chars
* within each of these arrays are chars
*/
for (i = 0; i < dim1; i++)
{
*(p + i) = malloc (sizeof (char) * dim2);
/* or p[i] = malloc (sizeof (char) * dim2); */
}
/* Do work */
/* Deallocate the allocated array. Start deallocation from the lowest level.
* that is in the reverse order of which we did the allocation
*/
for (i = 0; i < dim1; i++)
{
free (p[i]);
}
free (p);
Modify the above method. When you need another line to be added do *(p + i) = malloc (sizeof (char) * dim2); and update i. In this case you need to predict the max numbers of lines in the file which is indicated by the dim1 variable, for which we allocate the p array first time. This will only allocate the (sizeof (int *) * dim1) bytes, thus much better option than char p[dim1][dim2] (in c99).
There is another way i think. Allocate arrays in blocks and chain them when there is an overflow.
struct _lines {
char **line;
int n;
struct _lines *next;
} *file;
file = malloc (sizeof (struct _lines));
file->line = malloc (sizeof (char *) * LINE_MAX);
file->n = 0;
head = file;
After this the first block is ready to use. When you need to insert a line just do:
/* get line into buffer */
file.line[n] = malloc (sizeof (char) * (strlen (buffer) + 1));
n++;
When n is LINE_MAX allocate another block and link it to this one.
struct _lines *temp;
temp = malloc (sizeof (struct _lines));
temp->line = malloc (sizeof (char *) * LINE_MAX);
temp->n = 0;
file->next = temp;
file = file->next;
Something like this.
When one block's n becomes 0, deallocate it, and update the current block pointer file to the previous one. You can either traverse from beginning single linked list and traverse from the start or use double links.
There's no standard resizable array type in C. You have to implement it yourself, or use a third-party library. Here's a simple bare-bones example:
typedef struct int_array
{
int *array;
size_t length;
size_t capacity;
} int_array;
void int_array_init(int_array *array)
{
array->array = NULL;
array->length = 0;
array->capacity = 0;
}
void int_array_free(int_array *array)
{
free(array->array);
array->array = NULL;
array->length = 0;
array->capacity = 0;
}
void int_array_push_back(int_array *array, int value)
{
if(array->length == array->capacity)
{
// Not enough space, reallocate. Also, watch out for overflow.
int new_capacity = array->capacity * 2;
if(new_capacity > array->capacity && new_capacity < SIZE_T_MAX / sizeof(int))
{
int *new_array = realloc(array->array, new_capacity * sizeof(int));
if(new_array != NULL)
{
array->array = new_array;
array->capacity = new_capacity;
}
else
; // Handle out-of-memory
}
else
; // Handle overflow error
}
// Now that we have space, add the value to the array
array->array[array->length] = value;
array->length++;
}
Use it like this:
int_array a;
int_array_init(&a);
int i;
for(i = 0; i < 10; i++)
int_array_push_back(&a, i);
for(i = 0; i < a.length; i++)
printf("a[%d] = %d\n", i, a.array[i]);
int_array_free(&a);
Of course, this is only for an array of ints. Since C doesn't have templates, you'd have to either put all of this code in a macro for each different type of array (or use a different preprocessor such as GNU m4). Or, you could use a generic array container that either used void* pointers (requiring all array elements to be malloc'ed) or opaque memory blobs, which would require a cast with every element access and a memcpy for every element get/set.
In any case, it's not pretty. Two-dimensional arrays are even uglier.
Instead of an array here, you could also use a linked list, The code is simpler, but the allocation is more frequent and may suffer from fragmentation.
As long as you don't plan to do much random access (Which is O(n) here), iteration is about as simple as a regular array.
typedef struct Line Line;
struct Line{
char text[LINE_MAX];
Line *next;
};
Line *mkline()
{
Line *l = malloc(sizeof(Line));
if(!l)
error();
return l;
}
main()
{
Line *lines = mkline();
Line *lp = lines;
while(fgets(lp->text, sizeof lp->text, stdin)!=NULL){
lp->next = mkline();
lp = lp->next;
}
lp->next = NULL;
}
If you are using C you will need to implement the resizing of the array yourself. C++ and the SDL has this done for you. It is called a vector. http://www.cplusplus.com/reference/stl/vector/
While a multidimensional array can solve this problem, a rectangular 2D array would not really be the natural C solution.
Here is a program that initially reads the file into a linked list, and then allocates a vector of pointers of the right size. Each individual character does then appear as array[line][col] but in fact each row is only as long as it needs to be. It's C99 except for <err.h>.
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct strnode {
char *s;
struct strnode *next;
} strnode;
strnode *list_head;
strnode *list_last;
strnode *read1line(void) {
char space[1024];
if(fgets(space, sizeof space, stdin) == NULL)
return NULL;
strnode *node = malloc(sizeof(strnode));
if(node && (node->s = malloc(strlen(space) + 1))) {
strcpy(node->s, space);
node->next = NULL;
if (list_head == NULL)
list_head = node;
else
list_last->next = node;
list_last = node;
return node;
}
err(1, NULL);
}
int main(int ac, char **av) {
int n;
strnode *s;
for(n = 0; (s = read1line()) != NULL; ++n)
continue;
if(n > 0) {
int i;
strnode *b;
char **a = malloc(n * sizeof(char *));
printf("There were %d lines\n", n);
for(b = list_head, i = 0; b; b = b->next, ++i)
a[i] = b->s;
printf("Near the middle is: %s", a[n / 2]);
}
return 0;
}
You can use the malloc and realloc functions to dynamically allocate and resize an array of pointers to char, and each element of the array will point to a string read from the file (where that string's storage is also allocated dynamically). For simplicity's sake we'll assume that the maximum length of each line is less than M characters (counting the newline), so we don't have to do any dynamic resizing of individual lines.
You'll need to keep track of the array size manually each time you extend it. A common technique is to double the array size each time you extend, rather than extending by a fixed size; this minimizes the number of calls to realloc, which is potentially expensive. Of course that means you'll have to keep track of two quantities; the total size of the array and the number of elements currently read.
Example:
#define INITIAL_SIZE ... // some size large enough to cover most cases
char **loadFile(FILE *stream, size_t *linesRead)
{
size_t arraySize = 0;
char **lines = NULL;
char *nextLine = NULL;
*linesRead = 0;
lines = malloc(INITIAL_SIZE * sizeof *lines);
if (!lines)
{
fprintf(stderr, "Could not allocate array\n");
return NULL;
}
arraySize = INITIAL_SIZE;
/**
* Read the next input line from the stream. We're abstracting this
* out to keep the code simple.
*/
while ((nextLine = getNextLine(stream)))
{
if (arraySize <= *linesRead)
{
char **tmp = realloc(lines, arraysSize * 2 * sizeof *tmp);
if (tmp)
{
lines = tmp;
arraySize *= 2;
}
}
lines[(*linesRead)++] = nextLine;
)
return lines;
}