Difference between accessing members in char[][] and char** [duplicate] - c

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.

Related

Double pointer array allocation C [duplicate]

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.

Why I can not use the inputted value from one-dimensional array, in C? [duplicate]

This question already has answers here:
Crash or "segmentation fault" when data is copied/scanned/read to an uninitialized pointer
(5 answers)
Closed 6 years ago.
I understand that: I can use a two-dimensional array for storaging many strings. But, I would like to solve this problem with one-dimensional; so, please do not advise me to use two-dimensional array for solving.
I have a small one-dimensional array, which is used for storaging many strings. Here is all of source code:
#include <stdio.h>
void main()
{
#define MAX 10
int N = 3;
char * str[MAX];
printf("Input a string in each line:\n\n");
for (int i = 0; i < N; i++)
{
printf("str[%d] = ", i);
gets(str[i]);
}
printf("\n\n---\n\nExport the list of strings:\n\n");
for (int j = 0; j < N; j++)
{
printf("str[%d] = ", j);
printf("%s\n", str[j]);
}
}
When I compile it, the compiler does not return any error or warning. But, after I inputted the data, there is an error message from Windows:
A problem caused the program to stop working correctly
Then, my program is broken.
In this array: char * str[MAX]
None of the entries is initialized to point to a valid (properly allocated) memory block.
And so you should not scanf("%s", &str[i]) until you have initialized entry #i.
char * str[MAX];
str is an array of pointers. You should allocate memory to pointers before writing anything to it.
str[i] = malloc(20);/* Showing an example of using malloc() . How much memory should be allocated is left to you */
Using a scanf() to scan a string is not a good idea I would rather prefer fgets()

Allocate memory to array of strings in C

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));

Sorting an array of strings in C

I have an assignment I've been working on for a few hours now, and I can't seem to get it quite right. The assignment is to take a random number of names (from stdin), sort them, and then output them in alphabetical order. I can't find any sites online that handle this kind of sorting specifically, and have had no luck trying to implement qsort() into my code.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int stringcmp(const void *a, const void *b)
{
const char **ia = (const char **)a;
const char **ib = (const char **)b;
return strcmp(*ia, *ib);
}
void main(int argc, char *argv[])
{
char *input[] = {" "};
char temp[20][20];
int i = 0;
int num = 0;
int place = 0;
int stringlen = sizeof(temp) / sizeof(char);
printf("How many names would you like to enter? ");
scanf("%d", &num);
while (place < num)
{
printf("Please input a name(first only): ");
scanf("%s", input[place]);
printf("The name you entered is: ");
printf("%s\n", input[place]);
place++;
}
//qsort(temp, stringlen, sizeof(char *), stringcmp); <-- just an idea I was messing with
qsort(input, stringlen, sizeof(char *), stringcmp);
printf("Names:\n");
for(i=0; i<place; i++)
printf("%s\n", input[i]);
system("PAUSE");
return(EXIT_SUCCESS);
}
The main problem is, when I go to output my code, I cannot use the char *input variable because of how its declared. The temp[] will display, but will not be sorted by qsort because it is not declared as a pointer. Any ideas?
You can't declare your input array like that. Since you know how many the user requires, you can dynamically allocate the array:
char **input = malloc(num * sizeof(char*));
Likewise, when you read your strings in, they need somewhere to go. Simply passing an uninitialized pointer to scanf is not okay. I suggest you define the maximum length of a name and have a temporary buffer for reading it:
const size_t MAX_NAME = 50;
char name[MAX_NAME];
...
for( i = 0; i < num; i++ )
{
printf("Please input a name(first only): ");
scanf("%s", name);
input[i] = strdup(name);
}
[Note this does not prevent the user from overflowing the 'name' buffer. I used scanf for illustrative purposes only]
You seem to be passing the wrong array length to qsort. Try this:
qsort(input, num, sizeof(char *), stringcmp);
When you are finished, you need to release memory for all the names and the array.
for( i = 0; i < num; i++ ) free(input[i]);
free(input);
could you explain
the ** declarations throughout the code? I'm not sure what they're
used for, although I know the function for stringcmp is a widely used
algorithm, I have no idea how it works; I'm thrown off by the double
de-reference markers.
Yep, in the case where I used it, I am telling C that to get a single character, I have to dereference a pointer twice. When you index a pointer, it's dereferencing. So I allocated an array by requesting a block of memory containing num * sizeof(char*) bytes. Because I assigned that pointer to a char**, the compiler knows that I am pointing to a chunk of memory that contains char* values.
If I ask for input[0] (this is the same as *input) it should look at the very start of that memory and pull out enough bytes to form a char*. When I ask for input[1], it skips past those bytes and pulls out the next bunch of bytes that form a char*. Etc... Likewise, when I index a char*, I am pulling out single characters.
In your stringcmp function, you have the following situation. You passed a void* pointer to qsort so it doesn't actually know the size of the data values stored in your array. That's why you have to pass both the array length AND the size of a single element. So qsort just blindly rips through this arbitrary-length array of arbitrary-sized values and fires off memory addresses that ought to contain your data for comparison. Because qsort doesn't know anything else about your array elements except where they are located, it just uses void*.
But YOU know that those pointers are going to be the memory addresses of two of your array elements, and that your array elements are char*. So you need the address of a char* (hence you cast the pointers to char**). Now you need to dereference these pointers when you call strcmp() because that function requires a char* (ie a value that points directly to the memory containing your string characters). That is why you use the * in strcmp(*ia, *ib).
One quick way to fix your program is to declare input as an array of pointers, like this:
char *input[20];
When you read names in, use tmp[place] for your buffer, and store the pointer into input, like this:
scanf("%19s", tmp[place]);
input[place] = tmp[place];
Now sorting the input should work fine.
This has a limitation of being limited to 20 lines of 20 characters max. If you learned about malloc in the class, you should be able to fix that by allocating your strings and the string array dynamically.

C Programming - Changing chars of a string array element [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Problem with processing individual strings stored in an array of pointers to multiple strings in C
Ok so I'm trying to change a char of a string to another char in C. The thing is, each string is an element of a 1D array so essentially all together its a 2D array because a string itself is an array of chars. Anyways I have a problem creating code to do this. Is it even possible to do this? Any help is appreciated.
Here's the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main ()
{
int i, size;
char **a;
a=(char**)malloc(sizeof(char*));
printf("Enter the size of the array:");
scanf("%d", &size);
for(i=0;i<size;i++){
a[i]=(char*)malloc(sizeof(char)*8);
}
a[3]="Read";
while(*(a[3])!='\0'){
if(*(a[3]) == 'e'){
*(a[3]) = 'r';
}
}
printf("%s\n", a[3]);
system("pause");
return 0;
}
a=(char**)malloc(sizeof(char*));
printf("Enter the size of the array:");
scanf("%d", &size);
for(i=0;i<size;i++){
a[i]=(char*)malloc(sizeof(char)*8);
}
Nope. You've allocated 1 char*. Then you treat it like it's size elements. You need to allocate size * sizeof(char*) bytes. (Note that this multiplication could also overflow.)
a[3]="Read";
Bad times. You are overwriting a[3] (which previously pointed to an allocation of 8 chars) with the location of a string literal, "Read". This leaks the previous allocation and also puts a non-modifiable string into a[3]. You should look into strncpy et al. for this.
You didn't allocate enough space for a. Instead of
a=(char**)malloc(sizeof(char*));
you need
a=(char**)malloc(sizeof(char*)*size);
and obviously this must move to be after size has been read.
Once you sort that rather mundane problem out the fundamental problem is here:
a[3]="Read";
This makes the pointer a[3] point at a literal which cannot be modified. Instead you need to copy the contents of that literal into a[3]. Like this:
strcpy(a[3], "Read");
You must understand that a[3]=... assigns just the pointer a[3] and does not modify the string to which a[3] points.
Now, your code will obviously be in error if size is less than 4 since then a[3] would be out of bounds, but I guess a[3] is just transient while you debug this.
Your while loop is all wrong. Judging from your comments you want something like this:
char *p = a[3];
while (*p != '\0')
{
if (*p == 'e')
*p = 'r';
p++;
}
No need to cast the return value of malloc in C, so remove the casts. sizeof(char) is always equal to 1 so you can remove that too.
This:
a=(char**)malloc(sizeof(char*));
allocates space for one string. What you probably intend is something like:
char **a = NULL;
size_t number_of_strings = 8; /* for argument's sake */
a = malloc(number_of_strings * sizeof(char*));
if (!a)
return NOT_ENOUGH_MEMORY_ERROR;
At this point, you can dereference elements of a, e.g., a[3]. You'll still want to allocate space for those guys too:
char *staticStr = "Read";
a[3] = malloc(strlen(staticStr) + 1);
strncpy (a[3], staticStr, strlen(staticStr) + 1);
Start with that and see if rethinking how you're allocating memory will help you fix your other bugs.
Some notes:
You don't need to cast the result of malloc in C
You don't need to use sizeof(char) for allocating memory, which is always 1
You should be using a corresponding free() for each a[i] and for a itself, to prevent memory leaks
I see another problem, when you allocate memory for the:
a = (char **) malloc(sizeof(char *));
You are allocating memory for just one position, but you are using size positions. Then your code should be:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main ()
{
int i, size;
char **a;
char *ptr;
printf("Enter the size of the array:");
scanf("%d", &size);
a=(char**)malloc(size * sizeof(char*));
for(i=0;i<size;i++){
a[i]=(char*)malloc(sizeof(char)*8);
}
strcpy(a[3], "Read");
ptr=a[3];
while(*ptr!='\0'){
if(*ptr == 'e'){
*ptr = 'r';
}
ptr++;
}
printf("%s\n", a[3]);
system("pause");
return 0;
}
and of course, you need to free the allocated memory.
In your while when you try to change the 'e' to 'r' you are always point to the same char. You need a new pointer to walk throw the array.
Your while loop doesn't do anything.
while(*(a[3])!='\0'){
if(*(a[3]) == 'e'){
*(a[3]) = 'r';
}
}
It doesn't advance the pointer, it just keeps it at the first position.
A more proper-ish way would be to make a temporary pointer and use it to walk the string
char *temp = a[3];
while (*temp != '\0') {
if (*temp == 'e') *temp = 'r';
temp++;
}

Resources