1

Recently I attended an interview and there were a few question where I had to find the flaws in the code. Below is the question.

void fun(char *p)   
{    
    int a = 0;

    int b = strlen(p) - 1;

    int d = 0;

    while(d == 0)
    {
        if(a == b)
        {
            d = 1;
        }
        else
        {
            char t = *(p + a);
            *(p + a) = *(p + b);
            *(p + b) = t;

            a += 1;
            b -= 1;
        }
    }
}

My answer was:

  1. No NULL check for p.
  2. Wrong condition if(a == b). If the length of the string is even, this condition will never meet.

Please let me know if anybody finds any other flaws, and also any comments on the answers I gave are welcome.

MC93
  • 791
  • 6
  • 14
SAM
  • 31
  • 1
  • 4
    Stylistic: Non-expressive function and variable names, no comments as to the purpose of the code (so you cannot check if the code actually works as supposed to). At that point I usually stop *doing* a code review, because it's a waste of time. ;-) – DevSolar Aug 13 '15 at 08:39
  • 2
    Flows or flaws? Those are different things. – juanchopanza Aug 13 '15 at 08:41
  • More (stylistic as well): The `int d = ; while ( d == 0 ) if ( a == b ) { d = 1; }` is not necessary. You could just as well `while ( a < b )` directly. The `*(p + a)` notation is a bit non-standard, `p[a]` would be more expressive IMHO. Then again, you never know with style, the interviewer might disagree violently. ;-) Same with `a += 1` vs. `++a`. Not flaws *per se*, but a bit clumsy coding. – DevSolar Aug 13 '15 at 08:47
  • 1
    "No NULL check for p" is not necessarily a flaw. – Jabberwocky Aug 13 '15 at 08:51
  • The function is supposed to reverse a string. Flaw number 2 you mention is correct. Is don't see any other flaws. – Jabberwocky Aug 13 '15 at 08:56
  • Why checking P for NULL ? i do not see where did you initialized that pointer inside that function. – Michi Aug 13 '15 at 09:09
  • possibility of infinite loop – venki Aug 13 '15 at 09:12
  • what if you call function as `fun("stackoverflow")` ? – venki Aug 13 '15 at 09:15
  • 5
    @venki that's the caller's error : the parameter is non-`const`. – Quentin Aug 13 '15 at 09:17
  • @Quentin my next question is NULL case also should taken care by caller side right? – venki Aug 13 '15 at 09:22
  • @venki Yes. In my answer I recommend putting in an `assert` to be extra nice, but you can also just document that a null pointer will trigger UB. – Quentin Aug 13 '15 at 09:32

3 Answers3

5

The purpose of the code is to reverse a string, in the place where it is allocated. Issues are:

Bugs

  • As you already pointed out, a==b will only work for strings of odd length. Likely what the interview question was looking for.

    (Checking p for NULL may or may not be necessary. You can't tell from the code posted: it depends on the function's documentation. For a general-purpose function, it makes most sense to leave that check to the caller.)

Coding style

  • Nonsense variable and function names.
  • p[a] is preferred before *(p + a) since it is more readable.
  • The loop termination condition is very ugly and superfluous. I would prefer while(a < b), which will also fix the mentioned bug.
  • size_t should be used instead of int.
  • No comments or source code documentation.

The code should be rewritten to something like this (not tested):

#include <string.h>

void str_reverse (char* str)   
{    
  size_t start = 0;
  size_t end = strlen(p) - 1;

  while(start < end)
  {
    char tmp   = str[start];
    str[start] = str[end];
    str[end]   = tmp;

    start++;
    end--;
  }
}

Note that now there's actually not really a need for comments any longer, as the variable and function names made the code self-documenting.

Community
  • 1
  • 1
Lundin
  • 195,001
  • 40
  • 254
  • 396
  • Prefix increment / decrement as a good habit to get into should he later on switch to OOP (where postfix might imply an object copy)? – DevSolar Aug 13 '15 at 09:01
  • @DevSolar I assume you refer to [this](http://stackoverflow.com/questions/24901/is-there-a-performance-difference-between-i-and-i-in-c). Anyway, this is tagged C, and prefix or postfix are completely equivalent, given that the compiler isn't broken. – Lundin Aug 13 '15 at 09:09
4

First transformation : d can only be 0 or 1. It's only read in the while loop's condition. In fact, it is the loop condition, the function's body can be rewritten as :

int a = 0;
int b = strlen(p) - 1;

while(a != b)
{
    char t = *(p + a);
    *(p + a) = *(p + b);
    *(p + b) = t;

    a += 1;
    b -= 1;
}

The three-lines block is definitely a swap. In C++ you'd use the actual std::swap function, but in the absence of generic functions and overloading I guess doing it by hand is fine. Let's comment it and use the indexing notation though :

// Swap p[a] and p[b]
char t = p[a];
p[a] = p[b];
p[b] = t;

a += 1 and b -= 1 can also be rewritten as ++a and --b.

It's now clear that this function uses two indices, a and b, that start at the ends and meet in the center, swapping the indexed characters on the way. It's an in-place string reversal function, so let's rename it (and p). a and b could also be renamed, but it's clear enough IMO.

void reverse(char *str)

(Swiped from Lundin's answer) as a and b are indices inside an array, their type of choice should be size_t.

size_t a = 0u;
size_t b = strlen(str) - 1u;

Now, the bug you spotted : indeed, if strlen(str) is even, a and b will run past each other in the middle, and never be equal. Let's change the condition to account for this :

while(a < b)

Finally, handling str == NULL : this function is quite low-level. There's nothing useful you can do to react to a null pointer, so just add an intelligible fail condition if that happens :

assert(str && "Please provide a non-null pointer.");

The end product :

void reverse(char *str)   
{
    assert(str && "Please provide a non-null pointer.");

    size_t a = 0u;
    size_t b = strlen(str) - 1u;

    while(a < b)
    {
        // Swap str[a] and str[b]
        char t = str[a];
        str[a] = str[b];
        str[b] = t;

        ++a;
        --b;
    }
}

Live on Coliru

Quentin
  • 62,093
  • 7
  • 131
  • 191
  • That's an odd assert(), why the && "string" and not a comment? – Lundin Aug 13 '15 at 09:05
  • 3
    @Lundin certain implementations only report the expression that was asserted upon failure, not the full line where `assert` is. This is a trick to include a message in the expression. – Quentin Aug 13 '15 at 09:05
  • But for that to work, you must have something invisible in your runtime executable that knows how to spit out the source code line (ie a very specific compiler). Seems like a strange assumption to me. Also, some compilers will whine about the right operand of the && always being true. I'd rather have something portable like `#define verbose_assert(cond) v_assert(cond, __func__, __LINE__)` where v_assert is your custom print function, which also includes the assert() call. – Lundin Aug 13 '15 at 09:22
  • @Lundin I've tested it with GCC and Clang (you can tweak the Coliru example to try it). I don't know about other compilers, but I'd expect them to output the actual expression somewhere too (what else ?). Of course a custom-buffed assertion is always a good tool, but this is the best you can get from the standard one. – Quentin Aug 13 '15 at 09:30
  • 1
    @Lundin `assert` is a macro that the C standard says must output the text of the expression (amongst other things) to the standard error stream. It is already portable. Using clang and with `-Wall` set I don't see any complaints about the RHS being true – JeremyP Aug 13 '15 at 10:35
0

Wow that's almost as bad as:

void reverse(char *s){
  for(char *end=s+strlen(s)-1; s<end; ++s,--end)
    *s^=*end^=*s^=*end; //xor swap
}

Except:

  • it's buggy on odd length and null strings
  • don't do pointer arithmetic and indexing - pick one
  • unnecessary complexity, but simplifying it 1 level shows more smell
    • Remove unnecessary d: while(1) { ... if (a==b) break; else...}
    • Now fix busy loop: while(a!=b){...}
    • Now fix odd string bug: while(a<=b)
    • And null pointer bug: while(a<b)...
  • fun, p, a, ... Mean didly.
technosaurus
  • 7,676
  • 1
  • 30
  • 52