/*
About this program:
- This program counts words.
- The specific words that will be counted are passed in as command-line
  arguments.
- The program reads words (one word per line) from standard input until EOF or
  an input line starting with a dot '.'
- The program prints out a summary of the number of times each word has
  appeared.
- Various command-line options alter the behavior of the program.

E.g., count the number of times 'cat', 'nap' or 'dog' appears.
> ./main cat nap dog
Given input:
 cat
 .
Expected output:
 Looking for 3 words
 Result:
 cat:1
 nap:0
 dog:0
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "smp0_tests.h"

#define LENGTH(s) (sizeof(s) / sizeof(*s))

/* Structures */
typedef struct {
  char *word;
  int counter;
} WordCountEntry;


int process_stream(WordCountEntry entries[], int entry_count)
{
  short line_count = 0;
  char buffer[30];

  while (gets(buffer)) {
    if (*buffer == '.')
      break;
    /* Compare against each entry */
    int i = 0;
    while (i < entry_count) {
      if (!strcmp(entries[i].word, buffer))
        entries[i].counter++;
      i++;
    }
    line_count++;
  }
  return line_count;
}


void print_result(WordCountEntry entries[], int entry_count)
{
  printf("Result:\n");
  while (entry_count-- > 0) {
    printf("%s:%d\n", entries->word, entries->counter);
  }
}


void printHelp(const char *name)
{
  printf("usage: %s [-h] <word1> ... <wordN>\n", name);
}


int main(int argc, char **argv)
{
  const char *prog_name = *argv;

  WordCountEntry entries[5];
  int entryCount = 0;

  /* Entry point for the testrunner program */
  if (argc > 1 && !strcmp(argv[1], "-test")) {
    run_smp0_tests(argc - 1, argv + 1);
    return EXIT_SUCCESS;
  }

  while (*argv != NULL) {
    if (**argv == '-') {

      switch ((*argv)[1]) {
        case 'h':
          printHelp(prog_name);
        default:
          printf("%s: Invalid option %s. Use -h for help.\n",
                 prog_name, *argv);
      }
    } else {
      if (entryCount < LENGTH(entries)) {
        entries[entryCount].word = *argv;
        entries[entryCount++].counter = 0;
      }
    }
    argv++;
  }
  if (entryCount == 0) {
    printf("%s: Please supply at least one word. Use -h for help.\n",
           prog_name);
    return EXIT_FAILURE;
  }
  if (entryCount == 1) {
    printf("Looking for a single word\n");
  } else {
    printf("Looking for %d words\n", entryCount);
  }

  process_stream(entries, entryCount);
  print_result(entries, entryCount);

  return EXIT_SUCCESS;
}

Dani AI

Generated

As and noted, the exclamation in front of strcmp() is just a compact way to turn the function's zero return (strings equal) into a true boolean. That idiom is common, but terse. For readability and maintenance prefer an explicit equality test.

A clearer form:

if (strcmp(a, b) == 0) {
    /* strings are equal */
}

A few practical, immediate improvements for the posted program (these go beyond the equality question and fix real bugs you’ll hit when testing ’s code):

  • Don’t use gets() — it can overflow the buffer. Use fgets() and strip the newline before comparing.

    char buf[128];
    while (fgets(buf, sizeof buf, stdin) != NULL) {
      size_t n = strlen(buf);
      if (n && buf[n-1] == '\n') buf[n-1] = '\0';
      if (buf[0] == '.') break;
      /* compare buf to entries... */
    }
  • Fix the result printer: the original print_result dereferences entries without indexing and will repeatedly print the first entry. Use a simple indexed loop:

    for (int i = 0; i < entry_count; ++i)
      printf("%s:%d\n", entries[i].word, entries[i].counter);
  • Other cautions: check pointers for NULL before calling strcmp; use strncmp() or length-checked reads when appropriate; remember strcmp is case-sensitive (use strcasecmp/_stricmp or normalize case if you want case-insensitive matches). For option parsing prefer iterating argv from index 1 or using getopt(); also add break in switch cases to avoid fall-through.

These points explain why !strcmp(...) works and give concrete, safer alternatives and fixes so the program behaves predictably and is easier for others to read.

Recommended Answers

All 2 Replies

Here's the return value of strmp

RETURN VALUE
The strcmp() and strncmp() functions return an integer less than, equal
to, or greater than zero if s1 (or the first n bytes thereof) is found,
respectively, to be less than, to match, or be greater than s2.

The ! operator negates the return value of strcmp.

In other words, if the words match, 0 is returned - which is false in boolean terms. So if two words are equal, false is returned. Thus the reason for the ! operator.

if (!strcmp(x, "some_string"))
{
   // comparison is TRUE
}
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.