0

Possible Duplicate:
Confused about C macro expansion and integer arithmetic
A riddle (in C)

The expected output of the following C program is to print the elements in the array. But when actually run, it doesn't do so.

#include<stdio.h>

  #define TOTAL_ELEMENTS (sizeof(array) / sizeof(array[0]))
  int array[] = {23,34,12,17,204,99,16};
  int main() 
  {
      int d;
      for(d=-1;d <= (TOTAL_ELEMENTS-2);d++)
          printf("%d\n",array[d+1]);
      return 0;
  }
Community
  • 1
  • 1
Parag
  • 7,746
  • 9
  • 24
  • 29

3 Answers3

6

Because sizeof gives you an unsigned value, which you probably would have noticed had you turned up the warning level, such as using -Wall -Wextra with gcc (a):

xyzzy.c: In function 'main':
xyzzy.c:8: warning: comparison between signed and unsigned

If you force it to signed, it works fine:

#define TOTAL_ELEMENTS (int)((sizeof(array) / sizeof(array[0])))

What happens in detail can be gleaned from the ISO standard. In comparisons between different types, promotions are performed to make the types compatible. The compatible type chosen depends on several factors such as sign compatibility, precision and rank but, in this case, it was deemed that the unsigned type size_t was the compatible type so d was upgraded to that type.

Unfortunately, casting -1 to an unsigned type (at least for two's complement which is almost certainly what you're using) results in a rather large positive number.

One that's certainly larger the the 5 you get from (TOTAL_ELEMENTS-2). In other words, your for statement effectively becomes:

for (d = some big honking number way greater than five;
     d <= 5;
     d++
) {
    // fat chance of getting in here !!
}

(a) This requirement to use extra remains a point of contention between the gcc developers and myself. They're obviously using some new definition of the word "all" of which I was previously unaware (with apologies to Douglas Adams).

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • I am new to programmin language. So I dont know whats wrong with this code. Can you please suggest correction... – Parag Mar 28 '12 at 06:32
  • @Parag, I gave you the correction, it's casting the result of the sizeof expression to a signed type. – paxdiablo Mar 28 '12 at 06:33
  • So, it means my for loop will be like for(d=-1;d <= (int)(TOTAL_ELEMENTS-2);d++) – Parag Mar 28 '12 at 06:34
  • 2
    Glad you've found the answer. I've got a little question though. Since you could easily have started your loop at 0, WHY IN THE NAME OF ALL THAT IS LOGICAL DID YOU DO IT LIKE THIS? – Mr Lister Mar 28 '12 at 06:43
  • if you compare signed and unsigned, the signed item promotes to unsigned type, -1 goes to 0xffffffff, so the compare is false. – pizza Mar 28 '12 at 06:49
  • @Parag No. 1) Start the iteration at zero. 2) If you for some mighty strange reason must start the loop at a negative number, then the loop should be `for(d=-1; d <= ((int)TOTAL_ELEMENTS-2); d++)`. – Lundin Mar 28 '12 at 07:01
  • I think you meant to write `-Wextra` instead of `-Werror` above in the text, considering your comment to gcc developers – steabert Mar 28 '12 at 08:07
  • Per 6.3.1.3(2), casting -1 to an unsigned integer type always gives you `Utype_MAX`. On non-twos-complement platforms, that changes the bit-pattern. – Daniel Fischer Mar 28 '12 at 11:11
  • @paxdiablo - yes indeed, the logic of "all" meaning "most" (or even "some"?) escapes me too. Perhaps the next one will be -Wstillmore? – Dan Mar 29 '12 at 01:52
0

TOTAL_ELEMENTS is of type size_t, subtracting 2 is done at compile time and so it is 5UL (emphasis on the unsigned suffix). The comparison with the signed integer d is then always false. Try

for(d=-1;d <= (ssize_t)(TOTAL_ELEMENTS-2);d++)

FTW, the intel compiler warns about exactly this when you try and compile the code.

hroptatyr
  • 4,702
  • 1
  • 35
  • 38
  • Note: gcc doesn't warn about the signed/unsigned comparison, not even with `-Wall`. You have to specify `-Wextra`. – Mr Lister Mar 28 '12 at 06:40
0

To clarify what went wrong: sizeof() translates to a result type of size_t, which is nothing but an unsigned integer, larger than or equal to unsigned int.

So the result of (sizeof(array) / sizeof(array[0])) is a result in two operands of size_t type. The division is performed on these operands:size_t / size_t. Both operands are of the same type so it works fine. The result of the division is of type size_t, which is the type that TOTAL_ELEMENTS results in.

The expression (TOTAL_ELEMENTS-2) therefore have the types size_t - int, since the integer literal 2 is of type int.

Here we have two different types. What happens then is something called balancing (formally "the usual arithmetic conversions"), which happens when the compiler spots two different types. The balancing rules state that if one operand is signed and the other unsigned, then the signed one is silently, implicitly converted to an unsigned type.

This is what happens in this code. size_t - int is converted to size_t - size_t, then the subtraction is executed, the result is size_t. Then int <= size_t is converted to size_t <= size_t. The variable d turns unsigned, and if it had a negative value, the code goes haywire.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • "The balancing rules state that if one operand is signed and the other unsigned, then the signed one is silently, implicitly converted to an unsigned type." isn't quite correct. If the unsigned type has smaller rank than the signed, and all values of the unsigned type can be represented by the signed type, then the unsigned value is converted to the signed type. Doesn't apply here, just for completeness. – Daniel Fischer Mar 28 '12 at 11:19
  • @DanielFischer I am aware, I left out all the "ifs & buts" on purpose. I suspect that the OP will not be helped with a detailed explanation of all the integer promotion and usual arithmetic conversion rules. – Lundin Mar 28 '12 at 13:58
  • Yeah, my inner pedant just thought it should be mentioned. – Daniel Fischer Mar 28 '12 at 13:59