Sunday, January 17, 2010

C/C++ Quiz: the comma operator

I never cease to amaze me how I am always learning new things when programming C/C++ even after this many years. However I thought that mostly related to quirks in C++ and C++ template stuff. Yesterday I discovered something in C, which I kind of think I should have known. Especially since I have used it countless times without really knowing it. Why not make this a little quiz, to see if you really thought about it too.

You have probably written code like this before int x = 2, y = 3 or for (i = 0, j = 1; i < size; ++i, ++j). So here is the quiz. Without looking up in a reference or googling. What does this do, and print out?

int i = 5, size = 5;
i < size || (i = -1), ++i;
printf("%d\n", i); 

Answer: It increments i but wraps when becomes size or bigger. Meaning incrementing i when it is size or larger sets it to zero. So 0 is printed out. How is how it works:

  1. The || operator will only evaluate the right expression if the left one was false. Because the right expression does not need to be evaluated to determine that the whole expression is true when left is true.
  2. So when i is 5 the left expression is false and i is thus set to -1.
  3. The comma operator is an expression operator. a, b makes sure b is evaluated after a. Meaning ++i will always be evaluated, but after all the other expressions that needs to be evaluated to find the value of the expression.
So it could have been written as:

int i = 5, size = 5;
if (i >= size)
  i = 0;
printf("%d\n", i); 

What got me into this was trying to understand this macro found in the source code of the lua script language:

#define luaL_addchar(B,c) \
  ((void)((B)->p < ((B)->buffer+LUAL_BUFFERSIZE) || luaL_prepbuffer(B)), \
   (*(B)->p++ = (char)(c)))

It is basically used to copy characters into a buffer. B->p is current position in buffer. The code makes sure that luaL_prepbuffer is called if the size of the buffer is exceeded. That function will empty the current buffer (passing on the contents) and making it ready to receive more characters. So the current pointer is reset to the start.

The question is of course why do this? Probably because it makes it possible to treat the macro more like a function. An expression can be placed most places a function call can be placed. However multiple statement, even if they are enclosed by {} can not.

This is legal after current definition

for (i < 0; i < size; luaL_addchar(B,c))
But needles to say it would not have been legal if luaL_addchar(B,c) had expanded to something like this in the for statement:

for (i < 0; i < size; {if (B->p >= B->buffer+LUAL_BUFFERSIZE) 
                       *B->p++ = c;})


soumya said...

Good One :)

Anonymous said...

涼宮春日的憂鬱 -
情色成人小說 -
嘟嘟情色成人 -
台灣情色成人網 -
3d 動畫美女遊戲 -
情色成人影片 -
涼宮春日 h -
情色成人影音 -
網路美女照片 -
涼宮春日小說 -
美女交友 msn -
涼宮春日小說 bt -
18情色成人網 -
明星性愛裸照 -
中國性愛城 bt 區 -
免費視訊美女 -
涼宮春日的激奏 -
18禁情色成人影音 -
1中華性愛 -
情色成人短片 -
中國性愛城 bt -
中國性愛城 -
性愛圖貼 -
免費視訊美女影音觀賞 -
人妖性愛 -
性愛真諦 -
色 色 視訊網 -
免費情色成人圖片 -
bbs性愛網站 -
台灣視訊美女專區 -

David said...

The comma operator is possibly useful in very simple statements, but you should be wary of using it with arithmetic operators, such as:
int i = 5, size = 10;
(i = size) + ((i = 3), printf("%d\n", i));

Which will print out 5 when you may have expected it to print out 3.

David said...

Oops i meant 10.

iru said...

in the second, bigger, piece of code shouldn't i = 0 be i = -1?