Passing a string array to a function in C - c

I am just learning C, Could someone explain why the following code produces a Segmentation fault after printing the first element of an array?
what would working code look like?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define ELEMENTS 8
void make(char ***array) {
*array = malloc(ELEMENTS * sizeof(char *));
(*array)[0] = "test0";
(*array)[1] = "test1";
(*array)[2] = "test2";
(*array)[3] = "test3";
(*array)[4] = "test4";
(*array)[5] = "test5";
(*array)[6] = "test6";
(*array)[7] = "test7";
(*array)[8] = "test8";
}
int main(int argc, char **argv)
{
char **array;
make(&array);
int i;
for (i = 0; i < ELEMENTS; ++i) {
printf("%s\n", array[i]);
free(array[i]);
}
free(array);
return 0;
}

Your array size is 8, but you access index 8, which is one past the end of your array. Count the number of elements if you don't understand...
You call free on the assigned string constants. Don't do this. Only free what you malloc, which is just array, not array[0] to array[8].

When you put a literal string in C++ like "test0", it is actually stored in a special memory location where it cannot be modified. In the line
(*array)[0] = "test0";
you're pointing your char* to that memory location, which is alright to do. However, later, when you call free(array[i]);, you are attempting to free the same memory, which is a no-no. In general, only use free() if you have previously used malloc() on the same variable.
Also, as others have said, you need to allocate an array of size 9, since you're using 9 elements.

Related

How do I modify a char** in another function

This is what I expect my string array s to be after the program is run: {"#0", "#1", "2"}.
This is what I am getting: {"#2", "#2", "2"}.
How do I modify this code so that I can get {"#0", "#1", "#2"} in the main after the function is executed?
What am I doing wrong? Please help.
#include <stdio.h>
void func(char **s){
for(int i=0; i<3; i++){
char buf[10];
snprintf(buf, 10, "#%d",i);
s[i]=buf;
}
}
int main()
{
char *s[3];
func(s);
for(int i=0; i<3; i++){
printf("%s", s[i]);
}
return 0;
}
First, you have every element in your array pointing to the same character array.
Second, they are all pointing to a local character array. This leads to undefined behavior.
Local variables in functions get allocated on the stack. When the function call is over, that memory may be overwritten with some other value.
Given two pointers p and q, the statement:
p = q;
doesn't copy the contents of the memory pointed to by q to the contents of the memory pointed to by p. It copies the pointer values, such that both p and q now point to the same memory, and any change to the memory via p is reflected when q is used.
That being said, the statement:
char buf[10];
declares buf to be an array of 10 chars. It has a lifetime corresponding to the execution of its block of definition. Once the function returns, it's destroyed and s is now indeterminate. Indeterminate pointers lead to undefined behaviour.
Possible Solutions:
standard strcpy
POSIX's strdup (which will be included in C23)
Note that the strdup() function returns a pointer to a new string which is
a duplicate of the provided string. Memory for the new string is obtained with malloc, and must be freed by the calling process with free.
#Chris’s answer tells you what is wrong.
To fix it, you have options. The simplest is to make the argument array have strings (char arrays) that are big enough for your uses:
#define MAX_STR_LEN (9+1) // Every string’s capacity is 9+1 characters
void func(size_t n, char array_of_string[][MAX_STR_LEN])
{
for (size_t i=0; i<n; i++)
{
snprintf(array_of_string[i], MAX_STR_LEN, "#%d", (int)i); // use the extant string
}
}
int main(void)
{
char array_of_string[3][MAX_STR_LEN] = {{0}}; // Array of 3 strings
func(3, array_of_string);
...
return 0;
}
If you want to play with dynamic char * strings, life gets only a little more complicated:
void func(size_t n, char *array_of_string[])
{
for (size_t i=0; i<n; i++)
{
free(array_of_string[i]); // free any pre-existing string
array_of_string[i] = calloc( 10, 1 ); // allocate our new string
if (!array_of_string[i]) fooey(); // always check for failure
snprintf(array_of_string[i], 10, "#%d", (int)i); // use the new string
}
}
int main(void)
{
char *array_of_string[3] = {NULL}; // Array of 3 dynamic strings, all initially NULL
func(3, array_of_string);
...
for (size_t i=0; i<3; i++) // Don’t forget to clean up after yourself
free(array_of_string[i]);
return 0;
}
Ultimately the trick is to manage the size of your strings, remembering that a string is itself just an array of char. You must ensure that there is enough room in your character array to store all the characters you wish. (Good job on using snprintf()!
Also remember that in C any argument of the form array[] is the same as *array. So our functions could have been written:
void func(size_t n, char (*array_of_string)[MAX_STR_LEN])
or
void func(size_t n, char **array_of_string)
respectively. The first is an uglier (harder to read) syntax. The second is nicer, methinks, but YRMV.
Finally, if you are using C99 (or later) you can tell the compiler that those arguments are, actually, arrays:
void func(size_t n, char array_of_string[n][MAX_STR_LEN])
or
void func(size_t n, char *array_of_string[n])
MSVC does not support that syntax, though, and probably never will, alas.
{ // start of a new scope
char buf[10]; // a variable with automatic storage duration
// ...
} // end of scope - all automatic variables end their life
In your code, you make pointers point at buf which has seized to exist (3 times) at the } in the for loop. Dereferencing (reading from the memory those pointers point at) those pointers afterwards makes your program have undefined behavior (anything could happen).
What you can do is to allocate and release memory dynamically using malloc and free.
When sending in a pointer to the first element in an array of elements to a function like you do, it's also customary to provide the length of the array (the number of elements in the array) to the function.
It could look like this:
#include <stdio.h>
#include <stdlib.h>
// A macro to calculate the number of elements in an array
// sizeof (x) - the number of bytes the whole array occupies
// sizeof *(x) - the size of the first element in the array
// (all elements have equal size)
// The result of the division is the number of elements in the array.
#define SIZE(x) (sizeof (x) / sizeof *(x))
void func(char *s[], size_t len) {
for (size_t i = 0; i < len; i++) {
// calculate the required length for this string
size_t req = snprintf(NULL, 0, "#%zu", i) + 1; // +1 for '\0'
// and allocate memory for it
s[i] = malloc(req);
if(s[i] == NULL) exit(1); // failed to allocate memory
snprintf(s[i], req, "#%zu", i);
} // dynamically allocated memory is _not_ released at the end of the scope
}
int main() {
char *s[3];
func(s, SIZE(s));
for (size_t i = 0; i < SIZE(s); i++) {
puts(s[i]);
free(s[i]); // free what you've malloc'ed when you are done with it
}
}
Note that with the use of the macro there is only one hardcoded 3 in the program. Even that could be made into a named constant (#define CHAR_PTRS (3) or enum { CHAR_PTRS = 3 };) to further the ease of reading and maintaining the code.
A non-idiomatic version only accepting a pointer to an array of a fixed (at compile time) size could look like like below. In this example you couldn't accidentally provide a pointer to an array with only 2 char* (which would cause the function to write out of bounds). Instead, it'd result in a compilation error.
#include <stdio.h>
#include <stdlib.h>
// Here `s` is a pointer to an array of 3 char*
void func(char *(*s)[3]) {
for (int i = 0; i < 3; i++) {
(*s)[i] = malloc(10);
if((*s)[i] == NULL) exit(1);
snprintf((*s)[i], 10, "#%d", i);
}
}
int main() {
char *s[3];
func(&s); // &s is a char*(*)[3]
for (int i = 0; i < 3; i++) {
printf("%s\n", s[i]);
free(s[i]);
}
}
#include <stdio.h>
#include <string.h>
void func(char **s){
for(int i=0; i<3; i++){
s[i]=malloc(sizeof(char) * 100);
char buf[10];
snprintf(buf, 10, "#%d",i);
strcpy(s[i], buf);
}
}
int main()
{
char *s[3];
func(s);
for(int i=0; i<3; i++){
printf("%s", s[i]);
}
return 0;
}
This fixed my problem. My understanding is that I assigned memory and then copied the contents of buf to s to the now-present memory.

array function in c /Append/Delete basic

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int* create_int_array(){
int* arr;
arr = (int *)calloc(1,sizeof(int));
return arr;
}
char** create_string_array(){
char** arr = calloc(1,sizeof(char));
return arr;
}
void append_int(int* array, int element, int index){
array = (array+index);
*array = element;
}
void append_string(char** array , char* element,int index){
*(array + index) = calloc(1,sizeof(char*));
strcpy(*(array + index),element);
}
void delete_string(char** array, int index){
free(array[index]);
}
void delete_int(int* array,int index){
array[index] = NULL;
}
/////// M A I N F I L E ///////
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "basic_data_file.h"
int main(int argc, char const *argv[])
{
/* code */
char **array;
array = create_string_array();
char *full_name = calloc(strlen("hamza arslan"),sizeof(char*));
strcpy(full_name,"hamza arslan");
char* mail = calloc(strlen("test#gmail.com"),sizeof(char*));
strcpy(mail,"test#gmail.com");
char* address = calloc(strlen("Hacettepe Universty"),sizeof(char*));
strcpy(address,"Hacettepe Universty");
char* statu = calloc(strlen("student"),sizeof(char*));
strcpy(statu,"student");
append_string(array,full_name,0);
append_string(array,mail,1);
append_string(array,address,2);
append_string(array,statu,4);
for(int i=0; i< 3; i++){
printf("%s\n",array[i]);
free(array[i]); // get free double pointer
}
printf("%s\n",array[4]); // because index 3 blow up
free(full_name);
free(mail);
free(address);
free(statu);
return 0;
}
I was try to own my basic array library . As you know else in some languages have high level array types. They are making easy our stocking operation. But in c, it's more complicated especially about string. I have 2 problem in here . First of all , when i give index=3 in append_string function , code blow up with Aborted(core dumped) error.(./run': double free or corruption (out)). Secondly , when i was checking leak memory ,it's get memory leak even i use free. What can i do?
Your create_xy_array functions allocate an array of 1 element, and they stay that way until the very end. When you have a one-element array and index, read/write its second and further elements, C itself happily approves, but the result will not work, it silently destroys everything in its path.
First of all, for having a dynamic array, you have to track its length yourself, C allocations/arrays do not know their own size. So you need a struct, containing the length and a pointer, something like
typedef struct IntArray {
int length;
int *elements;
} IntArray;
Then allocate it, for 0 elements, as there is nothing inside at the beginning:
IntArray* create_int_array() {
IntArray* ret = (IntArray*) malloc(sizeof(IntArray));
ret->length = 0;
ret->elements = NULL;
return ret;
}
void free_int_array(IntArray* arr) {
free(arr->elements);
free(arr);
}
Then you can try putting something inside:
void append_int(IntArray* arr, int element) {
arr->length++;
arr->elements = (int*) realloc(arr->elements, arr->length*sizeof(int));
arr->elements[length-1] = element;
}
(appending means adding something to the end of an array, there is no need for indices here)
And this could go on forever, deletion of an arbitrary element should shift the "upper" part of the array (memcpy) and resize the result to one element smaller or you could track the capacity of the array, which can be larger than its current length (but then it has to be incorporated into the append function and probably others).
(Disclaimer: I hope the snippet is correct, but I do not use C too often - and I can not suggest a good tutorial for the same reason, but that is what you probably need)
Note: I haven't code in C for years and I haven't check the code so double check and let me know.
Based on your description, you are trying to do a Vector.
Therefore, there are different ways that you can handle this.
Approach 1:
Create a structure which will hold the array, the capacity of the array, and the size of the array.
typedef struct StrArray{
int capacity;
int size;
int *integers;
}
The trick here is to take attention when you increase/decrease the capacity.
If you increase the capacity above the size of the array, then:
You need to create a new array with double the capacity
Copy all elements to the new array
Change pointer to new array
Free the memory of the old array
Approach 2
You can extend the previous approach by creating a function which returns a struct which holds the storage plus function pointers to the methods you wish to implement.
typedef struct StrStorage{
int capacity;
int size;
int *integers;
} StrStorage;
typedef struct StrArray {
StrStorage storage;
int (*capacity)(StrArray*);
int (*size)(StrArray*);
StrArray *(*append)(StrArray*, int);
void (*increaseStorage)(StrArray*);
// Add other methods here
} StrArray;
int capacity(StrArray *self) {
return self->storage->capacity;
}
int size(StrArray *self) {
return self->storage->size;
}
StrArray *append(StrArray *self, int integer){
if ((self->capacity() + 1) > self->size()){
self->increaseStorage();
}
// The rest of the magic goes here
return self;
}
StrArray *initializeStrArray(int n) {
StrArray* strArray = malloc(sizeof(StrArray));
strArray->chars = malloc(sizeof(char) * n);
strArray->capacity= capacity;
strArray->append = append;
return strArray;
}
Approach 3:
This approach is a continuation of the previous one.
In order to reduce the memory allocation, you can create an equivalent to a singleton which holds all the manipulation functions and then assi
There will be several crashes, but here is one:
char** arr = calloc(1,sizeof(char));
You are allocating 1 byte which is not sufficient to store a (char *), which needs between 2 and 8 bytes depending on the OS and target machine.
Try this instead:
char** arr = calloc(1,sizeof(char*));
You should double-check each line of code. C is not a forgiving language - mistakes are severely punished.

C Dynamically creating array of structs which include variable sized 2d array [duplicate]

I know how to create an array of structs but with a predefined size. However is there a way to create a dynamic array of structs such that the array could get bigger?
For example:
typedef struct
{
char *str;
} words;
main()
{
words x[100]; // I do not want to use this, I want to dynamic increase the size of the array as data comes in.
}
Is this possible?
I've researched this: words* array = (words*)malloc(sizeof(words) * 100);
I want to get rid of the 100 and store the data as it comes in. Thus if 76 fields of data comes in, I want to store 76 and not 100. I'm assuming that I don't know how much data is coming into my program. In the struct I defined above I could create the first "index" as:
words* array = (words*)malloc(sizeof(words));
However I want to dynamically add elements to the array after. I hope I described the problem area clearly enough. The major challenge is to dynamically add a second field, at least that is the challenge for the moment.
I've made a little progress however:
typedef struct {
char *str;
} words;
// Allocate first string.
words x = (words) malloc(sizeof(words));
x[0].str = "john";
// Allocate second string.
x=(words*) realloc(x, sizeof(words));
x[1].FirstName = "bob";
// printf second string.
printf("%s", x[1].str); --> This is working, it's printing out bob.
free(x); // Free up memory.
printf("%s", x[1].str); --> Not working since its still printing out BOB even though I freed up memory. What is wrong?
I did some error checking and this is what I found. If after I free up memory for x I add the following:
x=NULL;
then if I try to print x I get an error which is what I want. So is it that the free function is not working, at least on my compiler? I'm using DevC??
Thanks, I understand now due to:
FirstName is a pointer to an array of char which is not being allocated by the malloc, only the pointer is being allocated and after you call free, it doesn't erase the memory, it just marks it as available on the heap to be over written later. – MattSmith
Update
I'm trying to modularize and put the creation of my array of structs in a function but nothing seems to work. I'm trying something very simple and I don't know what else to do. It's along the same lines as before, just another function, loaddata that is loading the data and outside the method I need to do some printing. How can I make it work? My code is as follows:
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <ctype.h>
typedef struct
{
char *str1;
char *str2;
} words;
void LoadData(words *, int *);
main()
{
words *x;
int num;
LoadData(&x, &num);
printf("%s %s", x[0].str1, x[0].str2);
printf("%s %s", x[1].str1, x[1].str2);
getch();
}//
void LoadData(words *x, int * num)
{
x = (words*) malloc(sizeof(words));
x[0].str1 = "johnnie\0";
x[0].str2 = "krapson\0";
x = (words*) realloc(x, sizeof(words)*2);
x[1].str1 = "bob\0";
x[1].str2 = "marley\0";
*num=*num+1;
}//
This simple test code is crashing and I have no idea why. Where is the bug?
You've tagged this as C++ as well as C.
If you're using C++ things are a lot easier. The standard template library has a template called vector which allows you to dynamically build up a list of objects.
#include <stdio.h>
#include <vector>
typedef std::vector<char*> words;
int main(int argc, char** argv) {
words myWords;
myWords.push_back("Hello");
myWords.push_back("World");
words::iterator iter;
for (iter = myWords.begin(); iter != myWords.end(); ++iter) {
printf("%s ", *iter);
}
return 0;
}
If you're using C things are a lot harder, yes malloc, realloc and free are the tools to help you. You might want to consider using a linked list data structure instead. These are generally easier to grow but don't facilitate random access as easily.
#include <stdio.h>
#include <stdlib.h>
typedef struct s_words {
char* str;
struct s_words* next;
} words;
words* create_words(char* word) {
words* newWords = malloc(sizeof(words));
if (NULL != newWords){
newWords->str = word;
newWords->next = NULL;
}
return newWords;
}
void delete_words(words* oldWords) {
if (NULL != oldWords->next) {
delete_words(oldWords->next);
}
free(oldWords);
}
words* add_word(words* wordList, char* word) {
words* newWords = create_words(word);
if (NULL != newWords) {
newWords->next = wordList;
}
return newWords;
}
int main(int argc, char** argv) {
words* myWords = create_words("Hello");
myWords = add_word(myWords, "World");
words* iter;
for (iter = myWords; NULL != iter; iter = iter->next) {
printf("%s ", iter->str);
}
delete_words(myWords);
return 0;
}
Yikes, sorry for the worlds longest answer. So WRT to the "don't want to use a linked list comment":
#include <stdio.h>
#include <stdlib.h>
typedef struct {
char** words;
size_t nWords;
size_t size;
size_t block_size;
} word_list;
word_list* create_word_list(size_t block_size) {
word_list* pWordList = malloc(sizeof(word_list));
if (NULL != pWordList) {
pWordList->nWords = 0;
pWordList->size = block_size;
pWordList->block_size = block_size;
pWordList->words = malloc(sizeof(char*)*block_size);
if (NULL == pWordList->words) {
free(pWordList);
return NULL;
}
}
return pWordList;
}
void delete_word_list(word_list* pWordList) {
free(pWordList->words);
free(pWordList);
}
int add_word_to_word_list(word_list* pWordList, char* word) {
size_t nWords = pWordList->nWords;
if (nWords >= pWordList->size) {
size_t newSize = pWordList->size + pWordList->block_size;
void* newWords = realloc(pWordList->words, sizeof(char*)*newSize);
if (NULL == newWords) {
return 0;
} else {
pWordList->size = newSize;
pWordList->words = (char**)newWords;
}
}
pWordList->words[nWords] = word;
++pWordList->nWords;
return 1;
}
char** word_list_start(word_list* pWordList) {
return pWordList->words;
}
char** word_list_end(word_list* pWordList) {
return &pWordList->words[pWordList->nWords];
}
int main(int argc, char** argv) {
word_list* myWords = create_word_list(2);
add_word_to_word_list(myWords, "Hello");
add_word_to_word_list(myWords, "World");
add_word_to_word_list(myWords, "Goodbye");
char** iter;
for (iter = word_list_start(myWords); iter != word_list_end(myWords); ++iter) {
printf("%s ", *iter);
}
delete_word_list(myWords);
return 0;
}
If you want to dynamically allocate arrays, you can use malloc from stdlib.h.
If you want to allocate an array of 100 elements using your words struct, try the following:
words* array = (words*)malloc(sizeof(words) * 100);
The size of the memory that you want to allocate is passed into malloc and then it will return a pointer of type void (void*). In most cases you'll probably want to cast it to the pointer type you desire, which in this case is words*.
The sizeof keyword is used here to find out the size of the words struct, then that size is multiplied by the number of elements you want to allocate.
Once you are done, be sure to use free() to free up the heap memory you used in order to prevent memory leaks:
free(array);
If you want to change the size of the allocated array, you can try to use realloc as others have mentioned, but keep in mind that if you do many reallocs you may end up fragmenting the memory. If you want to dynamically resize the array in order to keep a low memory footprint for your program, it may be better to not do too many reallocs.
This looks like an academic exercise which unfortunately makes it harder since you can't use C++. Basically you have to manage some of the overhead for the allocation and keep track how much memory has been allocated if you need to resize it later. This is where the C++ standard library shines.
For your example, the following code allocates the memory and later resizes it:
// initial size
int count = 100;
words *testWords = (words*) malloc(count * sizeof(words));
// resize the array
count = 76;
testWords = (words*) realloc(testWords, count* sizeof(words));
Keep in mind, in your example you are just allocating a pointer to a char and you still need to allocate the string itself and more importantly to free it at the end. So this code allocates 100 pointers to char and then resizes it to 76, but does not allocate the strings themselves.
I have a suspicion that you actually want to allocate the number of characters in a string which is very similar to the above, but change word to char.
EDIT: Also keep in mind it makes a lot of sense to create functions to perform common tasks and enforce consistency so you don't copy code everywhere. For example, you might have a) allocate the struct, b) assign values to the struct, and c) free the struct. So you might have:
// Allocate a words struct
words* CreateWords(int size);
// Assign a value
void AssignWord(word* dest, char* str);
// Clear a words structs (and possibly internal storage)
void FreeWords(words* w);
EDIT: As far as resizing the structs, it is identical to resizing the char array. However the difference is if you make the struct array bigger, you should probably initialize the new array items to NULL. Likewise, if you make the struct array smaller, you need to cleanup before removing the items -- that is free items that have been allocated (and only the allocated items) before you resize the struct array. This is the primary reason I suggested creating helper functions to help manage this.
// Resize words (must know original and new size if shrinking
// if you need to free internal storage first)
void ResizeWords(words* w, size_t oldsize, size_t newsize);
In C++, use a vector. It's like an array but you can easily add and remove elements and it will take care of allocating and deallocating memory for you.
I know the title of the question says C, but you tagged your question with C and C++...
Another option for you is a linked list. You'll need to analyze how your program will use the data structure, if you don't need random access it could be faster than reallocating.
Your code in the last update should not compile, much less run. You're passing &x to LoadData. &x has the type of **words, but LoadData expects words* . Of course it crashes when you call realloc on a pointer that's pointing into stack.
The way to fix it is to change LoadData to accept words** . Thi sway, you can actually modify the pointer in main(). For example, realloc call would look like
*x = (words*) realloc(*x, sizeof(words)*2);
It's the same principlae as in "num" being int* rather than int.
Besides this, you need to really figure out how the strings in words ere stored. Assigning a const string to char * (as in str2 = "marley\0") is permitted, but it's rarely the right solution, even in C.
Another point: non need to have "marley\0" unless you really need two 0s at the end of string. Compiler adds 0 tho the end of every string literal.
For the test code: if you want to modify a pointer in a function, you should pass a "pointer to pointer" to the function. Corrected code is as follows:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
typedef struct
{
char *str1;
char *str2;
} words;
void LoadData(words**, int*);
main()
{
words **x;
int num;
LoadData(x, &num);
printf("%s %s\n", (*x[0]).str1, (*x[0]).str2);
printf("%s %s\n", (*x[1]).str1, (*x[1]).str2);
}
void LoadData(words **x, int *num)
{
*x = (words*) malloc(sizeof(words));
(*x[0]).str1 = "johnnie\0";
(*x[0]).str2 = "krapson\0";
*x = (words*) realloc(*x, sizeof(words) * 2);
(*x[1]).str1 = "bob\0";
(*x[1]).str2 = "marley\0";
*num = *num + 1;
}
Every coder need to simplify their code to make it easily understood....even for beginners.
So array of structures using dynamically is easy, if you understand the concepts.
// Dynamically sized array of structures
#include <stdio.h>
#include <stdlib.h>
struct book
{
char name[20];
int p;
}; //Declaring book structure
int main ()
{
int n, i;
struct book *b; // Initializing pointer to a structure
scanf ("%d\n", &n);
b = (struct book *) calloc (n, sizeof (struct book)); //Creating memory for array of structures dynamically
for (i = 0; i < n; i++)
{
scanf ("%s %d\n", (b + i)->name, &(b + i)->p); //Getting values for array of structures (no error check)
}
for (i = 0; i < n; i++)
{
printf ("%s %d\t", (b + i)->name, (b + i)->p); //Printing values in array of structures
}
scanf ("%d\n", &n); //Get array size to re-allocate
b = (struct book *) realloc (b, n * sizeof (struct book)); //change the size of an array using realloc function
printf ("\n");
for (i = 0; i < n; i++)
{
printf ("%s %d\t", (b + i)->name, (b + i)->p); //Printing values in array of structures
}
return 0;
}
If you want to grow the array dynamically, you should use malloc() to dynamically allocate some fixed amount of memory, and then use realloc() whenever you run out. A common technique is to use an exponential growth function such that you allocate some small fixed amount and then make the array grow by duplicating the allocated amount.
Some example code would be:
size = 64; i = 0;
x = malloc(sizeof(words)*size); /* enough space for 64 words */
while (read_words()) {
if (++i > size) {
size *= 2;
x = realloc(sizeof(words) * size);
}
}
/* done with x */
free(x);
Here is how I would do it in C++
size_t size = 500;
char* dynamicAllocatedString = new char[ size ];
Use same principal for any struct or c++ class.

how to pass pointer to array of pointers in C

I have the following C code which works:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
int pw = sizeof(char*); // width of pointer (to char)
int num;
int first = 1;
int size = 0;
int incr = 10;
char *(*arr)[]; // pointer to array of pointers to char */
test(char* s, int i)
{
int j;
char *(*newarr)[]; // pointer to array of pointers to char
if (first) { // first time
arr = malloc(pw*incr); // malloc array
first = 0; // skip from now on
size = incr; // save the size
}
if (i >= size) { // out of space
newarr = malloc(pw*(size+incr)); // get incr bigger space
for (j=0; j<size; j++) // copy the elements from the old
(*newarr)[j] = (*arr)[j]; // array to new array
free(arr); // free the old array space
arr = newarr; // point old array to new array
size = size+incr;
};
int len = strlen(s); // length of s
(*arr)[i] = malloc(len+1); // assign pointer to pointer array element
strcpy((*arr)[i], s); // copy s to array
// both arguments must be pointers
printf("%d\t%s\n", i, (*arr)[i]);
};
main()
{
char* s = "this is a string";
for (num=0; num<30; num++) // add 30 pointers to s to *arr
test(s, num);
for (num=0; num<30; num++)
printf("%d\t%s\n", num, (*arr)[num]); // print out what they point to
};
It prints out 'i\tthis is a string' for 'i' from 0 to 29 twice. What I want to do is pass 'arr' from the top of the file as an argument of 'test'. The reason I want to do that is because I want to pass several different arrays all of which are declared the same way. If I make the minimal changes to do that I get:
0 this is a string
Segmentation fault (core dumped)
Here is the output of the diff command which shows the minimal changes:
13c13
< char *(*arr)[]; // pointer to array of pointers to char */
---
> char *(*jarr)[]; // pointer to array of pointers to char */
15c15
< test(char* s, int i)
---
> test(char* s, int i, char *(*arr)[])
52c52
< test(s, num);
---
> test(s, num, jarr);
54,55d53
< for (num=0; num<30; num++)
< printf("%d\t%s\n", num, (*arr)[num]); // print out what they point to
In other words everything is the same except for renaming 'arr' as 'jarr' and passing it to 'test'.
Thanks in advance,
Mike
The trouble occurs when you call:
test(s, num, jarr);
You are passing jarr by value. Inside the function, you are reallocating (the hard way — why not use realloc() which does the copying for you?) the array, but that change does not affect the value of jarr 'in main()' because it was passed by value. The second time through the loop, you are still passing a null pointer to the function, but you are then dereferencing that null pointer, which is bad news.
How to fix?
Fair question...I'm not sure if the old "well, if I want to get to there, I wouldn't start from here" gag passes muster.
The 'simplest' change is to revise the call:
jarr = test(s, num, jarr);
and then 'just' revise the function so that it returns a pointer to an array of character pointers. That is a very esoteric function. My brain's not awake (insufficient caffeine), so I used an intermediate typedef to get around the problem of how to write the function declaration and definition:
typedef char *(ArrayString[]);
ArrayString *test3(char *s, int i, char *(*arr)[]);
ArrayString *test3(char *s, int i, char *(*arr)[]) { (*arr)[i] = s; return arr; }
It compiles without warnings; that isn't a guarantee that it's correct.
The primary alternative is to pass a pointer to a pointer to an array of char pointers to the function, which is even more esoteric.
However, both of these are 'starting from here' solutions. You'd do better, on the whole, to devise a different way of handling things. Pointers to arrays are certainly a part of C, but they are at the outer edges of C and you should generally assume that if your design calls for their use, then your design is probably not the best. You should use a simpler char ** (or, perish the thought, char ***; triple indirection is best avoided too, but that isn't always possible).
You seem to have misunderstood how arrays and pointers works. Lets say you want a dynamic array of strings, that is basically a pointer to a pointer of char:
char **arr = NULL;
To allocate memory for that you do e.g.
arr = malloc(sizeof(char *) * current_size);
Now you have an "array" of character pointers. Lets say you want each of these to be a specific string str:
for (int i = 0; i < current_size; i++)
{
arr[i] = strdup(str);
}
Oh, now you need to increase the number of strings, all initialized to the same string as before:
size_t new_size = current_size + 10;
arr = realloc(arr, sizeof(char *) * new_size);
for (int i = current_size; i < new_size)
{
arr[i] = strdup(str);
}
The problem now is that you want to do all of the above in a separate function. It's first now that you have to add another indirection.
I think you can do a double check on the first malloc value assigned to jarr both in the test(s, 0, jarr) and out of the test(s, 0, jarr); the jarr assignement is not successful since you change the pointer value in the passing by value.

Passing multi-dimensional arrays in C

I am currently trying to learn C and I have come to a problem that I've been unable to solve.
Consider:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define ELEMENTS 5
void make(char **array, int *array_size) {
int i;
char *t = "Hello, World!";
array = malloc(ELEMENTS * sizeof(char *));
for (i = 0; i < ELEMENTS; ++i) {
array[i] = malloc(strlen(t) + 1 * sizeof(char));
array[i] = strdup(t);
}
}
int main(int argc, char **argv) {
char **array;
int size;
int i;
make(array, &size);
for (i = 0; i < size; ++i) {
printf("%s\n", array[i]);
}
return 0;
}
I have no idea why the above fails to read back the contents of the array after creating it. I have literally spent an hour trying to understand why it fails but have come up empty handed. No doubt it's something trivial.
Cheers,
You need to pass the address of "array" into the function. That is, you need char ***. This is because you need to change the value of array, by allocating memory to it.
EDIT: Just to make it more complete, in the function declaration you need to have something like
void make(char ***array, int *array_size)
Then you need to call it using
make(&array, &size);
Inside the function make, allocate memory with
*array = malloc(ELEMENTS * sizeof(char *));
And change other places accordingly.
Also, as kauppi has pointed out, strdup will allocate memory for you, so you don't need to do malloc on each string.
Here is the working code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define ELEMENTS 5
void make(char ***array) {
char *t = "Hello, World!";
*array = malloc(ELEMENTS * sizeof(char *));
int i;
for (i = 0; i < ELEMENTS; ++i) {
(*array)[i] = strdup(t);
}
}
int main(int argc, char **argv) {
char **array;
make(&array);
int i;
for (i = 0; i < ELEMENTS; ++i) {
printf("%s\n", array[i]);
free(array[i]);
}
free(array);
return 0;
}
As the other have posted - there was unused size, and strdup allocates memory by itself, and it is nice to free the memory afterwards...
See PolyThinker's comment which is absolutely spot on.
In addition to the way you pass the array, you should check a few other issues:
Perhaps you should assign something to array_size in make(...)?
strdup(char*) allocates memory, the malloc for array[i] is not necessary.
You should free all the memory you allocate after you don't need it anymore.
You are passing the current value of array to make as a copy (on the stack). when you change array in make(), you're only changing the copy, not the actual variable. Try passing by reference with &, or make it a char *** and work with *array = ...
size is declared but gets no value assigned (that should happen in function make, I suppose).

Resources