This small piece of code takes a user input and returns the last word from the user input.
I don't exactly understand how those 2 lines of code work
can someone explain why or how printf( &last_word[i]); when I is at position 0 prints the last word of the sentence entered by the user input.
the purpouse of the program is to print the last word of the user input, but I dont understand those lines of code, If I change something on those 2 lines, the code stops working and it doesnt return the last word entered by the user.
#include <stdio.h>
int main(){
int i = 0;
char *last_word;
char str[800];
printf("enter:");
fgets(str,100,stdin);
while (str[i] != '\0') {
if (str[i] == ' ' && str[i + 1] > 32)
last_word = &str[i + 1];
i++;
}
/*------------------------these next lines of code-----------------------*/
/*-----> i = 0;
/*-----> printf( &last_word[i]);
return 0;
}
Holy undefined behavior batman.
The while loop stuffs the address of the next character past a space character into last_word; thus if there are any space characters it points one past the last one.
i = 0 therefore &last_word[i] is the same as last_word. So it uses the last word as a printf format string.
But that's not the same as last word. Try this input: " %n%n%n%n%n%n%n%n". Goodbye.
The first argument to printf should almost always be a constant. You want printf("%s", last_word); In fact, let us put it this way; the only reason to ever not pass a constant format string to printf and friends is your standard library is too old to support %*d for dynamic widths.
Related
I don't quite understand this program. I don't understand what is happening in the for loop. Can someone explain to me in simple words. And the site also didn't explain it well-enough. This is the link to the site. https://www.geeksforgeeks.org/print-words-string-reverse-order/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void printReverse(char str[])
{
int length = strlen(str);
FILE *fptr;
if((fptr=fopen("Question1.txt","w"))==NULL)
{
printf("Invalid file");
exit(0);
}
int i;
for (i = length - 1; i >= 0; i--) {
if (str[i] == ' ')
{
str[i] = '\0';
printf("%s ", &(str[i]) + 1);
fprintf(fptr,"%s ", &(str[i]) + 1);
}
}
fprintf(fptr,"%s",str);
printf("%s.", str);
fclose(fptr);
}
int main()
{
char str[1000];
//clrscr();
printf("Enter string: ");
scanf("%[^\n]s", str);
printReverse(str);
//getch();
return 0;
}
In the for loop, why put &(str[i])+1? And also in printf("%s.", str)--this only has the first word; how?
Okay, let's see if I can help. I'll go through the code carefully.
I suspect you already understand this. It's just a method call.
void printReverse(char str[])
{
strlen is a standard method that returns the length of a null-terminated string. That means that str might contain Hello (5 characters), but there's one more byte with a 0 in it, which is how C has always marked the end of the string. In this case, str itself takes 6 bytes, but length will be 5.
int length = strlen(str);
This is how you open a file in C. C++ has better ways. The file is written for writing.
FILE *fptr;
if((fptr=fopen("Question1.txt","w"))==NULL)
{
printf("Invalid file");
exit(0);
}
Here's your for-loop. Let's assume str contains Hello, so length is 5, but the indexes into string are str[0..4]. C uses the index as "offset from the beginning", so the first element is 0, not 1. Thus, when this loop starts, str[i] == o (using Hello as our example string). We then loop, decrementing i each time. Once i goes below 0, the loop ends.
int i;
for (i = length - 1; i >= 0; i--) {
Okay, remember we're printing the words in normal order, but the words themselves are in reverse order. So this looks for a space -- between words. So if we use Hello there as our input text, this if-statement is true when i is pointing to the space between the two words.
Now here's the trick. Remember what I said earlier about null-terminated strings? What this does is to step on that space and replace it with a 0. That makes the rest of this magic work.
if (str[i] == ' ')
{
str[i] = '\0';
And here's the magic. Now, this is a strange way to do it. I would have done it with &str[i+1], but this works. What this is doing is saying "Print the string that begins after the space we just clobbered." We do it to the terminal and the file.
printf("%s ", &(str[i]) + 1);
fprintf(fptr,"%s ", &(str[i]) + 1);
}
}
This writes the produced rearranged string to the file that was opened as well as to your terminal then makes sure the file is closed.
fprintf(fptr,"%s",str);
printf("%s.", str);
fclose(fptr);
}
This all works because we step on the spaces with a zero. For Hello world, we:
Start from the tail
Find the space and stick a zero in it
Print world
Keep backing up to the end of the data.
Drop out of the for-loop and print whatever is left: Hello
Answer to your specific questions
In the for loop why put? &(str[i])+1?
&str[i] is the address of the character at index i where a space has been replaced with a NUL character. With +1 you get the address of the character after it, i.e. the beginning of the word that follows the space that was just replaced. (In case of double spaces this would result in an empty string.)
And also in printf("%s.", str); this only has the first word how?
Assuming the first word is not preceded by a space, the loop will not print it.
This printf("%s.", str); will print the string from the beginning until the first NUL character that replaces a former space character, hence resulting in the first word.
Additional question from comment
So... for example if I input Hello World does the W in that get the index 0?
The W is at index 6. (H is 0, e is 1 etc.)
When i has been counted down to 5, the space at this position will be replaced with a NUL ('\0') character, and it will print the remaining string from the W up to the end of the string which is also marked by a NUL character. (As defined by the C standard.)
And what if the character is not a NULL character? Then it won't go execute if right? It'll just increment i again till it encounters another NULL right?
I don't fully understand these questions. In case there was no NUL character at the end of the string printf would read past the end of the string leading to undefined behavior.
In case of an input string Hello World and Universe", all spaces after Worldwould have been replaced with NUL characters before, so when the program reaches the position of the space beforeWorld`, the string will be
Hello World\0and\0Universe\0
before the replacement and
Hello\0World\0and\0Universe\0
after the replacement.
This is the question on my assignment: Write a program that prompts the user to enter a sentence (assume that a sentence can have a maximum of 50 characters). It then counts the vowels and consonants in it. It also calculates the average word length of the input sentence. Word length is the total number of alphabetic characters in the sentence divided by the total number of words in it. Words are separated by one or more spaces. All the results are displayed at the end.
So far I have been able to complete all aspects of the question but I am running into a logical error on my part. When the user inputs more than a normal amount of spaces, it messes up the answer given for average word length.
Here is my code calculating average word length:
for(i = 1; sent[i] != '\0'; i++){
if( sent[i] == ' '){
++spaceCount;
}
else if((sent[i] != ' ') && (sent[i] != '\n')){
++charCount;
}
}
avgWordLength = (charCount / (spaceCount+1)) ;
Could someone help explain the logic behind the structure of code needed to account for extra spaces, in order to calculate the correct average word length
Here is a link to a previously already answered question:
Average word length for a sentence
But my school has not taught the "getchar" function yet and I would not like to use it unless I have too. To be more clear, is there away to complete the question without using the "getchar" function?
Here is an example of the problem when compiling and running
// Everything works good when
string: Thursday is ok
Average word length: 4.00 characters
// this is where my code fall apart
string: Thursday is ok
Average word length: 1.86 characters
Well, if you think about it, what you want to do is just treat any uninterrupted series of whitepace characters as one for the purpose of computing the word count. You can include ctype.h and use the isspace function to test all possible whitespace characters, or if you are supposed to do it manually, then at least check for space or tab characters (e.g. you could have a mixed sequence of spaces and tabs that should still be counted as a single (e.g. " \t \t ")
To handle multiple whitespace characters and count the sequence as one, just set a flag (e.g. ws for whitespace) and only increment spaceCount when you encounter the first whitespace, and reset the flag if another non-whitespace character is encountered.
Putting those pieces together, you could do something like the following:
int ws = 0; /* flag to treat multiple whitespace as 1 */
for(i = 0; sent[i]; i++){
if (sent[i] == ' ' || sent[i] == '\t') {
if (!ws) {
spaceCount++;
ws = 1;
}
}
else {
charCount++; /* non-whitespace character count */
ws = 0;
}
}
(note: begin your check at i = 0 to protect against Undefined Behavior in the event sent is the empty-string.)
(note2: you can check charCount before setting your first spaceCount and check ws after leaving the loop to handle leading and trailing whitespace -- and adjust spaceCount as necessary. That is left as an exercise)
Look things over and let me know if you have any further questions.
Could someone help explain the logic behind the structure of code needed to account for extra spaces, in order to calculate the correct average word length
You could use a state machine. You have two states:
1) Looking for the end of a word.
2) Looking for the end of a space sequence.
Look at the first character in the sentence. It is either the beginning of a word or a space. This tells you if you are in state 1 or 2.
If in state 1, then look for a space or the end of the sentence. If you find a space, set your state to 2.
If in state 2, then look for a non-space or the end of the sentence. If you find a non-space then set your state to 1.
counts the vowels and consonants in it. It also calculates the average word length of the input sentence.
Could someone help explain the logic behind the structure of code needed to account for extra spaces
There really is no need to count spaces. Instead all that is needed to to count the number of times a letter begins a word - it followed a non-letter - or was first character.
// pseudo code
sentence_stats(const char *s) {
vowels = 0;
consonants = 0;
word_count = 0;
previous = 0;
while (*s) {
if (isletter(*s)) { // OP to make isletter(), isvowel()
if (!isletter(previous)) {
word_count++; // start of word
}
if (isvowel(*s)) vowels++;
else consonants++;
} else if (*s == ' ') {
; // nothing to do
} else {
TBD_CODE_Handle_non_letter_non_space();
}
previous = *s;
s++;
}
average = (vowels + consonants)/word_count
}
I am not able to find out the reason for the misbehavior of the below code. This is a simple code to accept characters until either * is entered or until array size is reached and then print the characters read from the keyboard. It looks like the reading part is fine. Also if I enter * before array size is reached everything is OK. But if I do not enter * and wait until array size is reached in reading portion, I have the trouble. While printing it prints the characters read, but after that some garbage is printed. Ran through debugger, but while loop is not breaking when index is 3 or more.
int main()
{
char myStr [3];
unsigned int index=0;
printf("Enter Single characters. Enter * to stop\n");
do
{
scanf(" %c",&myStr[index]);
index++;
} while ((myStr[index-1]!='*')&&((index)<(sizeof(myStr)/sizeof(myStr[0]))));
index=0;
while ((myStr[index]!='*')&&(index<(sizeof(myStr)/sizeof(myStr[0]))))
{
printf("%c",myStr[index]);
index++;
}
printf("\n");
return(0);
}
The code runs into undefined behaviour on the printf loop's last iteration here
while ((myStr[index]!='*')&&(index<(sizeof(myStr)/sizeof(myStr[0]))))
{
...
as it in fact is doing
while ((myStr[3] ....
with myStr[3] accessing myStr out-of-bounds.
To fix this do:
while ((index < (sizeof(myStr)/sizeof(myStr[0]))) && (myStr[index] != '*'))
Boolean short-circuiting will take care of myStr[3] not being executed.
You are manipulating strings, witch need to be null-terminated.
You should use fgets(3) to get your string and then strlen(3) to get the length.
Then you can move in your string by
while (str[i] != '*' && i < strlen(str))
good luck
This seems like it should be a simple thing but after hours of searching I've found nothing...
I've got a function that reads an input string from stdin and sanitizes it. The problem is that when I hit enter without typing anything in, it apparently just reads in some junk from the input buffer.
In the following examples, the prompt is "input?" and everything that occurs after it on the same line is what I type. The line following the prompt echoes what the function has read.
First, here is what happens when I type something in both times. In this case, the function works exactly as intended.
input? abcd
abcd
input? efgh
efgh
Second, here is what happens when I type something in the first time, but just hit enter the second time:
input? abcd
abcd
input?
cd
And here is what happens when I just hit enter both times:
input?
y
input?
y
It happens to return either 'y' or '#' every time when I run it anew. 'y' is particularly dangerous for obvious reasons.
Here is my code:
#include <stdio.h>
#include <stdlib.h>
#define STRLEN 128
int main() {
char str[STRLEN];
promptString("input?", str);
printf("%s\n", str);
promptString("input?", str);
printf("%s\n", str);
return EXIT_SUCCESS;
}
void promptString(const char* _prompt, char* _writeTo) {
printf("%s ", _prompt);
fgets(_writeTo, STRLEN, stdin);
cleanString(_writeTo);
return;
}
void cleanString(char* _str) {
char temp[STRLEN];
int i = 0;
int j = 0;
while (_str[i] < 32 || _str[i] > 126)
i++;
while (_str[i] > 31 && _str[i] < 127) {
temp[j] = _str[i];
i++;
j++;
}
i = 0;
while (i < j) {
_str[i] = temp[i];
i++;
}
_str[i] = '\0';
return;
}
I've tried various methods (even the unsafe ones) of flushing the input buffer (fseek, rewind, fflush). None of it has fixed this.
How can I detect an empty input so that I can re-prompt, instead of this annoying and potentially dangerous behavior?
This part of cleanString
while (_str[i] < 32 || _str[i] > 126)
i++;
jumps over \0 when the string is empty.
You should add _str[i] != '\0' into the loop's condition.
To detect an empty string, simply check it's length just after the input:
do {
printf("%s ", _prompt);
fgets(_writeTo, STRLEN, stdin);
} while (strlen(_writeTo) < 2);
(comparing with two because of '\n' which fgets puts into the end of buffer)
Why do you have a bunch of variable names with leading underscores? That's nasty.
Anyway, the first thing you must do is check the return value of fgets. If it returns NULL, you didn't get any input. (You can then test feof or ferror to find out why you didn't get input.)
Moving on to cleanString, you have a while loop that consumes a sequence of non-printable characters (and you could use isprint for that instead of magic numbers), followed by a while loop that consumes a sequence of printable characters. If the input string doesn't consist of a sequence of non-printables followed by a sequence of printables, you will either consume too much or not enough. Why not use a single loop?
while(str[i]) {
if(isprint(str[i]))
temp[j++] = str[i];
++i;
}
This is guaranteed to consume the whole string until the \0 terminator, and it can't keep going past the terminator, and it copies the "good" characters to temp. I assume that's what you wanted.
You don't even really need to use a temp buffer, you could just copy from str[i] to str[j], since j can never get ahead of i you'll never be overwriting anything that you haven't already processed.
I really hope someone can give a well explained example. I've been searching everywhere but can't find a proper solution.
I am taking an introduction to C Programming class, and our last assignment is to write a program which validates a 10 digit ISBN with dashes... The ISBN is inputted as a string in a CHAR array. From there I need to separate each digit and convert them into an integer, so I can calculated the validity of the ISBN. On top of that, the dashes need to be ignored..
My thought process was to create an INT array and then use a loop to store each character into the array, and pass it through the atoi() function. I also tried using an IF statement to check each part of the CHAR array to see if it found a dash. If it did find one, it would skip to the next spot in the array. It looked something like this:
int num[12], i = 0, j = 0, count = 0;
char isbn[12];
printf ("Enter an ISBN to validate: ");
scanf ("%13[0-9Xx-]%*c", &isbn);
do {
if (isbn[i] == '-') {
i++;
j++;
}
else {
num[i]= atoi(isbn[j]);
i++;
j++;
}
count++;
} while (count != 10);
But that creates a segmentation fault, so I can't even tell if my IF statement has actually filtered the dashes....
If someone could try and solve this I'd really appreciate that. The Assignment was due Dec 4th, however I got an extension until Dec 7th, so I'm pressed for time.
Please write out the code in your explanation. I'm a visual learner, and need to see step by step.
There's obviously a lot more that needs to be coded, but I can't move ahead until I get over this obstacle.
Thanks in advance!
First of all, your definition of isbn is not sufficient to hold 13 characters; it should therefore be 14 chars long (to also store the terminating '\0').
Second, your loop is overly complicated; three loop variables that maintain the same value is redundant.
Third, the loop is not safe, because a string might be as short as one character, but your code happily loops 10 times.
Lastly, converting a char that holds the ascii value of a digit can be converted by simply subtracting '0' from it.
This is the code after above improvements have been made.
#include <stdio.h>
int main(void)
{
int num[14], i;
char isbn[14], *p;
printf("Enter an ISBN to validate: ");
scanf("%13[0-9Xx-]%*c", &isbn);
// p iterates over each character of isbn
// *p evaluates the value of each character
// the loop stops when the end-of-string is reached, i.e. '\0'
for (p = isbn, i = 0; *p; ++p) {
if (*p == '-' || *p == 'X' || *p == 'x') {
continue;
}
// it's definitely a digit now
num[i++] = *p - '0';
}
// post: i holds number of digits in num
// post: num[x] is the digit value, for 0 <= x < i
return 0;
}