Storing string input into different arrays in C - c

Hello this brand new noob on C is trying to take a string input of 3 words and store it on 3 different arrays, not 2D nor 3D arrays. For this problem I'm not allow to use any of the string library functions. Basically I'm trying to implement the sscanf function. I created a function that breaks the input into the three words, and stores them in their indicated array. My problem is when I try to print the each of the arrays, for my second array I can't get it to print the word I tried to store in it. I'm probably not storing anything, but I can't find my mistake. Here it's what I have...
#include<stdio.h>
#include<string.h>
void breakUp(char *, char *, char *, char *, int* );
int main()
{
char line[80], comm[10], p1[10], p2[10];
int len, n=0;
printf("Please Enter a command: ");
fgets(line, 80, stdin);
/* get rid of trailing newline character */
len = strlen(line) - 1;
if (line[len] == '\n')
line[len] = '\0';
/* Break up the line */
breakUp(line, comm, p1, p2, &n);
printf ("%d things on this line\n", n);
printf ("command: %s\n", comm);
printf ("parameter 1: %s\n", p1);
printf ("parameter 2: %s\n", p2);
return 0;
}
/*
This function takes a line and breaks it into words.
The orginal line is in the char array str, the first word
will go into the char array c, the second into p1, and the
the third into p2. If there are no words, the corresponding
char arrays are empty. At the end, n contains the number of
words read.
*/
void breakUp(char *str, char *c, char *p1, char *p2, int* n)
{
c[0] = p1[0] = p2[0] = '\0';
p1[0] = '\0';
int j = 0; // str array index
int i = 0; // index of rest of the arrays
n[0] = 0;
// stores first word in array c
while(str[j]!= ' '|| str[j] == '\0')
{
c[i]= str[j];
i++;
j++;
}
// increases n count, moves j into next element
// and sets i back to index 0
if (str[j] == ' '|| str[j] == '\0')
{
c[i] = '\0';
n[0]++;
j++;
i =0;
if( str[j] == '\0')
return;
}
// stores second word in array p1
while(str[j]!= ' '|| str[j] == '\0')
{
p1[i]= str[j];
i++;
j++;
}
// increases n count, moves j into next element
// and sets i back to index 0
if (str[j] == ' '|| str[j] == '\0')
{
p1[i] = '\0';
n[0]++;
j++;
i =0;
if( str[j] == '\0')
return;
}
// stores 3rd word in array p2
while(str[j] != ' ' || str[j] == '\0')
{
p2[i] = str[j];
i++;
j++;
}
// increases n count, moves j into next element
// and sets i back to index 0
if(str[j] == ' ' || str[j] == '\0')
{
p2[i] = '\0';
n[0]++;
if( str[j] == '\0')
return;
}
}
Advanced thanks if any help is provided

while(str[j]!= ' '|| str[j] == '\0')
should be
while(str[j]!= ' '&& str[j]!= '\0')
Same for if statements that check if end of the line was reached. Thanks.

In addition to the comments and answers, there are a few areas where you may be making things harder on yourself than they need to be. First lets look at the return of breakup. Why are you using void? If you need to be able to gauge success/failure of a function, pick a meaningful return type that will provide that information. Here a simple return of int will do (and it also prevents having to pass a pointer to n to update -- you can simply return n) You are free to pass a pointer to n, just note, it cannot be used to provide success/failure information at the time of the call to breakup. A return of n can.
(note: breakup is all lowercase -- leave camelCase variables to java and C++, C traditionally uses all lowercase variable and function names reserving all uppercase for constants and macros)
Second, avoid the use of magic numbers in your code (e.g. 80, 10, etc..). If you need constants, then #define them at the top, or use an enum to define global constants. That way you have a single convenient location at the top of your file to make adjustments and you don't have to go picking through each character array declaration changing magic numbers if you need to change the array sizes, etc. For example, to define the number of words, the command array lengths and the line buffer length, you can simple do:
#define NWRD 3 /* number of words */
#define CLEN 16 /* command length */
#define LLEN 80 /* line buffer len */
or using an enum:
enum { NWRD = 3, CLEN = 16, LLEN = 80 }; /* constants */
Now for the content of breakup, you are free to duplicate the code blocks for reading each word 3 separate times, but you can get a little smarter about it and realize you are already passing pointers to 3 character arrays to breakup, why not just use an array of pointers initialized to { c, p1, p2 } and then just write a single block of code to pick out each word from str? It's just a shorter way of skinning-the-cat so to speak. That way, instead of having to reference c, p1, p2, you can simply loop over arr[0], arr[1], arr[2], where arr[0] = c, arr[1] = p1, arr[2] = p2;.
For example, with your new declaration for breakup being:
int breakup (char *str, char *c, char *p1, char *p2);
within breakup you could assign the array of pointers as follows:
char *arr[] = { c, p1, p2 };
Then you can simply add all characters in the first word to arr[0], all chars in the second to arr[1], and so on which will in-turn fill c, p1, p2. That looks like something that could be handled in a nice single loop...
All you need do now is figure out a way of adding each character to each word, checking for spaces (or multiple spaces and tabs), making sure you nul-terminate each word, make sure you only fill three words -- and no more, and finally return the number of words you filled.
Without belaboring each point, you could shorten breakup to something like the following:
int breakup (char *str, char *c, char *p1, char *p2)
{
char *arr[] = { c, p1, p2 }; /* assign c, p1, p2 to arr */
int cidx = 0, n = 0; /* character index & n */
for (; n < NWRD; str++) { /* loop each char while n < NWRD */
if (*str == ' ' || *str == '\t') { /* have space or tab? */
if (*arr[n]) { /* have chars in arr[n]? */
arr[n++][cidx] = 0; /* nul-terminate arr[n] */
cidx = 0; /* zero char index */
}
}
else if (*str == '\n' || !*str) { /* reached '\n' or end of str? */
arr[n++][cidx] = 0; /* nul-terminate arr[n] */
break; /* bail */
}
else
arr[n][cidx++] = *str; /* assign char to arr[n] */
}
return n;
}
note: breakup tests the first character in each word to determine if you have started filling the word (to allow you to skip over multiple spaces or tabs), so you either need to insure you initialize all strings in main, or you could do it at the top of breakup as well. In main, you can simply initialize your character arrays when you declare them, e.g.
char line[LLEN] = "", comm[CLEN] = "", p1[CLEN] = "", p2[CLEN] = "";
(note: it is good practice to initialize all variables when you declare them).
You may want to also add a few additional validations when you are reading line to insure it isn't empty before passing it to breakup. You can do that by simply adding validations at the time line is read by fgets, example:
printf ("please enter a command: "); /* prompt & get line */
if (!fgets (line, LLEN, stdin) || !*line || *line == '\n') {
fprintf (stderr, "error: input empty or user canceled.\n");
return 1;
}
which checks that the first character in line is not the nul-character (empty-string) or that the first character is '\n' (empty-line).
Putting it altogether in a short example, you could do something like the following:
#include <stdio.h>
enum { NWRD = 3, CLEN = 16, LLEN = 80 }; /* constants */
int breakup (char *str, char *c, char *p1, char *p2)
{
char *arr[] = { c, p1, p2 }; /* assign c, p1, p2 to arr */
int cidx = 0, n = 0; /* character index & n */
for (; n < NWRD; str++) { /* loop each char while n < NWRD */
if (*str == ' ' || *str == '\t') { /* have space or tab? */
if (*arr[n]) { /* have chars in arr[n]? */
arr[n++][cidx] = 0; /* nul-terminate arr[n] */
cidx = 0; /* zero char index */
}
}
else if (*str == '\n' || !*str) { /* reached '\n' or end of str? */
arr[n++][cidx] = 0; /* nul-terminate arr[n] */
break; /* bail */
}
else
arr[n][cidx++] = *str; /* assign char to arr[n] */
}
return n;
}
int main (void) {
char line[LLEN] = "", comm[CLEN] = "", p1[CLEN] = "", p2[CLEN] = "";
int n = 0;
printf ("please enter a command: "); /* prompt & get line */
if (!fgets (line, LLEN, stdin) || !*line || *line == '\n') {
fprintf (stderr, "error: input empty or user canceled.\n");
return 1;
}
if ((n = breakup (line, comm, p1, p2)) != NWRD) { /* call breakup */
fprintf (stderr, "error: %d words found\n", n);
return 1;
}
printf ("command : %s\nparameter 1: %s\nparameter 2: %s\n",
comm, p1, p2);
return 0;
}
Example Use/Output
$ ./bin/breakup
please enter a command: dogs have fleas
command : dogs
parameter 1: have
parameter 2: fleas
$ ./bin/breakup
please enter a command: dogs have
error: 2 words found
$ ./bin/breakup
please enter a command: dogs have lots of fleas
command : dogs
parameter 1: have
parameter 2: lots
$ ./bin/breakup
please enter a command: dogs have fleas for sale
command : dogs
parameter 1: have
parameter 2: fleas
Look things over and make sure you understand what is happening. Being new. the array of pointers char *arr[] = { c, p1, p2 }; may have you scratching your head, but it is doing nothing more than creating 3 pointers arr[0], arr[1], arr[2] and assigning arr[0] = c; arr[1] = p1, arr[2] = p2; (just as you would use any pointer, say char *p = c, only you are using arr[0] instead of p to allow yourself to loop over each pointer.
Beyond that it is all fairly basic. But the time in now to ask questions to make sure you understand it going forward.

Related

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

Removing array of occurrences from string in C

I'm having looping issues with my code. I have a method that takes in two char arrays (phrase, characters). The characters array holds characters that must be read individually and compared to the phrase. If it matches, every occurrence of the character will be removed from the phrase.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
//This method has two parameters: (str, c)
//It will remove all occurences of var 'c'
//inside of 'str'
char * rmstr(char * c, char * str) {
//Declare counters and pointers
int stemp = 0;
int ctemp = 0;
char *p = str;
char *d = c;
//Retrieve str count
while(str[stemp] != '\0') {
stemp++;
}
//Retrieve c count
while(c[ctemp] != '\0') {
ctemp++;
}
//Output information
printf("String Count: %d\n",stemp);
printf("Character Count: %d\n",ctemp);
//Iterate through arrays
for (int i = 0; i != stemp; i++) {
for (int j = 0; j != ctemp; j++) {
if (c[j] != str[i]){
*p++ = str[i];
}
break;
}
printf("%s\n",str);
}
*p = 0;
return str;
}
int main()
{
char c[256] = "ema";
char input[256] = "Great message!";
char *result = rmstr(c, input);
printf("%s", result);
return 0;
}
In this case, the input would be "Great Message!" and the character I'd like to remove all occurrences of the characters: e, m, a (As specified in main).
Using the code as it is above, the output is as follows:
Grat mssag!
It is only looping through 1 iteration and removing 'e'. I would like it to loop through 'm' and 'a' as well.
After you fix your break; that was causing your inner loop to exit, it may make sense to reorder your loops and loop over the chars to remove while checking against the characters in str. This is more of a convenience allowing you to shuffle each character down by one in str if it matches a character is c. If you are using the functions in string.h like memmove to move characters down, it doesn't really matter.
A simple implementation using only pointers to manually work through str removing all chars in c could look something like the following:
#include <stdio.h>
char *rmstr (char *str, const char *chars)
{
const char *c = chars; /* set pointer to beginning of chars */
while (*c) { /* loop over all chars with c */
char *p = str; /* set pointer to str */
while (*p) { /* loop over each char in str */
if (*p == *c) { /* if char in str should be removed */
char *sp = p, /* set start pointer at p */
*ep = p + 1; /* set end pointer at p + 1 */
do
*sp++ = *ep; /* copy end to start to end of str */
while (*ep++); /* (nul-char copied on last iteration) */
}
p++; /* advance to next char in str */
}
c++; /* advance to next char in chars */
}
return str; /* return modified str */
}
int main (void) {
char c[] = "ema";
char input[] = "Great message!";
printf ("original: %s\n", input);
printf ("modified: %s\n", rmstr (input, c));
return 0;
}
(there are many ways to do this -- how is largely up to you. whether you use pointers as above, or get the lengths and use string-indexes is also a matter of choice)
Example Use/Output
$ ./bin/rmcharsinstr
original: Great message!
modified: Grt ssg!
If you did want to use memmove (to address the overlapping nature of the source and destination) to move the remaining characters in str down by one each time the character in str matches a character in c, you could leave the loops in your original order, e.g.
#include <string.h>
char *rmstr (char *str, const char *chars)
{
char *p = str; /* set pointer to str */
while (*p) { /* loop over each char in str */
const char *c = chars; /* set pointer to beginning of chars */
while (*c) { /* loop over all chars with c */
while (*c == *p) { /* while the character matches */
memmove (p, p + 1, strlen (p)); /* shuffle down by 1 */
c = chars; /* reset c = chars to check next */
}
c++; /* advance to next char in chars */
}
p++; /* advance to next char in str */
}
return str; /* return modified str */
}
(make sure you understand why you must reset c = chars; in this case)
Finally, if you really wanted the shorthand way of doing it, you could use strpbrk and memmove and reduce your function to:
#include <string.h>
char *rmstr (char *str, const char *chars)
{
/* simply loop using strpbrk removing the character found */
for (char *p = strpbrk (str, chars); p; p = strpbrk (str, chars))
memmove (p, p+1, strlen(p));
return str; /* return modified str */
}
(there is always more than one way to skin-the-cat in C)
The output is the same. Look things over here and let me know if you have further questions.

C program strings

For part of my program I would like to concatenate two strings together with an asterisk between each character. For example, if I had a first string of "abcde" and a second string of "1234567", I would like to have a result of "a*b*c*d*e*1*2*3*4*5*6*7*".
For this part of the program I have:
char *widen_stars(char *one, char *two)
{
int length_one = strlength(one); // length of first parameter
int length_two = strlength(two); // Length of second parameter
char *p = malloc((sizeof(char) * (length_one + length_two) * 2)+ 1), *p_start; //Allocate enough memory for both strings concatenated together with a * between each letter
p_start = p;
while(*one != '0')
{
if( (p - p_start) % 2 == 0) // Keeps track of where we are in the p string
{
*p = *one;
p++;
one++;
}
else
{
*p = '*';
p++;
}
}
while(*two != '0')
{
if( (p - p_start) % 2 == 0)
{
*p = *two;
p++;
two++;
}
else
{
*p = '*';
p++;
}
}
return p_start;
}
int main(int argc, char *argv[])
{
char first[31]= {0};
char second[31]= {0};
char *f = first, *s = second;
printf("Please enter a string of maximum 30 characters: ");
scanf("%s", f);
printf("Please enter a string of maximum 30 characters: ");
scanf("%s", s);
printf("The combined string is: %s\n", widen_stars(f, s));
}
return 0;
}
However, when I run the program with the above inputs, I get something like "a*b*c*d*e*", without any of the second string. If I block out the first while loop into comments to test the second loop, I get something like "1*2*3*4*5*5*7*", which leaves me scratching my head.
Your problem lies here:
while(*oneOrTwo != '0')
If you're looking for the end of the strings, it's '\0' that you should be looking for, not '0'. The former is the end-of-string marker, the latter is simply the character 0.
And, as an aside, there are much less ..., err, verbose ways to do this (assuming it's not class work - if it is, you should go with your current method). For example:
#include <stdio.h>
#include <string.h>
char *widen_stars(char *one, char *two) {
// Need to cater for memory exhaustion.
char *p = malloc((strlen(one) + strlen(two)) * 2) + 1);
if (p == NULL) return NULL;
// Init to empty string in case both inputs empty.
*p = '\0';
// Save string start for return.
char *p_start = p;
// Add character and asterisk for every character in both strings.
while (*one != '\0') {
sprintf(p, "%c*", *one++);
p += 2;
}
while (*two != '\0') {
sprintf(p, "%c*", *two++);
p += 2;
}
// Remove last asterisk (if needed).
// *(--p) = '\0';
// Return result.
return p_start;
}
That's based on your actual expected results, which place an asterisk after each character. However, your specifications call for an asterisk between each character. If you decide to got for the latter, it's a simple matter of un-commenting the penultimate statement in the function, to basically back up and replace the final asterisk with an end-of-string marker.
The problem in your code is in while conditions, you should increment pointers until '\0' not '0'. So instead of doing:
while(*one != '0')
...
while(*two != '0')
You do it like this:
while(*one != '\0')
...
while(*two != '\0')
And as you are returning a dynamically allocated memory, consider using a pointer to this memory, which you free after usage:
char *str = widen_stars(f, s);
printf("The combined string is: %s\n", str);
free(str);

Reverse string in malloc

I need define a “word” in this question to be any sequence of characters that doesn’t contain a space or null character.
For example, the string “Hello World” would contain 2 words. However, it is actually possible for a word to be empty
i.e., zero chars.
A sentence would be a series of words that are separated by 1 space character. So “Hello World” would be a
sentence of two words. The goal of ReverseSentence would be to reverse the sentence in terms of word.
Right now, I am having an error whereby programs proceed to call function and prints out a1 to a5. Upon reaching a5, it seems that program abort and core dumped. If i replace blank with space, it will read in the previous input and replace according to number of space.
Where am i going wrong?
ReverseSentence.c
#include <stdlib.h> /* malloc */
#include <string.h> /* strcat, strcpy */
void ReverseSentence(char *str)
{
char *newSentence;
int i, j, start, len;
/* contains the string length of the input */
len = strlen(str);
/* position or index in the array */
start = strlen(str);
/* malloc */
newSentence = malloc(len + 1);
/* loop checks from the right of the sentences */
for (i = len; i >= 0; i--) {
/* if index reach the array with a space or zero */
if (str[i] == ' ' || i == 0) {
/* allocates memory */
char *word = malloc((start - i) + 1);
int c = 0;
if (i == 0)
/* index remains same */
j = i;
else
j = i + 1;
/* j smaller or equal than the start position */
for (; j <= start; j++) {
/*do a incremental*/
word[c++] = str[j];
}
/* hits a null char */
word[c] = '\0';
/* string concatenate */
strcat(newSentence, word);
/* if index hits a space */
if (str[i] == ' ')
strcat(newSentence, " "); /* concatenate space to newSentence */
else
strcat(newSentence, "\0");
start = i - 1;
/* free memory */
free(word);
}
}
newSentence[len] = '\0';
/* string copy */
/* str is destination, newSentence is the source */
/* copy new string to original string */
strcpy(str, newSentence);
/* free memory */
free(newSentence);
}
main.c
#include <stdio.h>
#include "ReverseSentence.h"
int main()
{
char a1[] = "Hello World ";
char a2[] = "abcdefghi ";
char a3[] = " ";
char a4[] = "C programming is a dangerous activity";
char a5[] = "a "; /* a sentence with only empty words */
ReverseSentence(a1);
printf("Test case 1:\"%s\"\n", a1); /* prints "World Hello" */
ReverseSentence(a2);
printf("Test case 2:\"%s\"\n", a2); /* prints "abcdefghi" */
ReverseSentence(a3);
printf("Test case 3:\"%s\"\n", a3); /* prints "" */
ReverseSentence(a4);
printf("Test case 4:\"%s\"\n", a4); /* prints "activity dangerous a is pro Cgramming" */
ReverseSentence(a5);
printf("Test case 5:\"%s\"\n", a5); /* prints " " */
return 0;
}
EDIT: new version
void ReverseSentence(char *str)
{
/* holder */
/* pointer to char */
char *newSentence;
int i, start, len, lastindex, size;
/* contains the string length of the input */
len = strlen(str);
lastindex = strlen(str);
/* starting position */
start = 0;
i = 0;
/* malloc */
newSentence = malloc(sizeof(char) * strlen(str));
while (i >= 0) {
for (i = len - 1; str[i] != '\0' && str[i] != ' '; i--) {
lastindex--;
}
/* number of chars in string size */
size = len - lastindex;
/* Copy word into newStr at startMarker */
strncpy(&newSentence[start], &str[lastindex], size);
/* pointer move to right */
start = start + size;
/* Space placed into memory slot */
newSentence[start] = ' ';
/* start position moves by 1 towards the right */
start = start + 1;
/* pointer at len moves to left */
lastindex = lastindex - 1;
/* lastindex moves to where len is */
len = lastindex;
}
/* Copy new string into old string */
for (i = 0; str[i] != '\0'; i++) {
str[i] = newSentence[i];
}
/* free memory */
free(newSentence);
}
In addition to Matthias' answer: you don't allocate enough memory, I just did a wild guess and added 1 to the arguments passed to malloc.
newSentence = malloc(len + 2); // +2 instead of +1
and
char *word = malloc((start - i) + 2); // +2 instead of +1
And now it doesn't crash anymore. So there is definitely a buffer overflow here.
I don't pretend the program is perfectly correct now. You should have a look into this.
Your code is not safe. You never initialize newSentence, since malloc() only allocates but not initialize the memory (in contrast to calloc()). Thus, you are starting with a garbage sentence, where you append something new (strcat()). Depending on the garbage, there may be no 0 even in the allocated memory, and you access some unallocated memory area.
Your method is too complicated. It has several issues:
you do not initialize newSentence: since malloc memory is uninitialized, you invoke undefined behavior when you copy the words at its end with strcat. You can fix that with *newSentence = '\0';
when you copy the word into the allocated word buffer, you iterate upto and including start, then you add a '\0' at the end. You effectively write one byte too many for the last word (case i == 0). This invokes undefined behavior.
strcat(newSentence, "\0"); does nothing.
allocating a buffer for each word found is wasteful, you could just copy the word with memcpy or with a simple for loop.
You could simplify with the following steps:
allocate a buffer and copy the string to it.
for each word in the string, copy it at the end of the destination and copy the separator before it if not at the end.
free the buffer.
Here is the code:
char *ReverseSentence(char *str) {
int len = strlen(tmp); /* length of the string */
char *tmp = strdup(str); /* copy of the string */
int i; /* index into the copy */
int j = len; /* index into the string */
int n; /* length of a word */
for (i = 0; i < len; ) {
n = strcspn(tmp + i, " "); /* n is the length of the word */
j -= n; /* adjust destination offset */
memcpy(str + j, tmp + i, n); /* copy the word */
i += n; /* skip the word */
if (tmp[i] != '\0') { /* unless we are at the end */
j--;
str[j] = tmp[i]; /* copy the separator */
i++;
}
}
free(tmp); /* free the copy */
return str;
}
There are at least two issues in the new version of your program:
You don't allocate enough memory, you don't account for the zero terminator. You should allocate one more byte.
in your first for loop you allow i to become -1. The loop should stop when i is zero: modify your for statement like this: for(i=len-1; tr[i] != ' ' && i >= 0; i--). You incorrecty assume that the first byte before the str buffer is zero, therefore the str[i]!='\0' is wrong. BTW accessing one byte before the str buffer yields in undefined behaviour.
There are probably other issues.

"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.

Resources