5

I'm new to C, and I'm working on my own explode like function. I'm trying to count how many times a specified character occurs in a string.

int count_chars(char * string, char * chr)
{
    int count = 0;
    int i;

    for (i = 0; i < sizeof(string); i++)
    {
        if (string[i] == chr)
        {
            count++;
        }
    }

    return count;
}

It just returns 0 every time. Can anyone explain why, please? :)

Rob
  • 7,980
  • 30
  • 75
  • 115
  • Unsurprisingly, I get a compiler warning with this code. That should have told you something was wrong and where (only one of the things that are wrong, but still). Did you get a warning when compiling? – Skizz Sep 08 '11 at 14:25

4 Answers4

12

Your code is hopelessly flawed. Here's how it should look like:

int count_chars(const char* string, char ch)
{
    int count = 0;
    int i;

    // We are computing the length once at this point
    // because it is a relatively lengthy operation,
    // and we don't want to have to compute it anew
    // every time the i < length condition is checked.
    int length = strlen(string);

    for (i = 0; i < length; i++)
    {
        if (string[i] == ch)
        {
            count++;
        }
    }

    return count;
}

See this code run on example input.

Here's what you are doing wrong:

  1. Since you want to find a character, the second parameter should be a character (and not a char*), This has implications later (see #3).
  2. sizeof(string) does not give you the length of the string. It gives the size (in bytes) of a pointer in your architecture, which is a constant number (e.g. 4 on 32-bit systems).
  3. You are comparing some value which is not a memory address to the memory address chr points to. This is comparing apples and oranges, and will always return false, so the if will never succeed.
  4. What you want to do instead is compare a character (string[i]) to the second parameter of the function (this is the reason why that one is also a char).

A "better" version of the above

Commenters below have correctly identified portions of the original answer which are not the usual way to do things in C, can result in slow code, and possibly in bugs under (admittedly extraordinary) circumstances.

Since I believe that "the" correct implementation of count_chars is probably too involved for someone who is making their first steps in C, I 'll just append it here and leave the initial answer almost intact.

int count_chars(const char* string, char ch)
{
    int count = 0;
    for(; *string; count += (*string++ == ch)) ;
    return count;
}

Note: I have intentionally written the loop this way to make the point that at some stage you have to draw the line between what is possible and what is preferable.

See this code run on example input.

Jon
  • 428,835
  • 81
  • 738
  • 806
  • This code is horrible; in principle it calls `strlen` on each iteration, making a simple `O(n)` task become `O(n²)`! A good compiler *might* be able to optimize that out if it determines that the string does not change in the loop, but the correct loop condition is to replace `i – R.. GitHub STOP HELPING ICE Sep 08 '11 at 13:56
  • @R..: I do agree with all of your well-founded objections (I sincerely hope that any modern compiler will optimize the `strlen` call though). Please take into account that I am replying to a novice and I do not want to scrap *all* their code, which would make it much harder for them to understand what they were doing wrong and why. IMHO the code above, while subtly flawed, is a reasonable stepping stone in the path to knowledge. Thank you. – Jon Sep 08 '11 at 14:00
  • @Rob: Is the current version satisfactory or do you feel it could use some expansion? – Jon Sep 08 '11 at 14:04
  • Calling strlen every iteration through the loop - now that's efficient! Interestingly, in DevStudio 2010 release mode, the strlen function is inlined twice, once before the for loop and one at the end. – Skizz Sep 08 '11 at 14:06
  • @Skizz: Please see my answer to R. While *we* might know the difference, discussions of O(n) vs O(n^2) will be totally lost on the intended audience and will only distract from the gist of the answer. – Jon Sep 08 '11 at 14:08
  • @Jon, well your version still returns 0 for me. – Rob Sep 08 '11 at 14:10
  • 1
    Even if the discussion of O(n) versus O(n²) is lost on the audience, that's not an excuse to teach very bad practices that will lead to O(n) loops becoming O(n²). A new programmer should just learn that testing `string[i]!=0` is **idiomatic** in C for looping over a string. – R.. GitHub STOP HELPING ICE Sep 08 '11 at 14:12
  • @R..: I have added another section with a correct and idiomatic answer, which I hope you will find acceptable. But I would also like to know: would you ever try to *teach* someone using that? – Jon Sep 08 '11 at 14:26
  • Hm. You're right. It _is_ working for me, if I directly pass a string rather than the variable I'm passing. The problem lies in the variable I'm passing to it, I guess. I'll play with it and see if I can get it working from here. Thanks to both you and R for all the help. – Rob Sep 08 '11 at 14:30
  • 2
    @Jon: Absolutely. There's no point in learning C if you're not going to learn the *idioms of C*, since writing C with the idioms of a different language will just give you the worst of both worlds (all the dangers of C and all the inefficiency of other languages). I would consider looping over a string until you hit the null termination one of the most important idioms of writing your own string processing code in C (as opposed to just making library calls). – R.. GitHub STOP HELPING ICE Sep 08 '11 at 14:44
  • Also the solution that doesn't use the if statement avoids the branch mispredictions. That's an observation from the micro-architecture scope but still a subject to refer to. Also it's very C to use evaluations of logical expressions in arithmetic expressions just like here, talking about idioms. – gon1332 Jan 18 '15 at 23:13
  • @Jon To make things more dramatic you could have written: `while (count += (*string++ == ch), *string);` or even this: `while (*string && -~(count += (*string++ == ch)));`. – gon1332 Jan 18 '15 at 23:28
2

You probably want to use a function that actually gets the length of string, instead of sizeof.

sizeof will get the size of the datatype. It will not return the length of the string. strlen will return the length of the string.

FrustratedWithFormsDesigner
  • 26,726
  • 31
  • 139
  • 202
2

This is C! It's designed to be terse!

int count_chars(const char* string, char ch)
{
  int c = 0;
  while (*string) c += *(string++) == ch;
  return c;
}

Update

I'll try and explain how it works:

int c = 0;

This will be the count of the number of matches that have been found.

while (*string)

This is the loop control statement and will continue to iterate the loop as long as the condition is true. In this case the condition is *string. In C, strings are stored 'null terminated' which means the last character of the string is a character with value 0 ('\0'). *string evaluates to the character the pointer is pointing to. Expressions in C are 'true' if they evaluate to any non-zero value and 'false' if they evaluate to zero. *string is an expression, so any non-zero character *string points to is true and the '\0' at the end of the string is false. So this will terminate if *string is pointing to the end of the string.

*(string++)

This is an expression which evaluates to the value the pointer is pointing at. The ++ is a post-increment so the value of the pointer is moved one place forward, i.e. it points to the next character in the string. Note the the value of the expression is not the same as the value of *string after the expression has been evaluated because the pointer has moved.

*(string++) == ch

This is a comparison expression, it compares the value of *string (before it was updated) to the value of ch. In C, the result of this is an integer (there's no bool type in C) which has the value '1' if the expression is true and '0' if the expression is false.

c += *(string++) == ch;

We know the bit after the += is a '1' if the character is one we're looking for and '0' if not. The += is shorthand for:

c = c + (*(string++) == ch);

so it will increment the count if a matching character has been found.

In this particular case, there's little advantage to the += syntax, but if c was more complex, say *(variable [index].structure_member [index2]) then it would only be evaluated once.

The ; at the end marks the end of the statement and because there's no { after the while, it also marks the end of the while loop.

Skizz
  • 69,698
  • 10
  • 71
  • 108
0

As everyone told you the answer,

1)you cant use size of but instead strlen(string).They have told you the reason

2)I think everyone missed it here , the second parameter you used is char pointer.but everyone told you to make it as chr but if you want to do it still.

then in the loop it should be

if ( string(i)== *chr ) \\ not just ch remember you declared it as a pointer
                      ch gives the address but you want the character so use *ch

You can also use a strchr function.

   int count_chars (char *string, char ch)
       {
         int i;
        if(string=strchr(string,'s'))++i;
            while (string!=NULL)
               if(string=strchr(string+1,chr)
               ++i;
              return i;
        }
niko
  • 9,285
  • 27
  • 84
  • 131