This question already has answers here:
How do I dynamically allocate an array of strings in C?
(5 answers)
Closed 1 year ago.
I want to allocate an array for 6 words of a maximum of 30 charachters for example.
My teacher says I should allocate 6 "+1" to count for a NULL that I have to set my self
after inserting my words.
How exactly is a char **array is allocated and why is my teacher insisting ?
It sounds like your teacher wants you do use a null pointer at the end of the array, as a sentinel value. This is similar to null termination of individual strings and can sometimes be useful when the amount of strings is unknown.
When you allocate a single string you do char* str = malloc(length + 1); where the +1 is for the null terminator '\0. The extra +1 ensures that given length = strlen("hello");, we can safely do strcpy(str, "hello") to fill this string.
You can do something similar for an array of strings by
char** arr = malloc(sizeof(char*[size+1]));
If you then assign arr[size] = NULL; as sentinel value, it will be possible to iterate over this array in this way:
for(const char** ptr=arr; ptr!=NULL; ptr++)
{
printf("%s\n", *ptr); // do something with each string
}
But if you know the size, then the preferred method is a normal loop:
for(size_t i=0; i<size; i++)
{
printf("%s\n", arr[i]); // do something with each string
}
char **arr = calloc(7, sizeof(char*));
So let's break this down, calloc basically makes an array, first argument is the size of the array and the second is how much memory to allocate to every index of the array.
Related
I have a problem trying to create a variable size array that contains strings. I have tried to create a multidimensional array, but it's too difficult. Example:
char *audio_types[8][40]; // 8 is number of string elements in the array by default; 40 is the maximum length of a string
audio_types = (char *) malloc(15); // increasing number of strings in the array
free(audio_types);
Moreover, I was trying to create variable size pointer array. Example:
char *audio_types[40]; // 40 is the maximum length of a string
*audio_types = (char *) malloc(8); // setting number of strings to 8
free(audio_types);
The problem is that I don't know how to create an array with variable number of string elements properly. Sorry, I am new in C programming. In the short, my code has to hold multiple string elements in one array. Example:
audio_types[0] // some string...
audio_types[1] // another string...
audio_types[2] // more another sting... etc.
Hope you understand what I am trying to ask. Thank you for your attention.
Maybe something like this. It will grow the size of the array when you add new types. It will also keep track of how many types you have in the array (it is needed if you resize the array dynamically and you need to know how many elements are in this array).
#define AUDIO_TYPE_LENGTH 40
typedef char audio_type[AUDIO_TYPE_LENGTH];
typedef struct
{
size_t size;
audio_type audio_types[];
}audioTypes_t;
audioTypes_t *AddType(audioTypes_t *types, const char *type)
{
size_t size = types ? types -> size : 0;
types = realloc(types, sizeof(*types) + (size + 1) * sizeof(types -> audio_types[0]));
if(types)
{
strncpy(types -> audio_types[size], type, sizeof(types -> audio_types[0] - 1));
types -> audio_types[size][sizeof(types -> audio_types[0] - 1)] = 0; //making strncpy safe
types -> size = size + 1;
}
return types;
}
You cannot change the size of an array in C. You could create a new array each time you want to resize and transfer the contents from the old array to the new one. Alternatively, you could use a list to store your strings.
When you define the array ( the char *audio_types[40]; line) in C it creates a contiguous block of memory to the 40 char pointers in the array. As memory is allocated when the array is defined you cannot make the array longer. To do that you would need a List data type, or you could create a new array when you want to add a new element which is the old one with the new element at the end.
char string[40] is an array of 40 characters that can hold 39 characters maximum (plus the string terminator) - the maximum size cannot be changed and 40 bytes of memory are used even if you store a short string containing 2 characters (plus the string terminator).
char *string is a pointer to some characters - the pointer can point anywhere: to a single character or any size array of characters. If it points to an array of characters (which you can make using malloc() then it can be thought of as a "string")
char *array[8] is an array of 8 "strings" (really it is 8 pointers to characters) The size of the array cannot be changed but the pointers can point anywhere: to a single character or any size array of characters. If the pointers point to an array of characters (which you can make using malloc() then they can be thought of as "strings")
char array[8][40] is a 2D array which you can think of as an array of 8 "strings" - where "strings" is defined as an array of 40 characters that can hold 39 characters maximum (plus the string terminator). You cannot change the size of either dimension.
char **array is a pointer to a char * - which could just be a single "string" or to an array of "strings". The sizes aren't fixed and can be dynamically created.
char *string1 = "asd"; means that string1 points to an array of 4 characters ("asd" plus the string terminator) that are in read-only memory - you cannot change the contents of the string but you can point string1 anywhere you want.
char string1[] = "asd"; means that string1 is an array of 4 characters that are filled up by the compiler with 4 characters ("asd" plus the string terminator) - you can change the contents of the array but you can't change the size.
So if you want a dynamically sized array of character strings you can do this:
#include <stdio.h> // printf
#include <stdlib.h> // malloc, realloc, free
#include <string.h> // strcpy, strdup
int main()
{
// make an array of 8 string pointers
int original_size = 8;
char **array = malloc(original_size * sizeof(char *));
// point each pointer to a string
// you don't have to do this all at once if you don't want to
for (int i=0; i<original_size; i++)
array[i] = malloc(40 * sizeof(char)); // max of 39 plus the terminator
// array[7] is already allocated so we can just use it as a string
strcpy(array[7],"asd"); // the last string that we malloced
// reallocate the array to be bigger
int new_size = 15;
array = realloc(array, new_size * sizeof(char *));
// array[14] is a pointer that doesn't point anywhere
// so we need to allocate space before filling it in
array[14] = strdup("qwe"); // points 3 characters plus the string terminator
printf("%s\n",array[7]);
printf("%s\n",array[14]);
// free the one we made with strdup
free(array[14]);
// free the 8 we malloced
for (int i=0; i<original_size; i++)
free(array[i]);
// now free the array we malloced and then realloced
free(array);
return 0;
}
Note: there is NO error checking in that program because I didn't want to mask the basic ideas - make sure you add error checking to any program you make
Try it at https://onlinegdb.com/XQeyOGd0C
I got an assignment from my teacher to write a code that compares a given word to a bunch of words located in an array of strings.
If the word in the array is lexicography smaller than the word given, I need to put it inside a new array.
else, I'm moving to the next word.
for example;
given word: hello
arr=bus, alpha, world, java.
new array=bus,alpha.
I wrote a code that does that using STRCMP, but the computer throws me out when it gets to the strcpy part.
this is my code
char** LowerSTR(char* arr[], int size_arr, char* str, int* size_res)
size_res = 0;
char** newArr= (char**)calloc(size_arr, sizeof(char));
for (int i = 0; i < size_arr; i++)
{
if (strcmp(str, arr[i])==1)
{
for (int k = 0; k <size_arr;k++)
{
strcpy(newArr[k], arr[i]);
}
size_res++;
}
}
if (size_res == 0)
return NULL;
else return newArr;}
maybe I should use STRCAT instead?
please help :\
In calling strcpy with newArr[k] as an argument you're dereferencing a NULL pointer.
Recall that we allocate newArr as follows:
char** newArr= (char**)calloc(size_arr, sizeof(char));
There's actually multiple errors here. The first is that we calloc with sizeof(char) when we in fact want a region of char*s. So corrected1
we have
char** newArr= calloc(size_arr, sizeof(char*));
As we've calloc'd this piece of memory, all of it is zeroed. Thus when strcpy internally accesses newArr[k] (itself of type char*) it points to memory address 0, which is likely reversed by the OS, and in any case, not a valid address in the context of our program.
In order to resolve this, we need to allocate for each string. For instance, one might do
newArr[k] = malloc(strlen(arr[i]) + 1); // The +1 is for the \0 termination character
the line before we strcpy.
You also have a bug with size_res as you just treat it as an int instead of an int* as you need to dereference it when you want to change or read the value to which it points.
1 See here for why I've removed the cast.
You should scan newArr and print all strings inside, something like:
for (int i = 0; i < *size_res; i++) // !
{
printf("%s\n",newArr[i]);
}
(!) 'size_res' is passed to the function as a pointer to int,
This question already has answers here:
Correctly allocating multi-dimensional arrays
(2 answers)
Closed 5 years ago.
If I have an array, like:
char array[10][20];
then it is an array of 10 strings, each of length 20. If I make a similar array with malloc:
char **dyn_array = (char **) malloc(10 * sizeof(char *));
then how do I access the members? For example, if I had a loop like:
int limit, count=0;
printf("Enter the limit (max 10): ");
scanf("%d", &limit);
fflush(stdin);
while(count < limit)
{
int index = 0;
printf("\nEnter characters: ");
fgets(array[count], sizeof(array[count]), stdin);
fflush(stdin);
printf("\nYou typed: ");
while(array[count][index] != '\n' &&
array[count][index] != '\0')
{
printf("%c", array[count][index]);
index += 1;
}
count += 1;
}
could I replace array with dyn_array in every spot and have it be the same? What changes would I have to make? I know that the way these two kinds of arrays work is similar, but I just can't wrap my head around it.
char array[10][20];
then it is an array of 10 strings, each of length 20.
No it is not. It is an array of 10 arrays of 20 chars each. Remind that a c-string is just a sequence of consecutive chars terminated by a NUL one. An array of char may contains a c-string but it is not required.
If I make a similar array with malloc:
char **dyn_array = (char **) malloc(10 * sizeof(char *));
No, it is not similar, as you just created an array of 10 pointers to chars (for which your stored its address to a pointer). But these pointers (those in the array) are not initialized to anything. To obtain something similar you could have wrote:
for (int i=0; i<10; i++)
dyn_array[i] = malloc(20*sizeof(char));
Thus, in both cases you can write array[count][index] and dyn_array[count][index].
But alas you can't write fget(dyn_array[count],sizeof(dyn_array[count],stdin); because in that case dyn_array[count] is not an array but a pointer. Remind that an array can decay to pointer, but pointer is not an array.
There is several questions about pointers vs arrays. Read.
I know this question (or similar) was asked many times, but I was still struggling to find a good answer, so please don't mark it as duplicate. I am trying to allocate memory for two arrays of strings. The strings are 500 and 1000 chars long, and the number of strings is known at the runtime. Here is my code:
char *arrOfPrompts = (char*)calloc(500*maxR, sizeof(char));
char *arrOfPhonePrompts = (char*)calloc(1000*maxR, sizeof(char));
char **prompts = (char**)calloc(maxR, sizeof(char*));
char **phonePrompts = (char**)calloc(maxR,sizeof(char*));
for (int i = 0; i<maxR; i++)
{
prompts[i] = arrOfPrompts+(i*500);
phonePrompts[i] = arrOfPhonePrompts+(i*1000);
(prompts[i])[i*500] = '\0';
(phonePrompts[i])[i*500] = '\0';
}
..where maxR is number of the arrays. So what I am doing is creating a long char array and then storing strings at 500 offsets. Is this a legit way of doing it? Looks ugly. Also, the reason I put '\0' character at the beginning of every "string" is because I then want to append to it using strcat. Are there any potential problems with this?
Thanks.
The following lines are not right. They will end up modifying memory beyond what you have allocated.
(prompts[i])[i*500] = '\0';
(phonePrompts[i])[i*500] = '\0';
Let's say maxR is 10.
arrOfPrompts points to an array of 5000 characters.
In the for loop, you use:
prompts[i] = arrOfPrompts+(i*500);
That means prompts[9] points to memory starting at the 4501-th character.
prompt[i][i*500] is prompt[9][4500] for i = 9. That will end up accessing a character that is 4000 elements away from the memory you allocated.
Since you are using calloc to allocate memory, you don't need to do anything more to create empty strings.
If you want to do it anyway, you can use:
prompts[i][0] = '\0';
phonePrompts[i][0] = '\0';
Your code seems fine but it may give unexpected results sometimes.
Another method to allocate 2D array of chars is:-
//r=number of rows
//c=number of columns
char **arr = (char **)malloc(r * sizeof(char *));
for (i=0; i<r; i++)
arr[i] = (char *)malloc(c * sizeof(char));
I was wanting to create a 2 dimensional string array that had dimensions "string[5][*]" but was having some trouble. I would like to do something like this:
...
for(i = 0;i < 5;i++){
char* word = ...;
if(strcmp(word,...)){
string[i][j] = (char *) malloc(/*size of word*/);
string[i][j] = word;
j++;
}
}
The "string" variable should be of length 5 in the ith direction and should be allowed to grow as much as I need in the jth direction.
I tried doing:
char* string[5];
but that does not seem to work. Please note the above for loop has been simplified and parts might not seem logical.
EDIT:
I am trying to sort a list of strings into 5 categories. So the n-dimentional array should in a sense hold 5 arrays containing an unspecified number of strings (not chars). This I assume is a 3-dimentional char array but was having trouble thinking of how to write it. I want every string[i][j] term to hold a char array. So string[0][0] could equal "cat" or something.
Try this.
char **string;
string = (char **)(malloc(sizeof(char *) * 5));
//malloc the string[i] whenever you need to at what ever size is necessary.
Also in your code at the top the problem is each string[i][j] is a char and you can't malloc for a non pointer. Every string[i] is a char * you have to malloc for that.
This line
string[i][j] = ...
would doubly dereference a char**, which means you're assigning something to a char. If that something happens to be a pointer, then the compiler won't allow it.
Instead, I think you want:
string[i] = (char*) malloc(...);
And then your 2D array definition remains as you had tried it:
char* string[5];
You may already know this, but I'll add a few other notes just in case. C strings generally can't be assigned using =. Instead, you need:
strcpy(string[i], word);
Secondly, the malloc() shouldn't be strlen(word) but rather:
strlen(word) + 1
The extra byte is for the nul terminator.