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]);
Related
Full code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void printarray(int* array, int arraysize){
for (int i = 0; i<arraysize; i++){
printf("%d\n", *array);
array++;
}
}
void printStrArray(char** array, int arraysize){
int j = 0;
for (int i = 0; i<arraysize; i++){
j = 0;
while (array[i][j] != '\0'){
printf("%c", array[i][j]);
j++;
}
printf("\n");
}
}
int isStringInArray(char* string, char** stringArray, int arrayLen){ // returns 1 if string is contained in the array.
for (int i = 0; i < arrayLen; i++){
if (strcmp(string, stringArray[i]) == 0){
//printf("%s is equal to %s %d\n", string, stringArray[i], i);
return 1;
}
}
return 0;
}
int lenstring(char* string){ // checks string length (works only if string has null character at the end.)
char currchar = string[0];
int strlen = 0;
while (currchar != '\0'){
strlen++;
currchar = string[strlen];
}
return strlen;
}
char** riassemble(char* stringa){
char** riassembleds = calloc(1, sizeof(char*));
char* charLen = malloc(sizeof(char));
riassembleds[0] = charLen;
int riassembledLen = 1;
int stringalen = lenstring(stringa);
char tempstring[stringalen];
strcpy(tempstring, stringa);
for (int i = 0; i < stringalen; i++){
for (int j = 0; j < stringalen; j++){
tempstring[i] = stringa[j];
tempstring[j] = stringa[i];
//printf("%s\n", tempstring);
if (isStringInArray(tempstring, riassembleds, riassembledLen) == 0){
riassembleds = realloc(riassembleds, (riassembledLen+1)*sizeof(char*));
riassembledLen++;
riassembleds[riassembledLen-1] = calloc(stringalen, sizeof(char));
printf("%p\n", riassembleds[riassembledLen-1]);
strcpy(riassembleds[riassembledLen-1], tempstring);
}
strcpy(tempstring, stringa);
}
}
*charLen = (char)riassembledLen;
riassembleds[0] = charLen; /*return the array with the length of the it casted into a char pointer as the first element*/
return riassembleds;
}
int main(int argc, char *argv[]){
char** array = riassemble("ciao");
int arraylen = (int)(*(array[0]));
printf("\n%d\n", arraylen);
printStrArray(array, arraylen);
for (int i=0; i<arraylen; i++) {
free(array[i]);
}
free(array);
return 0;
}
i'm making a function that returns an array of pointers to a char, in which the first element is a pointer that points to the length of the array casted into a char.
When i try to free the elements in the array with
char** array = riassemble("ciao"); /*Creates the array*/
int arraylen = (int)(*(array[0])); /*Gets the length*/
for (int i=0; i<arraylen; i++) {
free(array[i]);
}
The program crashes after trying to free the second element of the array which is defined here:
riassembleds = realloc(riassembleds, (riassembledLen+1)*sizeof(char*));
riassembledLen++;
riassembleds[riassembledLen-1] = calloc(stringalen, sizeof(char));
printf("%p\n", riassembleds[riassembledLen-1]);
strcpy(riassembleds[riassembledLen-1], tempstring);
I really don't understand why this happens, one thing i noticed is that if i print the pointers that it's trying to free, they're not the same as when i print them right after allocating them in the riassemble function, but other than that i have no idea what i'm doing wrong.
edit: i was wrong about the fact that they're not the same, they actually are, i got confused.
Your function lenstring will return the length of the string without the terminating null character.
Therefore, the line
char tempstring[stringalen];
will create an array tempstringthat is sufficient in size to store the string stringa without the terminating null character.
However, the function call
strcpy(tempstring, stringa);
requires that tempstring is large enough to store stringa with the terminating null character. You are therefore writing to tempstring out of bounds, invoking undefined behavior.
In order to fix this, I recommend that you change the line
char tempstring[stringalen];
to:
char tempstring[stringalen+1];
The line
riassembleds[riassembledLen-1] = calloc(stringalen, sizeof(char));
has the same problem, as it only allocates sufficient space without the terminating null character, which causes the line
strcpy(riassembleds[riassembledLen-1], tempstring);
to invoke undefined behavior for the same reason.
Therefore, the line
riassembleds[riassembledLen-1] = calloc(stringalen, sizeof(char));
should be changed to
riassembleds[riassembledLen-1] = calloc(stringalen+1, sizeof(char));
I was able to find these errors very easily by using AddressSanitizer on your program.
I am 'saving' the reverse of an array into a new array.
Example:
array A = abc
array B = cba
The below code is my solution. The new reversed array B does not print unless it is explicitly looped over. Both fprint %s and fprint %c on individual elements print white space. What is causing this?
Note: Function takes a number and converts to a string first.
int reverse(int x){
int len = int_length(x); //Functions works, gets the length
len++; //add room for \0
char *num = (char *)malloc(len * sizeof(char));
char *ans = (char *)malloc(len * sizeof(char));
snprintf(num, len, "%d", x); //Turn the numbers into a char array
printf("%s\n", num);
int i;
for(i = 0; i < len; i++) {
ans[i] = num[len-(i+1)];
}
for(i = 0; i < len; i++) {
printf("%c\n", ans[i]); //PRINTS FINE
}
printf("\n%c\n", num[0]);
printf("%c\n", ans[0]); //NOTHING PRINTS
printf("%s\n", ans); //NOTHING PRINTS
return 0;
}
len++; //add room for \0
char *num = (char *)malloc(len * sizeof(char));
char *ans = (char *)malloc(len * sizeof(char));
that's good to add 1 to the length, but then don't use that number as the actual string length
Without debugging it, my guess is the nul terminator winds up at the start of the reversed string, making it empty when printed as a string (but showing up okay when you're forcing all characters to be printed beyond the ill-placed nul terminator)
I would not change len, then explicitly add 1 when allocating:
char *num = malloc(len+1);
char *ans = malloc(len+1);
(and of course drop the usual redundancy: don't cast a pointer from malloc, don't multiply by sizeof(char) since it's always 1)
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.
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
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.