1

In the below code,

#include<stdio.h>
#define TOTAL_ELEMENTS (sizeof(array) / sizeof(array[0]))

int main(void){
  int array[] = {7,5,4,3,2};
  int index = -1;
  for(;index <= TOTAL_ELEMENTS-2;index++)
    printf("%d\n", array[index+1]);
  return 0;
}

index <= TOTAL_ELEMENTS-2 fail before printing single element.

Is it not the condition -1 <= (5-2), that is being evaluated in for-loop?

overexchange
  • 15,768
  • 30
  • 152
  • 347

3 Answers3

2

sizeof(array) / sizeof(array[0]) yields 5 which is of type size_t. As a result, the type of the expression sizeof(array) / sizeof(array[0])-2 is also size_t, which is an unsigned integer type. The LHS of the comparison also gets promoted to size_t type (see usual arithmeic conversions) and -1 is equal to SIZE_MAX (the maximum value that a size_t can hold) after the conversion.

Hence, the comparsion index <= TOTAL_ELEMENTS-2, which is equivalent to SIZE_MAX <= 3, is false.

P.P
  • 117,907
  • 20
  • 175
  • 238
1

The division sizeof(array)/sizeof(array[0]) results in an unsigned int so when comparing with index = -1 or subtracting from it will be implicitly cast to unsigned int i.e. 0xffffffff and so the condition fails.

Instead just do a explicit cast in your macro

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

personally i would instead use an argument to your TOTAL_ELEMENTS

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

to make to code a little bit more readable.

AndersK
  • 35,813
  • 6
  • 60
  • 86
  • I know that C is loosely typed, but not at a level that it implicitly converts signed to unsigned. I could only see this as overhead feature in C – overexchange Oct 30 '16 at 12:27
  • 1
    @overexchange normally a compiler would give you a warning about implicit cast if you haven't turned it off but C doesnt prevent you from doing what you want – AndersK Oct 30 '16 at 12:28
  • Am using cygwin – overexchange Oct 30 '16 at 12:29
  • @overexchange Try to add `-Wall -Wextra` flags to your compiler and see if it will produce any warnings. – HolyBlackCat Oct 30 '16 at 12:31
  • I notice that MSVC only gives a warning if I assign the macro value to an `unsigned int` first, and use that variable in the loop. – Weather Vane Oct 30 '16 at 12:38
  • I heard of implicit and explicit type conversion in Java, now going further signed/unsigned conversion in C – overexchange Oct 30 '16 at 12:41
  • @overexchange please [see this](http://stackoverflow.com/questions/5416414/signed-unsigned-comparisons). – Weather Vane Oct 30 '16 at 12:42
  • Converting `size_t` to `int` is a bad idea: `size_t` could be larger. It'll work in a toy example, of course, but not always in the real world. Better stick to unsigned types: declare `index` of type `size_t` — `unsigned int` could overflow. – Gilles 'SO- stop being evil' Oct 30 '16 at 12:54
  • @Gilles true, i think the problem is that he wants to use -1 so I adapted it so that he could still use -1 – AndersK Oct 30 '16 at 12:56
0

In the comparison index <= TOTAL_ELEMENTS-2, the left-hand side index has the type int, and the right-hand side TOTAL_ELEMENTS-2 has the type size_t¹. The two operands of the operator have different types, so they need to be converted to a common type. The common type always has the larger of the two sizes², which is size_t (which may be the same as unsigned int on 32-bit machines, but it usually larger on 64-bit machines). And the common type is unsigned in this case, because the right-hand side is at least as large.

The common type is always chosen so that it can represent the maximum positive value of both types. If this results in an unsigned type then it can't represent negative values.

Here the variable index is an array index, so it should have the type size_t. The type int may be too small to represent all array indices.

size_t is an unsigned type; unsigned values are easier to manipulate, and have predictable behavior on overflow, so generally speaking you should use unsigned types unless you need negative values. (Overflow is still a problem with unsigned types, but at least the behavior is well-defined so the problem is easier to debug.) Here there's no point in having a variable with negative values: use index as the array index, instead of the array index minus one, and then it'll start at 0 instead of 1.

size_t index;
for(index = 0; index <= TOTAL_ELEMENTS-1; index++) {
    printf("%d\n", array[index]);
}

¹ Or unsigned int if size_t is smaller than int, e.g. if it's unsigned short, but it would be very unusual for a C implementation to be like this.
² In technical terms, the larger of the two ranks.

Gilles 'SO- stop being evil'
  • 104,111
  • 38
  • 209
  • 254