6

I've written the following code to reverse a string in C. The code seems to works properly and that's why I'm confused. Does anyone know why there is not an error here? I was expecting an array out of bounds or an infinite loop on the for loop, but it seems the loop breaks before it gets to negative values.

#include <stdio.h>
#include <string.h>

void reverse(char* str);

void reverse(char* str)
{
    size_t len = strlen(str); 

    for(int i = (int)len-1; i<=len; i--)
    {
        printf("%c", str[i]);
    }
}

int main (int argc, const char * argv[])
{
    char string[] = {'h', 'e', 'l', 'l', 'o', '\0'};
    reverse(string);

    return 0;
}
Edward Falk
  • 9,991
  • 11
  • 77
  • 112
sager89
  • 940
  • 1
  • 12
  • 25
  • `for(int i = (int)len-1; i >= 0; i--)` is what's logically correct. What you had, would be true while `i` is less than `len`, which is always true. –  Jun 12 '13 at 03:43
  • signed/unsigned comparison is unwise. http://stackoverflow.com/a/5416498/489590 – Brian Cain Jun 12 '13 at 03:44
  • 4
    @Legend so? that doesnt answer his question, why it is working for him, we all know how it is done properly – Ulterior Jun 12 '13 at 03:44
  • @Ulterior it's a logical flaw. I don't know C/C++ and only come from a Java and ASM background. "we all know how it is done properly", well obviously not considering it was done poorly here and you are supporting such acts. –  Jun 12 '13 at 03:46
  • 1
    @Ulterior also, I didn't post as an answer. If I wanted to answer it, I would've posted an answer. –  Jun 12 '13 at 03:49
  • @Legend its OK, no offense m8 – Ulterior Jun 12 '13 at 03:50
  • @Legend we will have to learn to live with that throughout the rest of our life, then... – Ulterior Jun 12 '13 at 03:53
  • The same mistake as in the classic `size_t len; for(len=x; len>=0; len--){}` bug, but it makes the code work instead of fail. – ugoren Jun 12 '13 at 06:37

1 Answers1

16

size_t is generally defined as unsigned. When you compare a signed and an unsigned number or equal rank the signed number is converted to unsigned. Since the signed number is probably represented in two's complement in your machine negative numbers are actually larger.

So once i hits -1, it is larger the comparison thinks its larger than len.

You can see this is happening by turning on warning in your compiler.

Compiling your program with clang -Weverything produces this warning

unsigned.c:10:30: warning: comparison of integers of different signs: 
                  'int' and 'size_t' (aka 'unsigned long') [-Wsign-compare]
FDinoff
  • 30,689
  • 5
  • 75
  • 96
  • 2
    Detail fixes: When you compare a signed and an unsigned number _of equal rank_ the signed number is _converted_ to unsigned. – aschepler Jun 12 '13 at 03:49
  • Strangely enough, rewriting as for(size_t i = len-1; i<=len; i--) also appears to work fine. For reference, I'm using GNU gdb 6.3.50-20050815 (Apple version gdb-1708) x86_64-apple-darwin. – sager89 Jun 12 '13 at 04:03
  • @sager89 since size_t is unsigned 0-1 is large. (MAX_INT) about 4 billion. (2^32-1). The integer rolled over. – FDinoff Jun 12 '13 at 04:05
  • @FDinoff Except in my case, size_t is 2^64-1 or 18446744073709551615. – sager89 Jun 12 '13 at 04:28