1

I'm reading the book "Head First C", and in the chapter 2.5 page 94, the author shows this code:

void find_track(char search_for[])
{
     int i;
     for (i = 0; i < 5; i++) {
         if (strstr(tracks[i], search_for))
             printf("Track %i: '%s'\n", i, tracks[i]);
     }
 }

This will search the input of the user in the following array of arrays:

char tracks[][80] = {
    "I left my heart in Harvard Med School",
    "Newark, Newark - a wonderful town",
    "Dancing with a Dork",
    "From here to maternity",
    "The girl from Iwo Jima"
};

main() function:

int main()
{
    char search_for[80];
    puts("Search for: ");
    fgets(search_for, 80, stdin);
    find_track(search_for);

    return 0;
}

Output example:

Search for: Iwo
Result:
Track 4: 'The girl from Iwo Jima'

The problem with this code is that the author says that it works, but it doesn't.

The man page of strstr() says that this function will return a null pointer if the sub-string is not encountered in any strings of the array, and it will return a pointer of the sub-string to the beginning, so I did this adaptation to the code:

void find_track(char search_for[]) {
    register int i;
    char *temp;

    for (i = 4; i >= 0; --i) {
        temp = strstr(tracks[i], search_for);
        if (temp) {
            strcpy(temp, tracks[i]);
            printf("Track %i: '%s'\n", i, temp);
        } else {
            puts("Song was not found ... ");
            break;
        }
    }
}

What is wrong with the code? I did a bit of debugging, but everything seems to be fine, so maybe its a logic error, but with something as simple as the strstr() function, something like a logic mistake sounds hard to believe.

Thanks,

And as an extra secondary question... Is this a good book for a noob?

chqrlie
  • 131,814
  • 10
  • 121
  • 189
Paul S-Pou
  • 93
  • 1
  • 11
  • 5
    [`fgets`](https://en.cppreference.com/w/c/io/fgets) *retains* the newline when its encounter is what terminates the read operation. That newline is important, because it is present in *none* of your search bodies, and thus guarantees a match will never be found. Think of a way to remove that newline from the input string (hint: if present, it is always the *last* character before the terminator). – WhozCraig May 23 '21 at 23:52
  • Your adaptation is certainly not correct; since the return value of `strstr` is a pointer into the "haystack" string (here the song title), your `strcpy` will try to copy the haystack string into itself. That is undesirable and causes at least two different kinds of undefined behavior (you mustn't use `strcpy` on overlapping strings, and if you could then you would write past the end of that string). The original author's idea to `printf(..., tracks[i])` was right. I think @WhozCraig is correct as to why their code didn't work. – Nate Eldredge May 24 '21 at 00:08
  • Btw, requests for book recommendations or opinions are off topic. There is a collected list of books at https://stackoverflow.com/questions/562303/the-definitive-c-book-guide-and-list and that's about all you're going to get from this site as far as book advice. Your book isn't on the "warning" list there, which is at least a good sign. – Nate Eldredge May 24 '21 at 00:11
  • Note that the keyword `register` is an irrelevant anachronism in C. As a new C programmer, you can forget that it exists as a keyword, except that it means you can't use it for a variable name (because it _is_ still a keyword, albeit a useless one). – Jonathan Leffler May 24 '21 at 01:24
  • @WhozCraig thanks for the comments, doing some debugging in GDB, I noticed the "iwo \n" of the fgets function, and I replaced it with a scanf, the funny thing about this is that it still doesn't work :/ – Paul S-Pou May 24 '21 at 03:48
  • @NateEldredge thanks for the feedback, I'm taking note :) – Paul S-Pou May 24 '21 at 03:49

1 Answers1

1

fgets also reads newline character. You need to skip the new line character from the string before passing it to function. May be the below line will help:

search_for[strcspn(search_for, "\n")]='\0';

strcspn gives no. of characters of first parameter before it finds first occurrence of 2nd parameter in 1st parameter. We are setting character at position of '\n' to '\0' which terminates the string at that position so characters after that position will be skipped.

Regards, Haridas.

Haridas
  • 121
  • 2
  • Thanks for the feedback, I ended up leaving this problem aside, but I have been using this validation in my other projects :) – Paul S-Pou Jun 03 '21 at 18:08