Trouble in C with string array where elements are repeated - c

I'm trying to take an unknown amount of lines of console input, convert each line to a String using malloc, and then add each string to an array of strings by dynamically reallocating memory each time. My goal is to have an array where each element is a different line entered by the console (the while loop should end with EOF).
My code is below.
char * inputString = (char *)malloc(100);
char * aWord = (char *)malloc(1);
char ** listWords = (char **)malloc(1);
int wordCount = 0;
while (fgets(inputString, 100, stdin) != NULL)
{
wordCount++;
*(inputString + (strlen(inputString) - 1)) = '\0';
aWord = (char *)realloc(aWord, strlen(inputString) + 1);
aWord = inputString;
listWords = realloc(listWords, sizeof(char) * wordCount);
*(listWords + (wordCount - 1)) = aWord;
}
for (int i = 0; i < wordCount; i++)
printf("%s\n", listWords[i]);
If I were to input in console
abc\n
b\n
cad\n
^Z\n
In theory, I would like my code to print out
abc\n
b\n
cad\n
Each line of console input. Instead it prints
cad\n
cad\n
cad\n
The last line entered. Help much appreciated

You can't copy a string using an assignment, you have to use strcpy():
strcpy(aWord, inputString);
Also,
*(listWords + (wordCount - 1)) = aWord;
can be simplified to:
listWords[wordCount-1] = aWord;

First is this:
aWord = (char *)realloc(aWord, strlen(inputString) + 1);
aWord = inputString;
Here you allocate space for aWord, but then overwrite the returned address with the address contained in inputString. So each string you read in ends up overwriting the previous string. You also should be using malloc instead of realloc because you want to get a new buffer instead of resusing an existing one.
Change the realloc call to malloc and instead of assigning one pointer to the other, usestrcpy` to copy it.
aWord = malloc(strlen(inputString) + 1);
strcpy(aWord, inputString);
Then on this line:
listWords = realloc(listWords, sizeof(char) * wordCount);
You only allocate enough memory for an array of characters instead of an array of pointers to characters. This results in writing past the end of allocated memory, invoking undefined beahvior.
The proper allocation is:
listWords = realloc(listWords, sizeof(char *) * wordCount);

There are three issues.
First, realloc does not allocate an additional aWord but increases/decreases the buffer of the current aWord, thereby probably giving back a new address while releasing the old one. So aWord = (char *)realloc(aWord, strlen(inputString) + 1) will adapt the memory to a new size, but you will loose the content written to aWord previously. Note that *(listWords + (wordCount - 1)) = aWord will let your final list point to aWord. Use malloc instead of realloc.
Second, with aWord = inputString;, you let aWord point to inputString instead of copying its contents. Use strcpy instead:
char *aWord = malloc(aWord, strlen(inputString) + 1);
strcpy (aWord,inputString);
Third, you need to reserve space for character pointers, not just characters.
listWords = realloc(listWords, sizeof(char*) * wordCount);

Related

Creating a dynamic array of characters using C with pointers to pointers

I'm trying to create an array of pointers to pointers, at least to my understanding. But I'm getting invalid reads and writes with valgrind running.
char **format_file(FILE *infile) {
char **char_array = malloc(20 * sizeof(char*));
int c;
int cUsed = 0;
while ((c = fgetc(infile)) != EOF) {
char_array[cUsed] = c;
cUsed += 1;
}
printf("%s", *char_array);
return char_array;
}
The code works by reading from an already opened file "infile". First I allocated memory for 20 characters with malloc, then I'm trying to read the file character by character into the allocated memory array until EOF is reached. However, valgrind's output is as follows when I make the code:
==7379== Invalid read of size 1
==7379== at 0x4E7CB36: vfprintf (vfprintf.c:1597)
==7379== by 0x4E85198: printf (printf.c:35)
==7379== by 0x400755: format_file (formatter.c:27)
==7379== by 0x4006C1: main (format265alt.c:21)
==7379== Address 0x6f is not stack'd, malloc'd or (recently) free'd
Line 27 is the printf command that valgrind refers to as an invalid read of size 1.
formatter.c is the file containing the format_file function, while format265alt.c is a file that calls the formatter.c function and opens the file to be read.
I'm confused by the syntax of **, that is, how do I access and read/write the allocated memory?
I apologize if I have not provided enough information about this problem.
valgrind complains because you are storing characters beyond the end of the allocated object. The compiler should complain that you are storing characters into an object of the wrong type, use -Wall -W to enable useful warnings.
A char ** is a pointer to a char pointer, it can point to an array of char pointers, also known as an array of strings. You must allocate the array and each of the strings with the appropriate size for the file contents.
Here there are 2 possibilities:
the function can either load the whole file into a single string, but there would be no need to return a pointer to a char*, just returning the string (char *) would suffice.
the proposed API is more appropriate if the function is to return a pointer to an array of strings, one per line, with an extra NULL at the end, just like the argv array passed as the second argument to the main function.
For this, you must reallocate the string array as more lines are read from the FILE* and each line should be reallocated as it grows. Add a NULL pointer at the end of the string array to indicate its end.
Here is a very inefficient way to do this:
#include <stdlib.h>
#include <stdio.h>
char **format_file(FILE *infile) {
size_t lines = 0;
char **array = malloc(1 * sizeof(char*));
size_t pos = 0;
char *line = malloc(1);
int c;
while ((c = getc(infile)) != EOF) {
if (c == '\n') {
line[pos] = '\0';
array = realloc(array, (lines + 2) * sizeof(char *));
array[lines++] = line;
line = malloc(1);
pos = 0;
continue;
}
line = realloc(line, pos + 2);
line[pos++] = c;
}
if (pos > 0) {
line[pos] = '\0';
array = realloc(array, (lines + 2) * sizeof(char *));
array[lines++] = line;
} else {
free(line);
}
array[lines] = NULL;
return array;
}
If you are just creating an array of characters, a one-dimensional character array is sufficient. No need for the char** business. However, make sure you null terminate the array if you are trying to use it as a string.

How to store fgets string results into an char array?

I am currently getting the following error
Process terminated with status -1073741819
and I suspect its my fgets() but I have no idea why this is happening, any help would be much appreciated.
//Gets Dictionary from file
char* GetDictionary() {
int ArraySize;
int i = 0;
FILE * DictionaryFile;
//Gets first line (in this case it is the amount of Lines)
DictionaryFile = fopen("dictionary.txt", "r");
fscanf(DictionaryFile,"%d", &ArraySize);
ArraySize = ArraySize + 1;
printf("%d", ArraySize);
fclose(DictionaryFile);
//Gets the array
char* Dictionary = malloc(sizeof(char)*ArraySize);
char Temp[ArraySize];
char TempArray[ArraySize];
DictionaryFile = fopen("dictionary.txt", "r");
while(fgets(Temp, sizeof Temp, DictionaryFile)!=NULL) {
Dictionary[i] = Temp;
//Check The array
printf("%s", Dictionary[i]);
i++;
}
fclose(DictionaryFile);
return Dictionary;
}
-1073741819 --> C0000005 and likely has some significance. Maybe use below to discern its meaning.
puts(strerror(-1073741819));
Code has many issues: Here are some corrected to get you going.
1) Allocate an array of pointers, not an array of char
// char* Dictionary = malloc(sizeof(char)*ArraySize);
char** Dictionary = malloc(ArraySize * sizeof *Dictionary);
2) Form a big buffer to read each line
char Temp[100];
3) After reading each line, get rid of the likely trailing '\n'
size_t len = strlen(Temp);
if (len && Temp[len-1] == '\n') Temp[--len] = 0;
4) Allocate memory for that word and save
Dictionary[i] = malloc(len + 1);
assert(Dictionary[i]);
memcpy(Dictionary[i], Temp, len + 1);
5) Robust code frees it allocations before completion
6) Code reads "amount of Lines" twice as file is opened twice. Just leave file open (and not re-open it). #user3386109
You likely want Dictionary to be an array of char strings. That is, Dictionary is an array, and each element in the array is a char *. That makes Dictionary a char **.
For this example, it may be most straightforward to allocate memory for the Dictionary array itself, then allocate memory for its contents. You'll need to free all this when you're done, of course.
char **Dictionary = malloc(sizeof(char *) * ArraySize);
for (int i = 0; i < ArraySize; i++) {
Dictionary[i] = malloc(ArraySize);
}
There are better ways to do this. For one, you might only allocate memory when you need it, for each fgets() return. You could also use strdup() to allocate only the memory you need. You could also pass in Dictionary from the caller, already allocated, so you don't worry about allocating it here.
Later in your program, as #WhozCraig pointed out, you need to copy the string in Temp, like strcpy(Dictionary[i], Temp), in place of Dictionary[i] = Temp. I too am surprised that's not generating a compiler warning!

C - list of char*'s - Memory Allocation

I am confused about how to allocate memory correctly. I am trying to make a list of char*'s from a text file. Every time I make a char* do I have to allocate memory for it? When and where are the exceptions?
#define BUFF 1000
int main(int argc, char** argv)
{
FILE* file;
file = fopen(argv[1], "r");
char* word = calloc(BUFF, sizeof(char));
char* sentence = calloc(BUFF, sizeof(char));
char** list = calloc(BUFF, sizeof(char*));
int i = 0;
while((fgets(sentence, BUFF, file)) != NULL)
{
word = strtok(sentence, " ,/.");
while(word != NULL)
{
printf("%s\n", word);
strcpy(list[i], word);
i++;
word = strtok(NULL, " ,/.");
}
}
int k;
for(k = 0; k < i; k++)
{
puts("segging here");
printf("%s\n", list[i]);
}
The rule is: you have to allocate any memory that you use.
Your problem comes in:
strcpy(list[i], word);
list[i] is currently not pointing to any allocated storage (it's probably a null pointer). You have to make it point somewhere before you can copy characters into it.
One way would be:
list[i] = strdup(word);
strdup is not an ISO C standard function, but it is equivalent to doing malloc then strcpy. You will need to free afterwards.
Also, the i++ line needs to stop when i == BUFF, and it'd be useful to add \n to the list of strtok separators .
In addition to Matt McNabb's answer, there's also a more subtle problem with your usage of strtok. That function doesn't require an output buffer; it just returns a pointer to somewhere inside the input buffer.
When you call char* word = calloc(BUFF, sizeof(char));, you allocate memory and assign word to point to the allocated memory. Then, when you call word = strtok(sentence, " ,/.");, you overwrite the value of word. That means that no pointer in your control points to the memory you've allocated. That memory is no longer useful to your code, and you can't deallocate it; it has been leaked.
You can fix this issue by writing char* word = strtok(sentence, " ,/."); Then, since you didn't allocate the memory that word points to, remember not to free it either.
your list is a char* list, with the size of BUFF, but the list[i] is what?
you do not allocate memory to it.
you need to allocate the memory for list[i] in a loop

Using malloc to create a string containing elements from another string in c

Create a function in C that takes a string as a parameter and copy it to a new string.
If the original string is "abc", then the new string should be "aabbcc", if the original string is "4", then the newstring should be 44 etc. I believe i understand the concepts needed to solve a problem like this, but i just can't get the new string to be printed in the console. Here is my function:
void eco(char * str)
{
int count = 0;
/*Counts the number of symbols in the string*/
while(*(str + count) != '\0')
{
count++;
}
/*Memory for the new string, wich should be 6 chars long ("aabbcc").*/
char * newstr = malloc(sizeof(char *) * (count * 2));
/*Creating the content for newstr.*/
while(count > 0)
{
*newstr = *str; //newstr[0] = 'a'
*newstr++; //next newstr pos
*newstr = *str; //newstr[1] = 'a'
*str++; //next strpos
count--;
}
/*I can't understand why this would not print aabbcc*/
printf("%s", newstr);
/*free newstr from memory*/
free(newstr);
}
I have tried to print every char individually inside the while loop that creates the content for newstr, and that work. But when i try with the "%s"-flag i either get strange non-keyboard symbols or nothing at all.
I can't understand why this would not print "aabbcc"
It wouldn't do it for two reasons:
You are not passing a pointer to the beginning of the string, and
Because you did not add a null terminator
To fix the first problem, store the pointer to the block allocated to newstr in a temporary before doing the increments.
To fix the second problem, add *newstr = '\0' after the loop, and adjust malloc call to add an extra char for the terminator.
// Do not multiply by sizeof(char), because the standard requires it to be 1
// You used sizeof(char*), which is wrong too.
char * newstr = malloc((count * 2) + 1);
char *res = newstr; // Store the original pointer
// Your implementation of the actual algorithm looks right
while (...) {
... // Do the loop
}
*newstr = '\0';
printf("%s\n", res); // Pass the original pointer
Your loop advances newstr, so after it completes, it's not pointing at the beginning of the string anymore. You need to save the original pointer to use for printing.
For a start this line
char * newstr = malloc(sizeof(char *) * (count * 2));
should be
char * newstr = malloc(1 + (count * 2));
to include the null character
Then you forgot to add it
Also newstr points to the end to the new string

how to put char * into array so that I can use it in qsort, and then move on to the next line

I have lineget function that returns char *(it detects '\n') and NULL on EOF.
In main() I'm trying to recognize particular words from that line.
I used strtok:
int main(int argc, char **argv)
{
char *line, *ptr;
FILE *infile;
FILE *outfile;
char **helper = NULL;
int strtoks = 0;
void *temp;
infile=fopen(argv[1],"r");
outfile=fopen(argv[2],"w");
while(((line=readline(infile))!=NULL))
{
ptr = strtok(line, " ");
temp = realloc(helper, (strtoks)*sizeof(char *));
if(temp == NULL) {
printf("Bad alloc error\n");
free(helper);
return 0;
} else {
helper=temp;
}
while (ptr != NULL) {
strtoks++;
fputs(ptr, outfile);
fputc(' ', outfile);
ptr = strtok(NULL, " ");
helper[strtoks-1] = ptr;
}
/*fputs(line, outfile);*/
free(line);
}
fclose(infile);
fclose(outfile);
return 0;
}
Now I have no idea how to put every of tokenized words into an array (I created char ** helper for that purpose), so that it can be used in qsort like qsort(helper, strtoks, sizeof(char*), compare_string);.
Ad. 2 Even if it would work - I don't know how to clear that line, and proceed to sorting next one. How to do that?
I even crashed valgrind (with the code presented above) -> "valgrind: the 'impossible' happened:
Killed by fatal signal"
Where is the mistake ?
The most obvious problem (there may be others) is that you're reallocating helper to the value of strtoks at the beginning of the line, but then incrementing strtoks and adding to the array at higher values of strtoks. For instance, on the first line, strtoks is 0, so temp = realloc(helper, (strtoks)*sizeof(char *)); leaves helper as NULL, but then you try to add every word on that line to the helper array.
I'd suggest an entirely different approach which is conceptually simpler:
char buf[1000]; // or big enough to be bigger than any word you'll encounter
char ** helper;
int i, numwords;
while(!feof(infile)) { // most general way of testing if EOF is reached, since EOF
// is just a macro and may not be machine-independent.
for(i = 0; (ch = fgetc(infile)) != ' ' && ch != '\n'; i++) {
// get chars one at a time until we hit a space or a newline
buf[i] = ch; // add char to buffer
}
buf[i + 1] = '\0' // terminate with null byte
helper = realloc(++numwords * sizeof(char *)); // expand helper to fit one more word
helper[numwords - 1] = strdup(buffer) // copy current contents of buffer to the just-created element of helper
}
I haven't tested this so let me know if it's not correct or there's anything you don't understand. I've left out the opening and closing of files and the freeing at the end (remember you have to free every element of helper before you free helper itself).
As you can see in strtok's prototype:
char * strtok ( char * str, const char * delimiters );
...str is not const. What strtok actually does is replace found delimiters by null bytes (\0) into your str and return a pointer to the beginning of the token.
Per example:
char in[] = "foo bar baz";
char *toks[3];
toks[0] = strtok(in, " ");
toks[1] = strtok(NULL, " ");
toks[2] = strtok(NULL, " ");
printf("%p %s\n%p %s\n%p %s\n", toks[0], toks[0], toks[1], toks[1],
toks[2], toks[2]);
printf("%p %s\n%p %s\n%p %s\n", &in[0], &in[0], &in[4], &in[4],
&in[8], &in[8]);
Now look at the results:
0x7fffd537e870 foo
0x7fffd537e874 bar
0x7fffd537e878 baz
0x7fffd537e870 foo
0x7fffd537e874 bar
0x7fffd537e878 baz
As you can see, toks[1] and &in[4] point to the same location: the original str has been modified, and in reality all tokens in toks point to somewhere in str.
In your case your problem is that you free line:
free(line);
...invalidating all your pointers in helper. If you (or qsort) try to access helper[0] after freeing line, you end up accessing freed memory.
You should copy the tokens instead, e.g.:
ptr = strtok(NULL, " ");
helper[strtoks-1] = malloc(strlen(ptr) + 1);
strcpy(helper[strtoks-1], ptr);
Obviously, you will need to free each element of helper afterwards (in addition to helper itself).
You should be getting a 'Bad alloc' error because:
char **helper = NULL;
int strtoks = 0;
...
while ((line = readline(infile)) != NULL) /* Fewer, but sufficient, parentheses */
{
ptr = strtok(line, " ");
temp = realloc(helper, (strtoks)*sizeof(char *));
if (temp == NULL) {
printf("Bad alloc error\n");
free(helper);
return 0;
}
This is because the value of strtoks is zero, so you are asking realloc() to free the memory pointed at by helper (which was itself a null pointer). One outside chance is that your library crashes on realloc(0, 0), which it shouldn't but it is a curious edge case that might have been overlooked. The other possibility is that realloc(0, 0) returns a non-null pointer to 0 bytes of data which you are not allowed to dereference. When your code dereferences it, it crashes. Both returning NULL and returning non-NULL are allowed by the C standard; don't write code that crashes regardless of which behaviour realloc() shows. (If your implementation of realloc() does not return a non-NULL pointer for realloc(0, 0), then I'm suspicious that you aren't showing us exactly the code that managed to crash valgrind (which is a fair achievement — congratulations) because you aren't seeing the program terminate under control as it should if realloc(0, 0) returns NULL.)
You should be able to avoid that problem if you use:
temp = realloc(helper, (strtoks+1) * sizeof(char *));
Don't forget to increment strtoks itself at some point.

Resources