Author Topic: Void cast as a function  (Read 10857 times)

0 Members and 3 Guests are viewing this topic.

Offline blueskullTopic starter

  • Supporter
  • ****
  • !
  • Posts: 367
  • Country: cn
  • BA7LKP
Void cast as a function
« on: August 01, 2017, 07:41:40 am »
I just came across this in uC-OS3 source code. What does it do?
I didn't include the real source for for copyright reasons. Here's a similar one:

static void x(int y)
{
    (void)&y;
}

I really can't think of what it does, besides to mute the unused variable warning from the compiler.
Any suggestions?
« Last Edit: August 01, 2017, 07:48:39 am by blueskull »
 

Offline Yansi

  • Super Contributor
  • ***
  • Posts: 3893
  • Country: 00
  • STM32, STM8, AVR, 8051
Re: Void cast as a function
« Reply #1 on: August 01, 2017, 08:31:28 am »
Probably only really to mute the unused warning.

This is how the "unused" macro is usually done:

Code: [Select]
#define UNUSED(x) ((void)(x))
 
The following users thanked this post: blueskull

Offline legacy

  • Super Contributor
  • ***
  • !
  • Posts: 4415
  • Country: ch
Re: Void cast as a function
« Reply #2 on: August 01, 2017, 09:14:28 am »
It passes the address of a function as uint32_t.
Then it converts back the uint32_t into a function_pointer
(it assumes it's void, no return arguments).
And It jumps into it.

 

Offline newbrain

  • Super Contributor
  • ***
  • Posts: 1739
  • Country: se
Re: Void cast as a function
« Reply #3 on: August 01, 2017, 10:33:49 am »
It passes the address of a function as uint32_t.
Then it converts back the uint32_t into a function_pointer
(it assumes it's void, no return arguments).
And It jumps into it.
:-// How do you read all of this in the code snippet from blueskull?
There no cast to function pointer, just to void.
What is strange is the use of the address operator.
Am I missing something?
Nandemo wa shiranai wa yo, shitteru koto dake.
 

Offline mib

  • Contributor
  • Posts: 14
Re: Void cast as a function
« Reply #4 on: August 01, 2017, 11:04:10 am »

 :palm:  That is not a cast to a function pointer. Not even close.


It's all about suppressing warnings. Back in the distant past it was surprisingly difficult to suppress the unused variable warning without actually generating any code. So people came up with nonsense like this. It does nothing, just ignore it.


These days compilers are smart enough to handle more obvious ways of suppressing the warning. Or you just don't name the argument and that avoids the warning.
 
The following users thanked this post: janoc, Frank

Offline Kjelt

  • Super Contributor
  • ***
  • Posts: 6504
  • Country: nl
Re: Void cast as a function
« Reply #5 on: August 01, 2017, 11:15:34 am »
It's all about suppressing warnings. Back in the distant past it was surprisingly difficult to suppress the unused variable warning without actually generating any code. So people came up with nonsense like this. It does nothing, just ignore it.
These days compilers are smart enough to handle more obvious ways of suppressing the warning. Or you just don't name the argument and that avoids the warning.
True, still compilers today are so smart they throw away unused strings of for instance program version information.
I always add a string at the start of my code with the ProductName, SVN commitnr, Date and compiler version.
Why? Because when I read out a uC I can directly see if it is the correct code for the product and which version, it sometimes is a big timesaver.

When I don't use the pointer to that string somewhere in the code the compiler even in non-optimized mode will just throw it away, but then it is not a great compiler :D
So something like this could also be used for that purpose.
 

Offline glarsson

  • Frequent Contributor
  • **
  • Posts: 814
  • Country: se
Re: Void cast as a function
« Reply #6 on: August 01, 2017, 11:18:12 am »
It passes the address of a function as uint32_t.
Then it converts back the uint32_t into a function_pointer
If this is supposed to be C then you can not do this. Pointers to data and pointers to functions are not compatible. No use of casts or other tricks can get around this.
 

Offline grumpydoc

  • Super Contributor
  • ***
  • Posts: 2906
  • Country: gb
Re: Void cast as a function
« Reply #7 on: August 01, 2017, 11:36:12 am »
I just came across this in uC-OS3 source code. What does it do?
I didn't include the real source for for copyright reasons. Here's a similar one:

static void x(int y)
{
    (void)&y;
}

I really can't think of what it does, besides to mute the unused variable warning from the compiler.
Any suggestions?
As written that code fragment does nothing - it takes the address of the parameter y but does nothing with it. The cast to void will just prevent the compiler issuing a "value not used" warning.

It passes the address of a function as uint32_t.
Then it converts back the uint32_t into a function_pointer
If this is supposed to be C then you can not do this. Pointers to data and pointers to functions are not compatible. No use of casts or other tricks can get around this.
Largely yes - in this case &y is the address of a parameter, in most architectures it will be on the stack so using it as a function address will almost certainly not produce a useful result.

That said maybe one could contrive for someting useful to happen depending on the exact architecture by taking the address of a variable and using it as a function pointer but that is really horrible code with no role outside some sort of obfusicated C contest.

But C generally makes no distinction between a data address and a function address - anything which can be cast to a function pointer type can be used to call a function.

It would be perfectly legal C to say1

    (void (*)())(&y)();


[1] I think - bit rusty on such arcanery.

NB https://cdecl.org/ is *very* useful for figuring out the syntax for complex declarations and casts.
 
The following users thanked this post: newbrain

Offline Jeroen3

  • Super Contributor
  • ***
  • Posts: 4090
  • Country: nl
  • Embedded Engineer
    • jeroen3.nl
Re: Void cast as a function
« Reply #8 on: August 01, 2017, 11:54:05 am »
Quote from: ISO/IEC 9899:TC3
6.3.2.2 void
1 The (nonexistent) value of a void expression (an expression that has type void) shall not
be used in any way, and implicit or explicit conversions (except to void) shall not be
applied to such an expression. If an expression of any other type is evaluated as a void
expression, its value or designator is discarded. (A void expression is evaluated for its
side effects.)
It makes the compiler ignore y.
However, some compilers then throw a "statement has no effect" warning, that is where the & is for, I think.
It first takes the address, then discards it.

Anyway, if you don't like it, use __attribute__((unused)) or whatever your compiler has to offer for that.
yay standards  :scared:

NB https://cdecl.org/ is *very* useful for figuring out the syntax for complex declarations and casts.
Cool.
« Last Edit: August 01, 2017, 11:56:14 am by Jeroen3 »
 

Offline dmills

  • Super Contributor
  • ***
  • Posts: 2093
  • Country: gb
Re: Void cast as a function
« Reply #9 on: August 01, 2017, 12:18:18 pm »
When I don't use the pointer to that string somewhere in the code the compiler even in non-optimized mode will just throw it away, but then it is not a great compiler :D
So something like this could also be used for that purpose.
If it does that with something like
Code: [Select]
volatile const char id_string [] = "Foo Bar Baz"; then it is broken (And yes, a volatile const is a reasonable thing in embedded programming), on the other hand
Code: [Select]
const char * id_string = "...."; is fair game in all probability.

Now there are plenty of broken C compilers in the embedded space (I remember, painfully, one which started ignoring volatile when you turned the optimiser on, a nasty surprise....).

Regards, Dan.
 

Offline helius

  • Super Contributor
  • ***
  • Posts: 3659
  • Country: us
Re: Void cast as a function
« Reply #10 on: August 01, 2017, 12:31:52 pm »
Depending on compiler settings, I can imagine that (void)&y; also forces y to be copied into the callee stack area (because you cannot take the address of a register). This may not happen if the "useless" statement is eliminated as dead code.
 

Offline Kjelt

  • Super Contributor
  • ***
  • Posts: 6504
  • Country: nl
Re: Void cast as a function
« Reply #11 on: August 01, 2017, 12:34:45 pm »
If it does that with something like
Code: [Select]
volatile const char id_string [] = "Foo Bar Baz"; then it is broken (And yes, a volatile const is a reasonable thing in embedded programming), on the other hand
Code: [Select]
const char * id_string = "...."; is fair game in all probability.
It is arguable if the volatile keyword would matter IF the variable itself (id_string) is never ever used in the code anywhere else. The compiler could conclude it is declared but never used so can be discarded.

Besides is a "volatile const" not a contradiction since the volatile is used for variables which value could change unexpectedly and const's never change ?
Anyway it does not work for that specific compiler (STM8 cosmic compiler) so I do have to use the pointer somewhere in the code.
 

Offline grumpydoc

  • Super Contributor
  • ***
  • Posts: 2906
  • Country: gb
Re: Void cast as a function
« Reply #12 on: August 01, 2017, 12:41:06 pm »
Besides is a "volatile const" not a contradiction since the volatile is used for variables which value could change unexpectedly and const's never change ?
No, think of some memory mapped read only register.

It can change without the code doing anything to it so needs to be marked volatile.

It cannot be modified so needs to be marked const
 

Offline Kjelt

  • Super Contributor
  • ***
  • Posts: 6504
  • Country: nl
Re: Void cast as a function
« Reply #13 on: August 01, 2017, 12:52:20 pm »
Ah yeah you're correct  :-+
 

Offline legacy

  • Super Contributor
  • ***
  • !
  • Posts: 4415
  • Country: ch
Re: Void cast as a function
« Reply #14 on: August 01, 2017, 02:37:19 pm »
Code: [Select]
#include "lib_debug_v1.h"
#include "types.h"

/*
 * type (*function)(argtypes);
 */

typedef void (*func_t)();

void do_call
(
    uint32_t function_addr
)
{
    func_t foo;
 // void (*foo)(void);

    foo = (void *)function_addr;
    foo();
}

void hallo()
{
    printf("hAllo\n");
}

int main()
{
    uint32_t addr;

    addr = (uint32_t) hallo;
    do_call(addr);
    return 0;
}

yup, sorry.

The above is what I described.
As you can see it's different.
 

Offline NivagSwerdna

  • Super Contributor
  • ***
  • Posts: 2497
  • Country: gb
Re: Void cast as a function
« Reply #15 on: August 01, 2017, 03:19:14 pm »
IMHO it's because the original developers used 'lint' and the void cast is to tell lint that the passed parameter is used*. 

For the avoidance of doubt I am referring to the C static analysis tool rather than the pocket fluff you might find in your dressing gown.

*used in the sense that a subsequent developer would provide a more interesting implementation at a later date with the parameter being useful to them.
« Last Edit: August 01, 2017, 03:29:52 pm by NivagSwerdna »
 

Offline janoc

  • Super Contributor
  • ***
  • Posts: 3816
  • Country: de
Re: Void cast as a function
« Reply #16 on: August 01, 2017, 04:18:49 pm »
IMHO it's because the original developers used 'lint' and the void cast is to tell lint that the passed parameter is used*. 

For the avoidance of doubt I am referring to the C static analysis tool rather than the pocket fluff you might find in your dressing gown.

*used in the sense that a subsequent developer would provide a more interesting implementation at a later date with the parameter being useful to them.

You don't need to use lint (anyone still uses that?). You will get similar warning about unused function parameters or variables from GCC and Clang by default if you use the
-Wall option, as you probably should. In plenty of places it is against the policy to turn the warnings off so hacks like the above are common to silence spurious warnings.

However, I am actually quite shocked how many people here are unable to read a simple C declaration - a function pointer? Seriously?

Some may find this guide useful - helps to decode even the most gnarly C declarations:
http://ieng9.ucsd.edu/~cs30x/rt_lt.rule.html
 

Offline NivagSwerdna

  • Super Contributor
  • ***
  • Posts: 2497
  • Country: gb
Re: Void cast as a function
« Reply #17 on: August 01, 2017, 04:31:51 pm »
You don't need to use lint (anyone still uses that?). You will get similar warning about unused function parameters or variables from GCC and Clang by default if you use the
I believe the OP is an archaeologist, I was trying to explain the behavior of our ancestors.

(*explain)();
« Last Edit: August 01, 2017, 04:33:41 pm by NivagSwerdna »
 

Offline lujji

  • Contributor
  • Posts: 29
  • Country: 00
Re: Void cast as a function
« Reply #18 on: August 01, 2017, 05:04:38 pm »
Compile and look at the assembly - it does nothing. There is one use case for this expression though - sometimes you want to read peripheral register and discard it's value.
Code: [Select]
#define ADCR (*(volatile uint8_t *)(0xDEFEC8))

(void) ADCR; // read access
 

Offline mib

  • Contributor
  • Posts: 14
Re: Void cast as a function
« Reply #19 on: August 01, 2017, 05:10:48 pm »
When I don't use the pointer to that string somewhere in the code the compiler even in non-optimized mode will just throw it away, but then it is not a great compiler :D
So something like this could also be used for that purpose.
If it does that with something like
Code: [Select]
volatile const char id_string [] = "Foo Bar Baz"; then it is broken (And yes, a volatile const is a reasonable thing in embedded programming), on the other hand
Code: [Select]
const char * id_string = "...."; is fair game in all probability.

Now there are plenty of broken C compilers in the embedded space (I remember, painfully, one which started ignoring volatile when you turned the optimiser on, a nasty surprise....).
Any non-static global variable, regardless of type, has to be preserved by the compiler even if you don't use it. The compiler has no way to know that it isn't used from another file. A compiler would have to be unusably broken to get that wrong.

The linker however is a different matter. You usually tell the linker to discard unused symbols when building embedded stuff to keep the size down. Most linkers also have an option to preserve specific symbols or those matching a pattern even if they're not used. That's there specifically for this use case.
 

Offline NivagSwerdna

  • Super Contributor
  • ***
  • Posts: 2497
  • Country: gb
Re: Void cast as a function
« Reply #20 on: August 01, 2017, 05:14:40 pm »
You don't need to use lint (anyone still uses that?). You will get similar warning about unused function parameters or variables from GCC and Clang by default if you use the
-Wall option, as you probably should. In plenty of places it is against the policy to turn the warnings off so hacks like the above are common to silence spurious warnings

https://doc.micrium.com/display/osiiidoc/MISRA+C%3A2012
 

Offline Kjelt

  • Super Contributor
  • ***
  • Posts: 6504
  • Country: nl
Re: Void cast as a function
« Reply #21 on: August 01, 2017, 06:31:35 pm »
IMO it is worse to use these kind of "bogus" compiler satisfying code then to disable a certain warning for Lint around the cause, so if one line throws a warning put a disable and re-enable around it with in the comments the reason why you disabled the Lint warning for that line. At least any following programmer will understand what has been done and why.
 

Offline Yansi

  • Super Contributor
  • ***
  • Posts: 3893
  • Country: 00
  • STM32, STM8, AVR, 8051
Re: Void cast as a function
« Reply #22 on: August 01, 2017, 09:05:19 pm »
It's all about suppressing warnings. Back in the distant past it was surprisingly difficult to suppress the unused variable warning without actually generating any code. So people came up with nonsense like this. It does nothing, just ignore it.
These days compilers are smart enough to handle more obvious ways of suppressing the warning. Or you just don't name the argument and that avoids the warning.
True, still compilers today are so smart they throw away unused strings of for instance program version information.
I always add a string at the start of my code with the ProductName, SVN commitnr, Date and compiler version.
Why? Because when I read out a uC I can directly see if it is the correct code for the product and which version, it sometimes is a big timesaver.

When I don't use the pointer to that string somewhere in the code the compiler even in non-optimized mode will just throw it away, but then it is not a great compiler :D
So something like this could also be used for that purpose.

Declare the string volatile const. Won't get thrown out then.
 

Offline ejeffrey

  • Super Contributor
  • ***
  • Posts: 3786
  • Country: us
Re: Void cast as a function
« Reply #23 on: August 02, 2017, 03:52:49 am »
True, still compilers today are so smart they throw away unused strings of for instance program version information.
I always add a string at the start of my code with the ProductName, SVN commitnr, Date and compiler version.
Why? Because when I read out a uC I can directly see if it is the correct code for the product and which version, it sometimes is a big timesaver.

When I don't use the pointer to that string somewhere in the code the compiler even in non-optimized mode will just throw it away, but then it is not a great compiler :D
So something like this could also be used for that purpose.

The reason turning off optimization doesn't stop this is because it isn't the compiler doing it, it is the linker (with help from the compiler of course).  If you compile with -fdata-sections it will put each global variable and constant in its own section of the object file (assuming ELF object format or similar).  The linker then does garbage collection and eliminates unused sections from the output.  You can fix this by using KEEP in your linker script, or an equivalent __attribute__.

This is completely different from how unused local variables are eliminated by compiler optimization.  Global variables and functions are placed by the linker at compiler time.  Automatic variables are allocated at runtime.
 

Offline Kjelt

  • Super Contributor
  • ***
  • Posts: 6504
  • Country: nl
Re: Void cast as a function
« Reply #24 on: August 02, 2017, 07:35:50 am »
Yes I think you guys are right, it is the linker, should have known better.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf