Unable to print string from 2D array in struct? - c

I'm from the Python/JS world, so I'm having issues wrapping my head around strings and arrays in C.
I'm attempting to create a struct -- FileInfo -- which contains a 2D array so that I can store a number of email addresses. I have a function -- createFileInfo() -- which creates the struct, manages memory for the struct and array indexes, assigns an email address to the array, and returns a pointer to the struct:
typedef struct FileInfo {
char **emailArr;
} FileInfo;
FileInfo *createFileInfo(int count) {
FileInfo *fi = malloc(sizeof(FileInfo));
char *buffer;
char *emailPrefix = "test";
char *emailDomain = "#test.com";
for (int i=0; i<count; ++i) {
fi->emailArr[i] = malloc(count * sizeof(char));
}
snprintf(buffer, sizeof(char), "%s%s", emailPrefix, emailDomain);
for (int i=0; i<count; ++i) {
for(int j=0; j<count; ++j) {
fi->emailArr[i][j] = *buffer;
}
}
return fi;
}
Then, in my main() function, I'm trying to create a pointer to the struct and print the index that contains the email address. However, I cannot get the string to print.
int main() {
int count = 1;
FileInfo *fi = createFileInfo(count);
for (int i=0; i<count; ++i) {
for(int j=0; j<count; ++j) {
fprintf(stdout, "test %s\n", fi->emailArr[i][j]);
}
}
for (int i=0; i<count; ++i) {
free(fi->emailArr[i]);
}
free(fi);
return 0;
}
What am I doing incorrectly? I'm also having quite the time with memory management; is memory allocation the issue?

In order to allocate a 2D dynamic array in C, you would need to do this instead, assuming that your 2D array is of dimensions N x M, where N is the number of emails, and M a number large enough to be able to store the email (that is something bigger than the length of the longest email to be stored, +1 for the C-string Null Terminator):
fi->emailArr = malloc( N*sizeof(char *) );
for(int i = 0 ; i < N ; i++)
fi->emailArr[i] = malloc( M*sizeof(char) );
In your attempt you missed the dynamic allocation which is just before the for loop.
You should also allocate memory for your buffer pointer. Now you do:
char *buffer;
snprintf(buffer, sizeof(char), "%s%s", emailPrefix, emailDomain);
but buffer has no memory allocated for it, so no place to store the string. You could do this instead:
size_t email_len = strlen(emailPrefix) + strlen(emailDomain);
char *buffer = malloc(email_len + 1); // +1 for the Null Terminator
snprintf(buffer, email_len, "%s%s", emailPrefix, emailDomain);
I also changed the call to populate the buffer, since the 2nd parameter of snprintf(), n, is the "Maximum number of bytes to be used in the buffer".
Putting everything together, a complete, minimal and basic example would look like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct FileInfo {
char **emailArr;
} FileInfo;
FileInfo *createFileInfo(int count) {
FileInfo *fi = malloc(sizeof(FileInfo));
char *emailPrefix = "test";
char *emailDomain = "#test.com";
size_t email_len = strlen(emailPrefix) + strlen(emailDomain);
char *buffer = malloc(email_len + 1); // +1 for the Null Terminator
snprintf(buffer, email_len + 1, "%s%s", emailPrefix, emailDomain);
fi->emailArr = malloc( count * sizeof(char *) );
for(int i = 0 ; i < count; i++)
fi->emailArr[i] = malloc( (email_len + 1) * sizeof(char) );
for(int i = 0 ; i < count; i++)
strcpy(fi->emailArr[i], buffer);
return fi;
}
int main(void) {
int count = 1;
FileInfo *fi = createFileInfo(count);
for (int i = 0; i < count; ++i) {
printf("%s\n", fi->emailArr[i]);
}
for (int i = 0; i < count; ++i) {
free(fi->emailArr[i]);
}
free(fi);
return 0;
}
Output:
test#test.com
Now of course, once you understand this piece of code, you could, if you like, think of how you would generalize this code to handle more emails than one.

emailArr is not initialized. You should initialize it first:
f->emailArr = malloc(number_of_char_arrays * sizeof(char*))
buffer is not initialized, too:
buffer = malloc(char_array_length * sizeof(char))
About snprintf's 2nd argument:
Maximum number of bytes to be used in the buffer.
The generated string has a length of at most n-1, leaving space for the additional >terminating null character.
size_t is an unsigned integral type.
It should be at least 14 if you want it to copy emailPrefix and emailDomain to buffer.
fprintf(stdout, "test %s\n", fi->emailArr[i][j]);
fi->emailArr[i][j] is a single character. You should only use the first index if you want to print it by %s.

In the createFileInfo function you're effectively creating a count*count char array, which I doubt is what you want and with count = 1 it can't contain the string "test#test". You should be allocating the size of the string you want to store, not "count" chars.
int size = 9; //this should probably be a parameter of the function
for (int i=0; i<count; ++i) {
fi->emailArr[i] = malloc(size * sizeof(char));
}
And you should allocate emailArr itself first (more on this here)
fi->emailArr = malloc(count * sizeof(char *));
You're also trying to copy the string in the wrong way; right now you're copying the pointer to buffer in each character of the string, which means each "string" is actually an array of pointers and cannot be printed as a string (I'm surprised it even compiles). It also means that if the content buffer points to changes, every element in your array will change. You need to use strncpy to do what you want.
for (int i=0; i<count; ++i) {
strncpy(fi->emailArr[i], buffer, size);
}
You also forgot to allocate memory for buffer.
I suggest you try some simpler programs involving strings to familiarize yourself with pointer logic.

Related

Loop of sprintf only outputing final looped value

I have an char* array. I loop through the correct index of the array to put numbers.
However, it seems that the value placed in the index is wrong. It seems to only store the last call to sprintf.
For instance, if I want the numbers 0, 1, 2, 3. However, it seems to return 3, 3, 3, 3.
I have placed a printf statement to see if there is an error, but it produces the correct numbers. I assume there must be a weird error when using sprintf with an array.
char* printArray[12];
for (int i = 1; i < 12+1; i++) {
char *printVal;
char temp[10];
sprintf(temp, "%d", i-1);
printVal = temp;
printf("%s\n", printVal);
printArray[i-1] = printVal;
}
for (int i = 0; i < 12; i++) {
printf("%s\n", printArray[i]);
}
This is a simplified version. This produces the error.
I have attempted to use strcpy but that results in the same error.
The problem is that the memory for temp goes away when you leave each iteration of the for loop, so you can't save pointers to temp into printArray.
You need to make a dynamic copy of temp so you can save it outside the loop.
printArray should be an array of strings, not pointers, then you can use strcpy() to copy them.
char *printArray[12];
for (int i = 1; i < 12+1; i++) {
char temp[10];
sprintf(temp, "%d", i-1);
printf("%s\n", temp);
printArray[i-1] = strdup(temp);
}
for (int i = 0; i < 12; i++) {
printf("%s\n", printArray[i]);
}
If you don't have strdup() available, it's simple:
char *my_strdup(const char *s) {
char *new = malloc(strlen(s)+1);
if (new) {
strcpy(new, s);
}
return new;
}
Undefined behavior (UB)
printArray[i-1] = printVal; repeatedly assigns the address of local object char temp[10];.
Later code attempts to print the data in this common, now invalid, address: result UB.
Instead of copying a pointer, copy the contents into memory.
// char* printArray[12];
#define INT_STRING_SIZE 12
char printArray[12][INT_STRING_SIZE];
...
for (int i = 0; i < 12; i++) {
snprintf(printArray[i], sizeof printArray[i], "%d", i);
}
If code must remain as char* printArray[12];, allocate memory.
for (int i = 0; i < 12; i++) {
char temp[INT_STRING_SIZE];
int len = snprintf(temp, sizeof temp, "%d", i);
printArray[i] = malloc(len + 1);
// Check for allocation omitted brevity.
memcpy(printArray[i], temp, len + 1);
// or
strcpy(printArray[i], temp);
}
And free the memory when done.

need help in understanding passing char* [] to function

I am trying to pass a array of pointers to string to a function where I need to set the values. In the passing function I do not know the number of strings I will get, the called function is calling some other function which returns list of strings.
Sample code below:
int main() {
char** list;
create(list);
}
int create(char **array) {
char* str[] = { "hello", "dear" };
int len;
int i = 0;
for (i = 0; i < 2; i++) {
len = strlen(str[i]);
printf("%d\n", len);
*(array + i) = (char*) malloc(len * sizeof(char*));
strcpy(*(array + i), str[i]);
i++;
}
return 1;
}
This gives me segmentation fault.
What wrong am I doing here. Please help.
Thanks
EDIT
Updated code from below comments:
int main() {
char** list;
create(list);
int i = 0;
for (i = 0; i < 2; i++) {
printf("%s\n", list[i]); // segmentation fault
}
}
int create(char **array) {
char* str[] = { "hello", "dear" };
int len;
int i = 0;
array = malloc(2 * sizeof(char*));
for (i = 0; i < 2; i++) {
len = strlen(str[i]);
printf("%d\n", len);
*(array + i) = (char*) malloc(len * sizeof(char));
strcpy(*(array + i), str[i]);
printf("%s\n", array[i]); // this prints
}
return 1;
}
Now getting segmentation fault in main while printing the list.
Actual code where I am reading the strings
int i;
for ( i=0; i<reply->elements; i++ )
{
printf( "Result: %d---%s\n", i,reply->element[i]->str );
*array[i] = (char*)malloc(strlen(reply->element[i]->str));
printf("***");
strcpy(array[i],reply->element[i]->str);
printf( "Array[%d]: %s\n", i,array[i] );
}
You correctly alloc memory for the individual strings, but fail to alloc some for the array itself.
You should use:
int main() {
char* list[8] = {0}; /* initialize pointers to NULL */
create(list);
/* free allocated memory - free(NULL) is legal and is a noop */
for (i=0; i<sizeof(list)/sizeof(list[0]); i++) free(list[i]);
return 0; /* never return random value from main */
}
And you should remove the i++ at the end of the loop in function create because it leads to a double increment.
Alternatively you could alloc the array itself in the function create:
int create(char ***array) {
char* str[] = { "hello", "dear" };
int len;
int i = 0;
*array = malloc(1 + sizeof(str)/sizeof(str[0]));
for (i = 0; i < 2; i++) {
len = strlen(str[i]) + 1;
printf("%d\n", len);
(*array)[i] = malloc(len * sizeof(char*));
strcpy((*array)[i], str[i]);
}
(*array)[i] = NULL;
return i;
}
int main() {
char** list;
create(&list);
}
In above code, the length of the array is the return value from create, and the last element of list is a NULL (in the same logic as argc/argv).
You need to allocate some space for list or undefined behavior occurs:
char* list[2];
You increment i twice; therefore, remove the i++ from the bottom of the for loop.
Minor notes:
refer to string literals as const char*
use array[i] instead of *(array + i)
don't cast the result of malloc
malloc allocates too much space as you allocate len char*s, even though you need just chars. Also, as #CoolGuy noted, you need one extra byte for the null byte. Replace the allocation with
array[i] = malloc(len * sizeof(char) + sizeof(char));
or
array[i] = malloc(len + 1);
call free after malloc
you assign 0 twice to i; remove the initialization
You allocate two arrays (char*) to store the strings "hello" and "dear" but does not allocate the array (char**) containing those two string array.
I would suggest you to change declaration of function create to this -
int create(char ***array);
And call it like this -
create(&list);
In function create allocate memory like this -
*array = malloc(2 * sizeof(char*));
for (i = 0; i < 2; i++)
{
len = strlen(str[i]);
printf("%d\n", len);
(*array)[i] =malloc(len * sizeof(char*)+1);
strcpy((*array)[i], str[i]);
}
And do the printing as you do in main.
Note - free memory that you allocate.
And you should declare len as type size_t -> size_t len;
and print it with %zu specifier in printf .
See working code here -https://ideone.com/GX2k9T

dynamically allocated char array only prints first character from each index

I'm trying to read from a file which has words in which are separated by spaces, the words are read within a loop. They are read correctly within the loop and they can be printed as so but as soon as the loop ends I am only able to print the first character of each element. Here is the code:
char **storeWords(char **words){
char* fileName = "words.txt";
FILE* fp = fopen(fileName, "r");
int i;
for (i = 0; i < 2; i++){
int j;
for (j = 0; j < 20; j++){
fscanf(fp, "%s", &words[i][j]);
printf("%s", &words[i][j]); //prints correctly
}
}
printf("%s", &words[0][0]); //prints first character of selected element
fclose(fp);
return **words;
}
int main(){
char **words = (char**)malloc(6 * sizeof(char*));
for (int i = 0; i < 6; i++){
words[i] = (char*)malloc(20 * sizeof(char));
}
storeWords(words);
system("pause");
return 0;
}
I don't understand why this happens, it would be appreciated if this could be explained. Thanks.
The problem is that you are allocating a 2D array of chars, not a 2D array of strings.
This is a pointer to a string, or a 1D array of chars:
char*
This is a pointer to a 1D array of strings, or a 2D array of chars:
char**
And this is a pointer to a 2D array of strings, or a 3D array of chars:
char***
You were trying to store a string to a char, and the only reason you didn't get a crash was because you allocated a [6][20] array but only iterated over 2x20 words, so there was room for overflow.
So, in your main function you need to allocate like this (note you don't need to cast the pointer returned from malloc):
char ***words = malloc(6 * sizeof(char**));
for (int i = 0; i < 6; i++){
words[i] = malloc(20 * sizeof(char*));
}
Change your function declaration to use the new type:
char ***storeWords(char ***words)
Then when you read in the words, use a temp char buffer and dynamically allocate enough space based on the word size:
for (j = 0; j < 20; j++){
char word[100]; // max word size of 100 chars
fscanf(fp, "%99s", word);
words[i][j] = malloc(strlen(word) + 1);
strcpy(words[i][j], word);
printf("%s", words[i][j]); //prints correctly
}
Note that when you print out a word you don't need to use the ampersand, because words[i][j] now contains a char* not a char:
printf("%s", words[0][0]);

Seg Faulting Double Pointer, Correctly Malloced (I believe)

Ok, here's some overview about what I'm trying to achieve. I want the user to be able to enter any amount of strings, and then I want to save those strings in a double char pointer. At the moment I haven't dealt with scaling my memory allocation for my double char pointer because I want to get it working first.
char **list = malloc(sizeof(char*)*5);
for(i = 1; i < argc; i++) {
strcpy(list[i], argv[i]);
}
I honestly thought this was going to be simple, so hopefully I'm making some stupid mistake. I keep receiving a seg fault error at the strcpy function.
What you did is only allocated memory for array of pointers to strings (which is correct), but you also need to allocate memory for each string in your array:
First (simpler) option:
char **list = malloc(sizeof(char*) * argc);
for(i = 1; i < argc; i++)
{
list[i] = strdup(argv[i]);
}
second (more complex) option:
size_t n = 0;
char **list = malloc(sizeof(char*) * argc);
for(i = 1; i < argc; i++)
{
n = strlen(argv[i]) + 1;
list[i] = malloc(n);
strcpy(list[i],argv[i]));
}
You've allocated an array of pointers to strings, but you haven't allocated any space for the strings themselves. Every time you call strcpy(), you're passing it an uninitialized destination pointer. Add a line to the beginning of your loop:
list[i] = malloc(strlen(argv[i]) + 1);
If you want it to work for any number of strings, your initial allocation has to be:
char **list = malloc(sizeof(char *) * argc);
It's not correctly malloced you only malloced the main pointer not the other ones.
Try this
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(int argc, char **argv) {
char **list = malloc(sizeof(char*)*argc);
int i;
for(i = 0; i < argc-1; i++) {
list[i] = malloc(sizeof(char) * (strlen(argv[i+1]) + 1));
strcpy(list[i], argv[i+1]);
printf("%s\n", list[i]);
}
return 0;
}
EDIT I forget the v in argv
EDIT (working copy edited above) output below
./temp hello this is a list of arguments
hello
this
is
a
list
of
arguments

Difficulties with creating the Cartesian product of a pointer to char pointer

As input I have a pointer to char pointer containing:
{"ab", "cd"}
As output I need to create the following Cartesian product:
{"abab", "abcd", "cdab", "cdcd"}
I created a function that receives "ab, cd" and a pointer to char pointer that is meant to hold the resulting set. Although everything seems to be working fine inside the function, once it gets exited, my output remains empty. I suppose I'm doing something wrong during the concatenation but I'm not sure what.
This is how my code looks like:
#include <stdio.h>
void Permute(char**, int, char**);
main() {
// my input
int words = 2;
char **input;
input = malloc(sizeof(char*) * words);
input[0] = "ab";
input[1] = "cd";
// compute how much memory we need
char **output;
output = malloc(sizeof(char*) * (words * 2));
// start permutation
Permute(input, words, output);
// show output
int i = 0;
for(i = 0; i < (words * 2); ++i) {
// should print: {abcd, abab, cdab, cdcd}
// but nothing gets printed
printf("%s\n", output[i]);
}
free(input);
free(output);
}
void Permute(char **input, int words, char **output){
int i = 0, j = 0, k = 0;
char str[5];
for(i = 0; i < words; ++i) {
for(j = 0; j < words; ++j) {
strcpy (str, input[i]);
strcat (str, input[j]);
output[k] = str;
// at this point concatenation is printed correctly
printf("%s\n", output[k]); correctly
++k;
}
}
}
Edit
Thanks to Goz's comment I updated my function. Now, a pointer to char gets allocated, is pointed to the concatenation and is then stored inside output[k]. This way no data is lost when exciting the funcion:
void Permute(char **input, int words, char **output){
int i = 0, j = 0, k = 0;
char *p;
for(i = 0; i < words; ++i) {
for(j = 0; j < words; ++j) {
p = malloc(sizeof(char*) * 5);
strcpy(p, input[i]);
strcat (p, input[j]);
output[k] = p;
printf("%d %s \n", k, output[k]);
++k;
}
}
}
Edit
The buffer holding the result gets allocated before passing it over to the Permute function:
// compute how much memory we need
// allocate space for 4 pointers to char
char **output = malloc(sizeof(char*) * 4);
int i = 0;
// pre-allocate space for every pointer
for(i = 0; i < 4; i++)
output[i] = malloc( sizeof( char ) * 5 );
Edit
Free all memory pointed to by char pointer before cleaning up the pointer to char pointer:
// free memory
for(i = 0; i < 4; i++ )
free( output[i] );
free(output);
for(i = 0; i < 2; i++ )
free(input[i]);
free(input);
There are a couple of issues. Firstly you allocate a char*. You then assign it to a char** and expect it to have 2 dimensionality. It doesn't. You'd need to malloc a set of char* pointers (4 * whatever your pointer size is ... ie sizeof( char* )) then malloc 5 bytes for each of those pointers.
Furthermore in Permute you overwrite the pointer value with the pointer to str (which doesn't exist outside the function). You ought to be strcpy'ing the contents of str to output[k].
In answer to the comment: Yes that will work but it would be advisable to allocate the buffer before you go into the loop.
ie
char** ptr = malloc( sizeof( char* ) * 4 );
for( int i = 0; i++; i < 4 )
{
ptr[i] = malloc( sizeof( char ) * 4 ); // sizeof( char ) == 1 but its a good habit to get into.
}
Then as said before strcpy the temporary array into the relevant char* array.
Furthermore remember that when you free the memory you need to do the opposite of the loop above. ie dealloc the 4 individual arrays and then dealloc the array of pointers. ie:
for( int i = 0; i++; i < 4 )
{
free( ptr[i] );
}
free( ptr );
ie all 5 occasions malloc is called are met with a corresponding free. If you free the array of ptr first you cannot guarantee that the memory is valid. Therefore the 4 pointers stored in that array may no longer be valid. So free them first then the array of pointers.
char str[5]; in Permute is on the stack and lost after you exit Permute. output[k] will point to an undefined place once you exit Permute.
output = malloc(sizeof(char*) * (words * 2));
Ok, you created output[0], output[1], ... but what are their values?
output[0] is a char * ... where does it point to?
And you cannot copy the address of a local variable in Permute (str) to output. That object ceases to exist once the function returns.

Resources