Array of Strings issue - c

I am currently having difficulty reading words separated by spaces line by line from stdin. I am trying to read words line by line, and just print them, from accessing an array of strings.
If I am trying to read this sentence:
Enter words:
Hi there, how was your day sir?
Then I just want to print the sentence underneath, like this:
Your sentence:
Hi there, how was your day sir?
This is what my code is so far:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
int
main(int argc, char *argv[]) {
char *word = NULL;
char **words = NULL;
int word_size = 1, word_len = 0, word_count = 0;
int words_size = 1, i, ch;
word = (char*)malloc(word_size *sizeof(char));
words = (char **) malloc(words_size*sizeof(char*));
printf("Enter words:\n");
while ((ch = getchar()) != EOF) {
if (isalpha(ch)) {
word_size++;
word = realloc(word, word_size+1);
word[word_len++] = ch;
word[word_len] = '\0';
}
if (isspace(ch)) {
words_size++;
words = realloc(words, words_size+1);
words[word_count] = malloc(strlen(word)+1);
words[word_count++] = word;
word_len = 0;
word_size = 1;
}
if (ch == '\n') {
printf("Your sentence is:\n");
for (i = 0; i < word_count; i++) {
printf("%s ", words[i]);
}
printf("\n");
word_len = 0;
word_size = 1;
words_size = 1;
}
}
return 0;
}
I am just not sure why this doesn't work, and why it prints the last word. I know there is a lot of mallocing and reallocing, I am just trying to get better at using them.
Any help would be appreciated

You are failing assigning the word to your char **.
Using
words[word_count++] = word;
You are assigning address of local variable word to pointer words[word_count]
That gave you, at the end of computation, all words with last stored word into word c-string.
You prepare space for the word c-string using
words[word_count] = malloc(strlen(word)+1);
So what you have to do is to copy the content of word c-string into allocaded space
strcpy(words[word_count++], word);
Otherwise you are leaking memory allocated for the word.
Side notes:
malloc and realloc can fail, so check its return value != NULL
You must free mallocated memory. On "hi-level" OS memory is freed automatically at the end of execution, but is not granted on all platforms/OS
EDIT
Another problem is that you are reallocating the wrong size for your char**
You shoud use
words_size++;
words = realloc(words, sizeof(*words)*words_size);
That is size of char * for the new number of words to store
You can also avoid to use strlen, you have the length of word stored into word_len variable
words[word_count] = malloc(word_len+1);
Last thing, before to store a new word you should check that at least alpha char was found. This avoid the output of first space char of your test sting:
if ((isspace(ch)) && (word_size>1))

Related

C, values in array of pointers dissapear (pointers)

I seem to be losing the reference to my pointers here. I dont know why but I suspect its the pointer returned by fgets that messes this up.
I was told a good way to read words from a file was to get the line then separate the words with strok, but how can I do this if my pointers inside words[i] keep dissapearing.
text
Natural Reader is
john make tame
Result Im getting.
array[0] = john
array[1] = e
array[2] =
array[3] = john
array[4] = make
array[5] = tame
int main(int argc, char *argv[]) {
FILE *file = fopen(argv[1], "r");
int ch;
int count = 0;
while ((ch = fgetc(file)) != EOF){
if (ch == '\n' || ch == ' ')
count++;
}
fseek(file, 0, SEEK_END);
size_t size = ftell(file);
fseek(file, 0, SEEK_SET);
char** words = calloc(count, size * sizeof(char*) +1 );
int i = 0;
int x = 0;
char ligne [250];
while (fgets(ligne, 80, file)) {
char* word;
word = strtok(ligne, " ,.-\n");
while (word != NULL) {
for (i = 0; i < 3; i++) {
words[x] = word;
word = strtok(NULL, " ,.-\n");
x++;
}
}
}
for (i = 0; i < count; ++i)
if (words[i] != 0){
printf("array[%d] = %s\n", i, words[i]);
}
free(words);
fclose(file);
return 0;
}
strtok does not allocate any memory, it returns a pointer to a delimited string in the buffer.
therefore you need to allocate memory for the result if you want to keep the word between loop iterations
e.g.
word = strdup(strtok(ligne, " ,.-\n"));
You could also hanle this by using a unique ligne for each line read, so make it an array of strings like so:
char ligne[20][80]; // no need to make the string 250 since fgets limits it to 80
Then your while loop changes to:
int lno = 0;
while (fgets(ligne[lno], 80, file)) {
char *word;
word = strtok(ligne[lno], " ,.-\n");
while (word != NULL) {
words[x++] = word;
word = strtok(NULL, " ,.-\n");
}
lno++;
}
Adjust the first subscript as needed for the maximum size of the file, or dynamically allocate the line buffer during each iteration if you don't want such a low limit. You could also use getline instead of fgets, if your implementation supports it; it can handle the allocation for, though you then need to free the blocks when you are done.
If you are processing real-world prose, you might want to include other delimiters in your list, like colon, semicolon, exclamation point, and question mark.

Appending words to an array based on a separator

I am trying to break up the sentence "once upon a time" into an array of words. I am doing this via a for loop, detecting three conditions:
It's the end of the loop (add the \0 and break);
It's the separator character (add the \0 and advance to the next word)
It's anything else (add the character)
Here is what I have now:
#include <stdlib.h>
#include <stdio.h>
char ** split_string(char * string, char sep) {
// Allow single separators only for now
// get length of the split string array
int i, c, array_length = 0;
for (int i=0; (c=string[i]) != 0; i++)
if (c == sep) array_length ++;
// allocate the array
char ** array_of_words = malloc(array_length + 1);
char word[100];
for (int i=0, char_num=0, word_num=0;; i++) {
c = string[i];
// if a newline add the word and break
if (c == '\0') {
word[char_num] = '\0';
array_of_words[word_num] = word;
break;
}
// if the separator, add a NUL, increment the word_num, and reset the character counter
if (c == sep) {
word[char_num] = '\0';
array_of_words[word_num] = word;
word_num ++;
char_num = 0;
}
// otherwise, just add the character in the string and increment the character counter
else {
word[char_num] = c;
char_num ++;
}
}
return array_of_words;
}
int main(int argc, char *argv[]) {
char * input_string = "Once upon a time";
// separate the string into a list of tokens separated by the separator
char ** array_of_words;
array_of_words = split_string(input_string, ' ');
printf("The array of words is: ");
// how to get the size of this array? sizeof(array_of_words) / sizeof(array_of_words[0]) gives 1?!
for (int i=0; i < 4 ;i++)
printf("%s[sep]%d", array_of_words[i], i);
return 0;
}
However, instead of printing "once", "upon", "a", "time" at the end, it's printing "time", "time", "time", "time".
Where is the mistake in my code that is causing this?
Here is a working example of the code: https://onlinegdb.com/S1ss6a4Ur
You need to allocate memory for each word, not just for one. char word[100]; only puts aside memory for one word, and once it goes out of scope, the memory is invalid. Instead, you could allocate the memory dynamically:
char* word = malloc(100);
And then, when you found a separator, allocate memory for a new word:
if (c == sep) {
word[char_num] = '\0';
array_of_words[word_num] = word;
word = malloc(100);
Also, this here is incorrect:
char ** array_of_words = malloc(array_length + 1);
You want enough memory for all the char pointers, but you only allocate 1 byte per pointer. Instead, do this:
char ** array_of_words = malloc(sizeof(char*)*(array_length + 1));
The sizeof(array_of_words) / sizeof(array_of_words[0]) works to calculate the amount of elements when array_of_words is an array, because then its size is known at compile time (barring VLAs). It's just a pointer though, so it doesn't work as sizeof(array_of_words) will give you the pointer size. Instead, you'll have to calculate the size on your own. You already do so in the split_string function, so you just need to get that array_of_words out to the main function. There are multiple ways of doing this:
Have it be a global variable
Pass an int* to the function via which you can write the value to a variable in main (this is sometimes called an "out parameter")
Return it along with the other pointer you're returning by wrapping them up in a struct
Don't pass it at all and recalculate it
The global variable solution is the most simple for this small program, just put the int array_length = 0; before the split_string instead of having it inside it.
Last but not least, since we used malloc to allocate memory, we should free it:
for (int i = 0; i < array_length; i++) {
printf("%s[sep]%d", array_of_words[i], i);
free(array_of_words[i]); // free each word
}
free(array_of_words); // free the array holding the pointers to the words
Is strtok not suitable?
char str[] = "once upon a time";
const char delim[] = " ";
char* word = strtok(str, delim);
while(word != NULL)
{
printf("%s\n", word);
word = strtok(NULL, delim);
}

Dynamically create array in C taking in 1 character at a time

I am given an assignment to take in and store a string using a function, however, I am given some restrictions.
Only able to use getchar() to take in user input character by character
No assumption of length of the input (Not allowed to create a array of size 100 for example)
Not allowed to read the input twice, for example, using the first round of input to count string size and then ask the user to input again after creating an array of the string's size that was counted on the first round.
Not allowed to create a large buffer so a constant size buffer means memory will be wasted if the input is 1 character for example
int read_string()
{
char* input;
int counter = 0;
while (( input = getchar()) != '\n') //read until detect '\n'
{
printf("%c\n",input);
counter = counter + 1;
}
printf("Length of string: %d\n", counter);
}
I currently have no idea how to store character by character and dynamically resize an "array" like vectors equivalent in C++. C does not have vectors based on my research.
Based on my code now, when i type in "Hello",
the output will be
h
e
l
l
o
but I do not know how to store each character in a dynamic array
You'd have to use the realloc function, if you want to dynamically increase the size with every new character that you read.
When you use realloc, the content of the memory block is preserved up to the lesser of the new and old sizes, even if the block is moved to a new location. If the function fails to allocate the requested block of memory, a null pointer is returned.
For every character that I read, I increment buffsize, but I do allocate buffsize + 1. Why? Because I need one extra position for the NULL terminator.
The last free position for a letter would be buffsize - 1 in this case and the last one will be assigned at the end of the while loop.
#include <stdio.h>
#include <stdlib.h>
int main(void) {
size_t buffsize = 0;
char *buffer = NULL;
char *temp;
char input;
while ((input = getchar()) != '\n') {
printf("%c\n", input);
/* Incraese the size & realloc */
++buffsize;
temp = realloc(buffer, (buffsize + 1) * sizeof(char));
if (!temp) {
printf("Error reallocating buffer!\n");
exit(1);
}
/* Setting the new read char */
buffer = temp;
buffer[buffsize - 1] = input;
}
if (buffsize) {
buffer[buffsize] = '\0';
printf("Result = [%s]\n", buffer);
} else {
printf("Empty input!\n");
}
printf("String size=%lu\n", buffsize);
/* Clean */
free(buffer);
return 0;
}
A bit more generic - function which adds a char to the string. Initially pointer should be NULL and it will take it into account automatically
char *addchar(char **str, int c)
{
size_t len= 0;
char *tmp;
if(*str)
{
len = strlen(*str);
}
tmp = realloc(*str, len + 2);
if(tmp)
{
*str = tmp;
tmp[len] = c;
tmp[len + 1] = 0;
}
return tmp;
}
and usage - a bit different than yours
int main()
{
char *mystring = NULL;
int input;
while (( input = getchar()) != EOF)
{
if(input == '\n' || input == '\r') continue;
if(!addchar(&mystring, input))
{
printf("\nMemory allocation error\n");
}
else
{
printf("String length %zu\n", strlen(mystring));
}
}
}
First off, the function getchar() returns and int not char * so you should not assign its return value to the pointer input declared in your code as char* input;
You should start by declaring an int variable; could be called len ; and initialize it with the value of 0. Next you should call the function malloc() and feed it 1 to allocate 1 byte of memory to hold a single character, and assign its return value to the pointer input, like the following:
int len = 0;
input = malloc(1);
Then you should store the NUL-terminating character '\0' in the allocated memory:
input[0] = '\0';
Then you create an int variable since the return value of getchar() is int. This variable which could be called ch shall store the user input.
Then you increase the size of your allocated storage to accommodate the new character:
input = realloc(input, len + 1);
input[len] = ch;
len++;
The entire code should look like the following:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int len = 0;
char *input = malloc(1);
input[0] = '\0';
int ch;
while ((ch = getchar()) != '\n')
{
input = realloc(input, len + 1);
input[len] = ch;
len++;
}
input[len] = '\0';
printf("You entered: %s\n", input);
printf("Length of str: %d\n", len);
free(input);
return 0;
}

"Pointer being freed was not allocated" happen on mac but not on window7

I am doing an exercise on a book, changing the words in a sentence into pig latin. The code works fine in window 7, but when I compiled it in mac, the error comes out.
After some testings, the error comes from there. I don't understand the reason of this problem. I am using dynamic memories for all the pointers and I have also added the checking of null pointer.
while (walker != NULL && *walker != NULL){
free(**walker);
free(*walker);
free(walker);
walker++;
}
Full source code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#define inputSize 81
void getSentence(char sentence [], int size);
int countWord(char sentence[]);
char ***parseSentence(char sentence[], int *count);
char *translate(char *world);
char *translateSentence(char ***words, int count);
int main(void){
/* Local definition*/
char sentence[inputSize];
int wordsCnt;
char ***head;
char *result;
getSentence(sentence, inputSize);
head = parseSentence(sentence, &wordsCnt);
result = translateSentence(head, wordsCnt);
printf("\nFinish the translation: \n");
printf("%s", result);
return 0;
}
void getSentence(char sentence [81], int size){
char *input = (char *)malloc(size);
int length;
printf("Input the sentence to big latin : ");
fflush(stdout);
fgets(input, size, stdin);
// do not copy the return character at inedx of length - 1
// add back delimater
length = strlen(input);
strncpy(sentence, input, length-1);
sentence[length-1]='\0';
free(input);
}
int countWord(char sentence[]){
int count=0;
/*Copy string for counting */
int length = strlen(sentence);
char *temp = (char *)malloc(length+1);
strcpy(temp, sentence);
/* Counting */
char *pToken = strtok(temp, " ");
char *last = NULL;
assert(pToken == temp);
while (pToken){
count++;
pToken = strtok(NULL, " ");
}
free(temp);
return count;
}
char ***parseSentence(char sentence[], int *count){
// parse the sentence into string tokens
// save string tokens as a array
// and assign the first one element to the head
char *pToken;
char ***words;
char *pW;
int noWords = countWord(sentence);
*count = noWords;
/* Initiaze array */
int i;
words = (char ***)calloc(noWords+1, sizeof(char **));
for (i = 0; i< noWords; i++){
words[i] = (char **)malloc(sizeof(char *));
}
/* Parse string */
// first element
pToken = strtok(sentence, " ");
if (pToken){
pW = (char *)malloc(strlen(pToken)+1);
strcpy(pW, pToken);
**words = pW;
/***words = pToken;*/
// other elements
for (i=1; i<noWords; i++){
pToken = strtok(NULL, " ");
pW = (char *)malloc(strlen(pToken)+1);
strcpy(pW, pToken);
**(words + i) = pW;
/***(words + i) = pToken;*/
}
}
/* Loop control */
words[noWords] = NULL;
return words;
}
/* Translate a world into big latin */
char *translate(char *word){
int length = strlen(word);
char *bigLatin = (char *)malloc(length+3);
/* translate the word into pig latin */
static char *vowel = "AEIOUaeiou";
char *matchLetter;
matchLetter = strchr(vowel, *word);
// consonant
if (matchLetter == NULL){
// copy the letter except the head
// length = lenght of string without delimiter
// cat the head and add ay
// this will copy the delimater,
strncpy(bigLatin, word+1, length);
strncat(bigLatin, word, 1);
strcat(bigLatin, "ay");
}
// vowel
else {
// just append "ay"
strcpy(bigLatin, word);
strcat(bigLatin, "ay");
}
return bigLatin;
}
char *translateSentence(char ***words, int count){
char *bigLatinSentence;
int length = 0;
char *bigLatinWord;
/* calculate the sum of the length of the words */
char ***walker = words;
while (*walker){
length += strlen(**walker);
walker++;
}
/* allocate space for return string */
// one space between 2 words
// numbers of space required =
// length of words
// + (no. of words * of a spaces (1) -1 )
// + delimater
// + (no. of words * ay (2) )
int lengthOfResult = length + count + (count * 2);
bigLatinSentence = (char *)malloc(lengthOfResult);
// trick to initialize the first memory
strcpy(bigLatinSentence, "");
/* Translate each word */
int i;
char *w;
for (i=0; i<count; i++){
w = translate(**(words + i));
strcat(bigLatinSentence, w);
strcat(bigLatinSentence, " ");
assert(w != **(words + i));
free(w);
}
/* free memory of big latin words */
walker = words;
while (walker != NULL && *walker != NULL){
free(**walker);
free(*walker);
free(walker);
walker++;
}
return bigLatinSentence;
}
Your code is unnecessarily complicated, because you have set things up such that:
n: the number of words
words: points to allocated memory that can hold n+1 char ** values in sequence
words[i] (0 <= i && i < n): points to allocated memory that can hold one char * in sequence
words[n]: NULL
words[i][0]: points to allocated memory for a word (as before, 0 <= i < n)
Since each words[i] points to stuff-in-sequence, there is a words[i][j] for some valid integer j ... but the allowed value for j is always 0, as there is only one char * malloc()ed there. So you could eliminate this level of indirection entirely, and just have char **words.
That's not the problem, though. The freeing loop starts with walker identical to words, so it first attempts to free words[0][0] (which is fine and works), then attempts to free words[0] (which is fine and works), then attempts to free words (which is fine and works but means you can no longer access any other words[i] for any value of i—i.e., a "storage leak"). Then it increments walker, making it more or less equivalent to &words[1]; but words has already been free()d.
Instead of using walker here, I'd use a loop with some integer i:
for (i = 0; words[i] != NULL; i++) {
free(words[i][0]);
free(words[i]);
}
free(words);
I'd also recommending removing all the casts on malloc() and calloc() return values. If you get compiler warnings after doing this, they usually mean one of two things:
you've forgotten to #include <stdlib.h>, or
you're invoking a C++ compiler on your C code.
The latter sometimes works but is a recipe for misery: good C code is bad C++ code and good C++ code is not C code. :-)
Edit: PS: I missed the off-by-one lengthOfResult that #David RF caught.
int lengthOfResult = length + count + (count * 2);
must be
int lengthOfResult = length + count + (count * 2) + 1; /* + 1 for final '\0' */
while (walker != NULL && *walker != NULL){
free(**walker);
free(*walker);
/* free(walker); Don't do this, you still need walker */
walker++;
}
free(words); /* Now */
And you have a leak:
int main(void)
{
...
free(result); /* You have to free the return of translateSentence() */
return 0;
}
In this code:
while (walker != NULL && *walker != NULL){
free(**walker);
free(*walker);
free(walker);
walker++;
}
You need to check that **walker is not NULL before freeing it.
Also - when you compute the length of memory you need to return the string, you are one byte short because you copy each word PLUS A SPACE (including a space after the last word) PLUS THE TERMINATING \0. In other words, when you copy your result into the bigLatinSentence, you will overwrite some memory that isn't yours. Sometimes you get away with that, and sometimes you don't...
Wow, so I was intrigued by this, and it took me a while to figure out.
Now that I figured it out, I feel dumb.
What I noticed from running under gdb is that the thing failed on the second run through the loop on the line
free(walker);
Now why would that be so. This is where I feel dumb for not seeing it right away. When you run that line, the first time, the whole array of char*** pointers at words (aka walker on the first run through) on the second run through, when your run that line, you're trying to free already freed memory.
So it should be:
while (walker != NULL && *walker != NULL){
free(**walker);
free(*walker);
walker++;
}
free(words);
Edit:
I also want to note that you don't have to cast from void * in C.
So when you call malloc, you don't need the (char *) in there.

How to assign char * to character array?

I have following code:
int main(){
char sentence[] = "my name is john";
int i=0;
char ch[50];
for (char* word = strtok(sentence," "); word != NULL; word = strtok(NULL, " "))
{
// put word into array
// *ch=word;
ch[i]=word;
printf("%s \n",ch[i]);
i++;
//Above commeted part does not work, how to put word into character array ch
}
return 0;
}
I am getting error: error: invalid conversion from ‘char*’ to ‘char’ [-fpermissive]
I want to store each word into array, can someone help?
To store a whole set of words you need an array of words, or at least an array of pointers pointing to a word each.
The OP's ch is an array of characters and not an array of pointers to characters.
A possible approach would be:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define WORDS_MAX (50)
int main(void)
{
int result = EXIT_SUCCESS;
char sentence[] = "my name is john";
char * ch[WORDS_MAX] = {0}; /* This stores references to 50 words. */
char * word = strtok(sentence, " "); /* Using the while construct,
keeps the program from running
into undefined behaviour (most
probably crashing) in case the
first call to strtok() would
return NULL. */
size_t i = 0;
while ((NULL != word) && (WORDS_MAX > i))
{
ch[i] = strdup(word); /* Creates a copy of the word found and stores
it's address in ch[i]. This copy should
be free()ed if not used any more. */
if (NULL == ch[i])
{
perror("strdup() failed");
result = EXIT_FAILURE;
break;
}
printf("%s\n", ch[i]);
i++;
word = strtok(NULL, " ")
}
return result;
}

Resources