简体   繁体   中英

Can the post-increment operator be used in the parameters of a function call? in C?

My question pertains to function calls in general, but I thought of it while I was writing a priority queue using a heap. Just to give some context (not that it matters much) my heap stores items top to bottom left to right and I represent the heap as an array of structures. Upon inserting a new item, I just put it in the last place in the heap and then call the function "fix_up" at the bottom which will move the item to the proper place in the heap. I am wondering if instead of doing...

fix_up(pQueue->heap, pQueue->size);
pQueue->size++;

...I could just do...

fix_up(pQueue->heap, pQueue->size++);

I am unsure as to if this is ok for a few reasons.
1) Since pQueue->size is in the function call, I'm not even sure if it's actually pQueue->size or rather a copy of the integer stored in pQueue->size. If it was a copy then obviously I wouldn't be adding 1 to the actual pQueue->size so there'd be no point in doing this.

2) Since it's a function call, it is going to then go into the function fix_up and execute all the code there. I am wondering if this would have an unintended consequence of maybe when it went to fix_up it would get incremented by 1 and my index would be 1 higher than I intended while executing fix_up? Or would it do what it's supposed to do and wait until after fix_up had finished executing?

3) Even if it is ok, is it considered a good coding practice for C?

Status priority_queue_insert(PRIORITY_QUEUE hQueue, int priority_level, int data_item)
{
    Priority_queue *pQueue = (Priority_queue*)hQueue;
    Item *temp_heap;
    int i;

    /*Resize if necessary*/
    if (pQueue->size >= pQueue->capacity) {
        temp_heap = (Item*)malloc(sizeof(Item) * pQueue->capacity * 2);
        if (temp_heap == NULL)
            return FAILURE;
        for (i = 0; i < pQueue->size; i++)
            temp_heap[i] = pQueue->heap[i];
        pQueue->capacity *= 2;
    }

    /*Either resizing was not necessary or it successfully resized*/
    pQueue->heap[pQueue->size].key = priority_level;
    pQueue->heap[pQueue->size].data = data_item;

    /*Now it is placed as the last item in the heap. Fixup as necessary*/
    fix_up(pQueue->heap, pQueue->size);
    pQueue->size++;

    //continue writing function code here
}

Yes you can use it directly in the expression you pass as argument.

A statement like

fix_up(pQueue->heap, pQueue->size++);

is somewhat equivalent to

{
    int old_value = pQueue->size;
    pQueue->size = pQueue->size + 1;
    fix_up(pQueue->heap, old_value);
}

A note about the "equivalent" example above. Since the order of evaluation of arguments to function calls is not specified, the actual increment of pQueue->size could happen after the call to fix_up . And it also means that using pQueue->size more than once in the same call would lead to undefined behavior .

Yes you can.

However, you cannot do this:

foo(myStruct->size++, myStruct->size)

The reason is that the C standard does not say in which order the arguments should be evaluated. This would lead to undefined behavior.

1) Since pQueue->size is in the function call, I'm not even sure if it's actually pQueue->size or rather a copy of the integer stored in pQueue->size. If it was a copy then obviously I wouldn't be adding 1 to the actual pQueue->size so there'd be no point in doing this.

Whatever argument you're sending to a function, it will be evaluated before the function starts to execute. So

T var = expr;
foo(var);

is always equivalent to

foo(expr);

2) Since it's a function call, it is going to then go into the function fix_up and execute all the code there. I am wondering if this would have an unintended consequence of maybe when it went to fix_up it would get incremented by 1 and my index would be 1 higher than I intended while executing fix_up? Or would it do what it's supposed to do and wait until after fix_up had finished executing?

See above

3) Even if it is ok, is it considered a good coding practice for C?

Somewhat subjective, and a bit OT for this site, but I'll answer it anyway from my personal view. In general, I would try to avoid it.

Though, the other posts already answer this question, but none of them talk about role of Sequence Point , in this particular case, which can greatly help in clarifying OP's doubt.

From this [emphasis mine] :

There is a sequence point after the evaluation of all function arguments and of the function designator, and before the actual function call .

From this [emphasis mine] :

Increment operators initiate the side-effect of adding the value 1 of appropriate type to the operand. Decrement operators initiate the side-effect of subtracting the value 1 of appropriate type from the operand. As with any other side-effects, these operations complete at or before the next sequence point .

Also, the post increment operator increase the value of operand by 1 but the value of the expression is the operand's original value prior to the increment operation.

So, in this statement:

fix_up(pQueue->heap, pQueue->size++);

the value of pQueue->size will be increased by 1 before the fix_up() function call but the argument value will be the original value prior to the increment operation.

Yeah you can use it in function calls, but please note that your two examples are not equivalent. The pQueue->heap argument may be evaluated before or after pQueue->size++ and you can't know or rely on the order. Consider this example:

int func (void)
{
  static int x = 0;
  x++;
  return x;
}

printf("%d %d", func(), func());

This will print 1 2 or 2 1 and we can't know which we'll get. The compiler need not evalute the function parameters consistently throughout the program. So if we add a second printf("%d %d", func(), func()); we could get something like 1 2 4 3 as output.

The importance here is to not write code which relies on order of evaluation. Which is the same reason as mixing ++ with other operations or side-effects in the same expression is bad practice. It can even lead to undefined behavior in some cases.

To answer your questions:

1) Since pQueue->size is in the function call, I'm not even sure if it's actually pQueue->size or rather a copy of the integer stored in pQueue->size. If it was a copy then obviously I wouldn't be adding 1 to the actual pQueue->size so there'd be no point in doing this.

The ++ is applied to the variable in the caller, so this isn't an issue. The local copy of the variable happens during function call, independently of the ++. However, the result of a ++ operation is not a so-called "lvalue" (addressable data), so this code is not valid:

void func (int* a);
...
func(&x++);  

++ takes precedence and is evaluted first. The result is not an lvalue and cannot have its address taken.


2) Since it's a function call, it is going to then go into the function fix_up and execute all the code there. I am wondering if this would have an unintended consequence of maybe when it went to fix_up it would get incremented by 1 and my index would be 1 higher than I intended while executing fix_up? Or would it do what it's supposed to do and wait until after fix_up had finished executing?

This isn't an issue unless the function modifies the original variable through a global pointer or such. In that case you would have problems. For example

int* ptr;

void func (int a)
{
  *ptr = 1;
}

int x=5;
ptr = &x;
func(x++);

This is very questionable code and x will be 1 after the line func(x++); and not 6 as one might have expected. This is because the function call expression is evaluated and finished before the function call.


3) Even if it is ok, is it considered a good coding practice for C?

It will work ok in your case but it is bad practice. Specifically, mixing the ++ or -- operators together with other operators in the same expression is bad (although common) practice, since it has a high potential for bugs and tends to make code less readable.

Your original code with pQueue->size++; on a line of it's own is superior in every way - stick with that. Contrary to popular belief, when writing C, you get no bonus points for "most operators on a single line". You may however get bugs and maintenance problems.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM