0

I'm brushing up on some C++ and so one of the problems I'm trying to solve is counting characters from a character pointer and check it against what I expect to see. However in my solution, I noticed a peculiar result. I passed in a reference to a char to my function and it returned a count of 3. Why would the reference test return back a count of 3 for a reference to a character?

I realize the character doesn't have a null terminator and so the code keeps counting but it does eventually return a result, so that means the solution falls short. Any ideas to make it more robust? Here is my solution and result.

CountCharacters.cpp

#include <cstdio>
#include <iostream>

#define ASSERT_EQUALS(paramx1, paramx2) \
{\
        int param1 = paramx1;\
        int param2 = paramx2;\
        if (param1==param2)\
                std::cout << "PASS! param1=" << param1 << " param2=" << param2 << std::endl;\
        else\
                std::cout << "FAIL! param1=" << param1 << " param2=" << param2 << std::endl;\
}

int countCharacters(const char * characters);


int main()
{
        char character = '1';
        ASSERT_EQUALS(countCharacters("string8\0"), 7);
        ASSERT_EQUALS(countCharacters("\0"), 0);
        ASSERT_EQUALS(countCharacters(""), 0);
        ASSERT_EQUALS(countCharacters(NULL), 0);
        ASSERT_EQUALS(countCharacters(&character), 1);
        ASSERT_EQUALS(countCharacters('\0'), 0);
        return 0;
}


int countCharacters(const char * characters)
{
        if (!characters) return 0;
        int count = 0;
        const char * mySpot = characters;
        while (*(mySpot) != '\0')
        {
                std::cout << "Count=" << count << " mySpot=" << *(mySpot) << std::endl;
                count++;
                mySpot++;

        }
        return count;
}

Results:

PASS! param1=7 param2=7
PASS! param1=0 param2=0
PASS! param1=0 param2=0
PASS! param1=0 param2=0
FAIL! param1=2 param2=1  
PASS! param1=0 param2=0
halfer
  • 19,824
  • 17
  • 99
  • 186
nndhawan
  • 597
  • 6
  • 24
  • How many wheels do you want to reinvent? There is a site for codereview. – Captain Giraffe Apr 24 '17 at 00:12
  • 1
    Possible duplicate of [Strlen Function behavior on single character](http://stackoverflow.com/questions/35477662/strlen-function-behavior-on-single-character) – Ken Y-N Apr 24 '17 at 00:14
  • You're not going to be able to adapt your countCharacters function to this scenario. There's no way to tell if it's been given a null-terminated string or a single character. I think your best bet if you're set on doing this is to overload the function for a char* and return 1. – gsemac Apr 24 '17 at 00:16
  • Note that although you don't use `strlen()` please read the answers to see why you cannot treat the address of a single character as a pointer to a null-terminated string. – Ken Y-N Apr 24 '17 at 00:16
  • I'm actually trying to preparing myself and without having a fundamental understanding of writing good code you will never allow coders to get better. Yea the question is basic, yea there are new and accepted methods to solve an archaic problem..but regardless the how you get to the solution is far more meaningful not the solution itself. So please, I'm just looking for a little assistance... – nndhawan Apr 24 '17 at 00:19
  • 1
    If you wrote this `while (*(mySpot+count++) != '\0')` in my interview you would lose marks. It is way harder to read and maintain than any of the alternative ways of doing the same thing. When I hire I want people who write understandable and maintainable code. Not code I have to parse in my head. – John3136 Apr 24 '17 at 00:20
  • @John3136 Thanks for the input, that's great advice. I 100% agree. I've updated the function to correct that. – nndhawan Apr 24 '17 at 00:41

1 Answers1

1

You're not passing a reference to a character. You're passing a pointer. Specifically, this is a pointer:

&character

The & and * symbols are a bit confusing when learning c++. Depending on where their located, they can be a pointer or a reference:

char character = '1';               // <- char variable
char* characterPtr = &character;    // <- pointer to the char variable
char& characterRef = *characterPtr; // <- reference to the char variable

So, you're passing a pointer to a character and your function is treating it like the head of a string and counting characters until it hits a nullptr. There just happened to be one a few chars away, which is why you're getting the value 2.

EDIT: C/C++ has no native string type, just character types. So you need libraries like the ones you're including to treat characters like heads of strings. The convention is that a nullptr terminates the string. So, you're exercising that convention nicely, but also demonstrating the issue that there's no difference between the pointer to a character and the pointer to the character at the head of a string, so its easy to accidentally pass a pointer to a character to a function that's expecting a string. Things get really interesting if the function starts copying characters into that 'string' because it assumes you allocated that memory, but it could be other data that then gets squashed.

Aside from being dangerous, the other major downside of using character strings is they're tedious to manipulate them, since there's no native functions. So, nice libraries like STL have been written to solve these problems. They don't require pointers, so are a lot safer to use (you can use references instead and do bounds checking), and they have a lot of built in methods, so cut down on the amount of coding you need to do.

buttonsrtoys
  • 2,359
  • 3
  • 32
  • 52
  • Ah...Ok I see - so I'm hitting undefined behavior in that case. How would I protect myself from that? – nndhawan Apr 24 '17 at 00:25
  • This is a nit, but I wouldn't call this an undefined behavior. The challenge is that C/C++ has no native string type. Rather, it uses a pointer to a series of chars. Pointers are both awesome and dangerous, so should be used sparingly. Short answer to your question: use std::string. – buttonsrtoys Apr 24 '17 at 14:33
  • Ok @button. So if I gave this solution in an interview would this answer be sufficient and I can ignore the fifth case as unsolvable given the function signature. – nndhawan Apr 26 '17 at 04:57
  • @nndhawan : Interview? You mean if you are asked to write a function that satisfies the given tests, in a job interview? I'd say they expect you to point out the nonsensical test. Of course you could write a function that always passes the given tests -- just extend your function to return 1 if (characters&&*characters=='1'), as none of the other test strings starts with '1'. It's just that such a function would not have any usefulness beyond passing these specific tests, and that your silence would paint you as a blasé nitpicker if you did not point it out to them. – Ludwig Schulze Apr 26 '17 at 21:03
  • @nndhawan, I edited my answer to add some clarifications on strings. If I'm following your question, the fifth case is erroneous because its passing the pointer to a character that is not the head of a string to a function that thinks it is. The interview answer would be to point out this mistake/bug. – buttonsrtoys Apr 27 '17 at 00:55