1

So I am trying to write a program that takes a sentence and prints it out from the third word. Ex one two three four should print out three four.

Now this code works but I have no idea why as the logic under the else statement make it seem like it should not.

Would be thankful if someone could explain why it works like this.

Here is the code:

#include <stdio.h>
#include <string.h>

#define SIZE 100

int main(void) {
    char arr[SIZE];
    char *point;
    char again = 'n';

    do {
        int count = 0;
        for (int i = 0; i < SIZE; i++) {
            arr[i] = '\0';
        }
        printf("Enter a sentence:");
        gets(arr);
        for (int i = 0; i < SIZE; i++) {
            if (arr[i] == ' ') {
                count++;
            }
        }
        if (count < 2) {
            printf("The sentence is to short!\n");
        } else {
            count = 1;  //shouldn't this be count = 0?
            for (int i = 0; i < SIZE; i++) {
                if (arr[i] == ' ') {
                    count++;
                }
                if (count == 2) {
                    point = &arr[i + 2]; //shouldn't this be [i+1]? 
                }
            }
            printf("%s\n", point);
        }
        printf("Do you want to try again? (y/n)");
        scanf("%c", &again);
        while (getchar() != '\n');
    } while (again == 'y' || again == 'Y');

    return 0;
}
chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • 2
    Have you tried strtok? – Andreas DM Oct 12 '18 at 20:32
  • 3
    [don't use gets](https://stackoverflow.com/q/1694036/5522303) – Kevin Oct 12 '18 at 20:33
  • So the question is why the code works? – Weather Vane Oct 12 '18 at 20:34
  • 1
    you wrote the code, ask yourself why it seems to work. If in doubt step through with your debugger, watch all the variables – pm100 Oct 12 '18 at 20:36
  • As a small point, if the user wants to repeat the loop more than once, `scanf("%c", &again);` should be `scanf(" %c", &again);` note the added space and [scanf() leaves the newline char in the buffer](https://stackoverflow.com/questions/5240789/scanf-leaves-the-new-line-char-in-the-buffer). – Weather Vane Oct 12 '18 at 20:37
  • The way you're zeroing out the buffer is really inefficient. Consider using [`memset`](https://en.cppreference.com/w/c/string/byte/memset) if you need to do that. This isn't really an issue as the first NUL byte is what counts, so you can zero the first byte and it's now a usable string buffer. The rest are just redundant. That doesn't even matter since you're calling `gets` to populate that buffer, and `gets` doesn't care if it's initialized or not since it replaces the contents. – tadman Oct 12 '18 at 20:40
  • The variable `point` is being re-assigned every time `count==2` inside your loop. The last time it is assigned is when the value `i` indexes the letter previous to the second space in the sentence. At this location, `array[i+2]` indexes the first letter of the third word. – MFisherKDX Oct 12 '18 at 20:54

3 Answers3

1

Your code has multiple problems:

  • You should never use gets(). This function has been removed from the C Standard because it cannot be given the maximum number of characters to write to the destination buffer, so any sufficiently long line from the input stream will cause undefined behavior. This is a classic security flaw. Use fgets() instead.
  • The loop while (getchar() != '\n'); will cause an infinite loop if there is no newline before the end of file, which will happen if you redirect input an empty file. You should also check for EOF:

    while ((c = getchar()) != EOF && c != '\n')
        continue;
    
  • There is no need to initialize the destination array, but you should check if the input operation succeeded, by comparing the return value of fgets() to NULL.
  • When iterating through the array to count spaces, you should stop at the null terminator. The contents of the array beyond the null terminator is indeterminate after the input operation, even if you initialized it prior to the call.
  • The code to skip the words is cumbersome and not easy to validate. Indeed point = &arr[i+2]; should be point = &arr[i+1].
  • words might be separated by more than one space, and initial spaces should be ignored.

Here is a corrected version using string functions strspn and strcspn to skip blanks and non-blanks:

#include <stdio.h>
#include <string.h>

#define SIZE  100
#define WS  " \t\n\r\v\f"  /* white space characters */

int main(void) {
    char arr[SIZE];
    char *p;

    for (;;) {
        printf("Enter a sentence:");
        if (fgets(arr, sizeof arr, stdin) == NULL)
            break;
        p = arr;
        p += strspn(p, WS);     /* skip initial spaces */
        p += strcspn(p, WS);    /* skip first word */
        p += strspn(p, WS);     /* skip spaces */
        p += strcspn(p, WS);    /* skip second word */
        p += strspn(p, WS);     /* skip spaces */

        if (*p == '\0') {
            printf("The sentence is too short!\n");
        } else {
            printf("%s", p);
        }
        printf("Do you want to try again? (y/n)");
        if (fgets(arr, sizeof arr, stdin) == NULL)
            break;
        if (*arr != 'y' && *arr != 'Y')
            break;
    }
    return 0;
}
chqrlie
  • 131,814
  • 10
  • 121
  • 189
1

Another simple way to handle the word count is to walk-a-pointer down your string in a state loop keeping track of whether you are in a word (if so increase word count), otherwise you are not in a word and just keep walking down the buffer (i.e. iterating over each char) until you find the next word (or end of string).

The logic is simple, after filling your buffer, and setting a pointer to it, e.g.

#define MAXC  1024  /* buffer size (don't skimp) */
#define NWORD    3  /* output beginning with NWORD word */
...    
        char buf[MAXC] = "",    /* buffer to hold line */
            *p = buf;           /* pointer to walk down buffer */
        int n = 0,              /* word counter */
            in = 0;             /* flag - in a word */

Just loop checking each character with isspace() and handle setting your in flag to either 1 (in word) or 0 (in space before or between words) incrementing your counter each time you go in a new word, and exiting the loop when your count reaches 3, e.g.

        for (; *p; p++) {               /* loop over each char */
            if (!in && !isspace(*p)) {  /* if not in word and not space */
                in = 1, n++;            /* set in flag, increment words */
                if (n == NWORD)         /* if 3rd word, break */
                    break;
            }
            else if (isspace(*p))       /* if space */
                in = 0;                 /* unset in flag */ 
        }

Putting it altogether in a short example, you could do something similar to the following which takes input until the Enter key is pressed alone on an empty line, and outputting each sentence entered beginning with the third-word, or displaying the error "too few words." if a sentence with less that three words is entered, e.g.

#include <stdio.h>
#include <ctype.h>

#define MAXC  1024  /* buffer size (don't skimp) */
#define NWORD    3  /* output beginning with NWORD word */

int main (void) {

    for (;;) {                  /* loop continually until empy-line */
        char buf[MAXC] = "",    /* buffer to hold line */
            *p = buf;           /* pointer to walk down buffer */
        int n = 0,              /* word counter */
            in = 0;             /* flag - in a word */

        fputs ("\nenter sentence: ", stdout);               /* prompt */
        if (!fgets (buf, MAXC, stdin) || *buf == '\n') {    /* read line */
            puts ("all done!");
            break;
        }

        for (; *p; p++) {               /* loop over each char */
            if (!in && !isspace(*p)) {  /* if not in word and not space */
                in = 1, n++;            /* set in flag, increment words */
                if (n == NWORD)         /* if 3rd word, break */
                    break;
            }
            else if (isspace(*p))       /* if space */
                in = 0;                 /* unset in flag */ 
        }

        if (n == NWORD) /* if 3 or more words */
            fputs (p, stdout);
        else            /* other wise handle error */
            fputs ("too few words.\n", stderr);
    }

    return 0;
}

Example Use/Output

$ ./bin/thirdword

enter sentence: one two three four five
three four five

enter sentence: one two
too few words.

enter sentence:   one   two   three
three

enter sentence:
all done!

Look things over and let me know if you have further questions.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
0

count = 1; //shouldn't this be count = 0? and point = &arr[i + 2]; //shouldn't this be [i+1]?

The following answers both questions.

count    count     count       count
  0        1         2           3
     one       two       three       four
     i+0       i+1       i+2         i+3

point = &arr[i + 2]; along with printf("%s\n", point); says that print all characters from address of arr[i + 2] till seeing \0 character