Void cast as a function - Page 1
Forum section: electronics / general

author: blueskull
author_rank: Unknown
timestamp: August 01, 2017, 07:41:40 am
content: 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?

author: Yansi
author_rank: Super Contributor
timestamp: August 01, 2017, 08:31:28 am
content: Probably only really to mute the unused warning.This is how the "unused" macro is usually done:Code:#define UNUSED(x) ((void)(x))

author: legacy
author_rank: Super Contributor
timestamp: August 01, 2017, 09:14:28 am
content: 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.

author: newbrain
author_rank: Super Contributor
timestamp: August 01, 2017, 10:33:49 am
content: Quote from: legacy on August 01, 2017, 09:14:28 amHow 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?

author: mib
author_rank: Contributor
timestamp: August 01, 2017, 11:04:10 am
content: 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.

author: Kjelt
author_rank: Super Contributor
timestamp: August 01, 2017, 11:15:34 am
content: Quote from: mib on August 01, 2017, 11:04:10 amTrue, 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 compilerSo something like this could also be used for that purpose.

author: glarsson
author_rank: Frequent Contributor
timestamp: August 01, 2017, 11:18:12 am
content: Quote from: legacy on August 01, 2017, 09:14:28 amIf 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.

author: grumpydoc
author_rank: Super Contributor
timestamp: August 01, 2017, 11:36:12 am
content: Quote from: blueskull on August 01, 2017, 07:41:40 amAs 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.Quote from: glarsson on August 01, 2017, 11:18:12 amLargely 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)(); I think - bit rusty on such arcanery.NBhttps://cdecl.org/is *very* useful for figuring out the syntax for complex declarations and casts.

author: Jeroen3
author_rank: Super Contributor
timestamp: August 01, 2017, 11:54:05 am
content: Quote from: ISO/IEC 9899:TC3It makes the compiler ignorey.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 standardsQuote from: grumpydoc on August 01, 2017, 11:36:12 amCool.

author: dmills
author_rank: Super Contributor
timestamp: August 01, 2017, 12:18:18 pm
content: Quote from: Kjelt on August 01, 2017, 11:15:34 amIf it does that with something likeCode: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 handCode: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.

author: helius
author_rank: Super Contributor
timestamp: August 01, 2017, 12:31:52 pm
content: 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.

author: Kjelt
author_rank: Super Contributor
timestamp: August 01, 2017, 12:34:45 pm
content: Quote from: dmills on August 01, 2017, 12:18:18 pmIt 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.

author: grumpydoc
author_rank: Super Contributor
timestamp: August 01, 2017, 12:41:06 pm
content: Quote from: Kjelt on August 01, 2017, 12:34:45 pmNo, 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

author: Kjelt
author_rank: Super Contributor
timestamp: August 01, 2017, 12:52:20 pm
content: Ah yeah you're correct

author: legacy
author_rank: Super Contributor
timestamp: August 01, 2017, 02:37:19 pm
content: Code:#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.

author: NivagSwerdna
author_rank: Super Contributor
timestamp: August 01, 2017, 03:19:14 pm
content: 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.

author: janoc
author_rank: Super Contributor
timestamp: August 01, 2017, 04:18:49 pm
content: Quote from: NivagSwerdna on August 01, 2017, 03:19:14 pmYou 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

author: NivagSwerdna
author_rank: Super Contributor
timestamp: August 01, 2017, 04:31:51 pm
content: Quote from: janoc on August 01, 2017, 04:18:49 pmI believe the OP is an archaeologist, I was trying to explain the behavior of our ancestors.(*explain)();

author: lujji
author_rank: Contributor
timestamp: August 01, 2017, 05:04:38 pm
content: 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:#define ADCR (*(volatile uint8_t *)(0xDEFEC8))(void) ADCR; // read access

author: mib
author_rank: Contributor
timestamp: August 01, 2017, 05:10:48 pm
content: Quote from: dmills on August 01, 2017, 12:18:18 pmAny 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.

author: NivagSwerdna
author_rank: Super Contributor
timestamp: August 01, 2017, 05:14:40 pm
content: Quote from: janoc on August 01, 2017, 04:18:49 pmhttps://doc.micrium.com/display/osiiidoc/MISRA+C%3A2012

author: Kjelt
author_rank: Super Contributor
timestamp: August 01, 2017, 06:31:35 pm
content: 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.

author: Yansi
author_rank: Super Contributor
timestamp: August 01, 2017, 09:05:19 pm
content: Quote from: Kjelt on August 01, 2017, 11:15:34 amDeclare the string volatile const. Won't get thrown out then.

author: ejeffrey
author_rank: Super Contributor
timestamp: August 02, 2017, 03:52:49 am
content: Quote from: Kjelt on August 01, 2017, 11:15:34 amThe 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.

author: Kjelt
author_rank: Super Contributor
timestamp: August 02, 2017, 07:35:50 am
content: Yes I think you guys are right, it is the linker, should have known better.

author: ejeffrey
author_rank: Super Contributor
timestamp: August 02, 2017, 07:52:27 pm
content: Quote from: Yansi on August 01, 2017, 09:05:19 pmYes it will, or at least it is allowed to. Volatile guarantees that reads and writes to a variable will not be eliminated or reordered. It does not prevent an unreferenced constant from being eliminated by the linker.

author: Jeroen3
author_rank: Super Contributor
timestamp: August 02, 2017, 09:43:56 pm
content: Quote from: ejeffrey on August 02, 2017, 07:52:27 pmThis is the only correct answer. Volatiles will be read and/or writteneach expression.You would need a storage duration specifier, likestaticorregister. But even those are unsafe when the linker is cleaning unreferenced objects.

author: danadak
author_rank: Super Contributor
timestamp: August 03, 2017, 12:34:28 am
content: Holy crap, programming since 1972 once I saw this prior post Iam leaving the programming profession.....QuoteI wish I had know about this sitehttps://cdecl.org/years ago, thanks grumpydoc.Regards, Dana.

author: Hypernova
author_rank: Unknown
timestamp: August 03, 2017, 04:45:29 am
content: Quote from: grumpydoc on August 01, 2017, 11:36:12 amIt will also be perfectly legal for your team members to stone you to death if you try to put this in production code. The judge will surely acquit them.It's done in the way it is in the op due to the code base being multi-platform, so you can't rely on a common compiler command to flag it as unused.

author: janoc
author_rank: Super Contributor
timestamp: August 03, 2017, 09:49:24 am
content: Quote from: legacy on August 01, 2017, 05:21:09 pmMy bad, you are right, of course. I was looking at it through the prism of a software guy where tools like Lint have been largely rendered unnecessary by the compilers getting better, rendering linting largely redundant. Embedded dev and fields where special vendor toolchains are needed are a different matter, those compilers can really compile all sorts of dangerous crap without a peep of a warning.

author: janoc
author_rank: Super Contributor
timestamp: August 03, 2017, 09:53:23 am
content: Quote from: Kjelt on August 01, 2017, 06:31:35 pmThat isn't always an option. And littering the code with stuff like (Visual Studio):#pragma warning( push )#pragma warning( disable : 4101)// Your function#pragma warning( pop )+ comment explaining which warning is being suppressed is not really more readable vs that one liner. The comment explaining the construct probably would be still a good idea.

author: grumpydoc
author_rank: Super Contributor
timestamp: August 03, 2017, 10:52:10 am
content: Quote from: Hypernova on August 03, 2017, 04:45:29 amTypedef is your friendQuoteIt's still an ugly hack whatever the excuse.

author: ejeffrey
author_rank: Super Contributor
timestamp: August 04, 2017, 02:55:03 am
content: Quote from: grumpydoc on August 01, 2017, 11:36:12 amThis is pretty far off topic, but not really. Casting between function and data pointers is not defined in C in any useful way. The compiler will let you do it because it might work on your some platforms. Even by the standards of C's weak guarantees on the behavior of casts, converting between function and data pointers is fraught with peril.Code and data pointers don't have to be part of the same address space. You could be on a harvard architecture, you could have segmentation, and even on modern architectures that don't have those problems, it is possible that virtual memory protection will prevent you from executing from a data region or reading from a code region. Furthermore, some architectures poke extra bits into function pointers. Some 64 bit systems put extra bits in the otherwise unused high bits to mitigate stack overflows. ARM uses the low bit to determine whether the target is a arm or thumb instruction set. So even things that look like they should work will not. For instance, if you know the physical address of a function, store it as an integer, then cast to a function pointer and call it, you will get a fault on an ARM Cortex because it only supports the thumb instruction set and needs the low order bit to be one. Likewise, if you try to convert a function pointer to a void * and memcpy it that would fail for the same reason. Function and data pointers don't even have to be the same size: back in the DOS days, you could use a memory model where data used far pointers and code used near pointers (or vice versa). In this case, even something innocuous like casting a function pointer to void* and back may lose information and fail. All C guarantees is that you can cast a function pointer to another type and back losslessly.

author: NorthGuy
author_rank: Super Contributor
timestamp: August 04, 2017, 03:24:51 am
content: Quote from: ejeffrey on August 04, 2017, 02:55:03 amThat is true, but it might be useful - for example you can generate code and then execute it. Of course it'll be platform dependent, but still ...

author: orin
author_rank: Frequent Contributor
timestamp: August 04, 2017, 04:17:41 am
content: Quote from: janoc on August 03, 2017, 09:53:23 amThe Microsoft compilers also have#pragma warning(suppress: 4101)which disables 4101 for the next line only. Though you have to put it the line before the line where compiler detects the warning... which might not be the line you think.It's not too bad. I always turn _all_ warnings on and insist on at least _understanding_ them before disabling or suppressing them. Probably less than one in one hundred of such warnings is 'real', but reviewing a hundred or so warnings takes much less time than tracking down that mystery bug...

author: janoc
author_rank: Super Contributor
timestamp: August 04, 2017, 04:46:34 pm
content: Quote from: orin on August 04, 2017, 04:17:41 amSuppress is actually a fairly bad idea that could give you some nasty surprises, especially if the code contains macros or you are trying to write portable code:https://visualstudio.uservoice.com/forums/121579-visual-studio-ide/suggestions/2339300--pragma-warning-suppress-should-operate-on-the-ne

author: janoc
author_rank: Super Contributor
timestamp: August 04, 2017, 04:51:51 pm
content: Quote from: NorthGuy on August 04, 2017, 03:24:51 amThat is a sure fire way to have the program blow up in your face on any platform that blocks execution from data segments. Like 'NX' bit on Intel, ARM has now a similar flag too. And, of course, any Harward-style platform.Please, don't do this. There are extremely few situations where this sort of hack should be required. Even if you are implementing something like a JIT there are proper ways to do it that don't require such casts.

author: Kjelt
author_rank: Super Contributor
timestamp: August 04, 2017, 05:17:00 pm
content: Quote from: janoc on August 04, 2017, 04:46:34 pmIt depends on the alternatives. There is no Utopia where all warnings esp from Lint can be solved in a nice clean way.If your coding guidelines do not allow warnings you have little choice.Another alternative to leave hundreds of warnings than chance is high you will never notice any new serious warning.So yeah although I agree in principle, the hard reality and possible choices do not always go hand in hand with studybook theory.

author: janoc
author_rank: Super Contributor
timestamp: August 04, 2017, 10:24:44 pm
content: Quote from: Kjelt on August 04, 2017, 05:17:00 pmYou have misunderstood me. Warnings certainly need to be fixed or silenced if spurious.However using the suppress pragma in Visual Studio is not the best method of doing that and will not even work if you use #ifdef/#endif macros to provide support for more than one compiler. In that case I find that dummy cast to void pointer a lesser evil - no messy #ifdefs are needed, no need to push/pop warning settings neither, the entire thing is one line. Maybe put a comment next to it explaining that it is to suppress a spurious warning in case someone wonders. It is much more readable.

author: Kjelt
author_rank: Super Contributor
timestamp: August 05, 2017, 09:14:29 am
content: How about the hundreds of other possible warnings Lint can produce?Are you going to write inline code for those too?If you use a tool and that tool has some unwanted side effects and the tool has an option built in to circumvent that side effect, i say use it or use a different tool.

author: janoc
author_rank: Super Contributor
timestamp: August 05, 2017, 09:46:25 am
content: Quote from: Kjelt on August 05, 2017, 09:14:29 amIf you are using an actual Lint, that one has a configuration file to suppress warnings. And for any normal codebase with reasonably set up tooling suppressing warnings is an exception, not a rule.And yes, if a certain warning has to stay on, I am totally happy to write a (commented) cryptic oneliner to avoid a spurious warning than to spew something like this all over the codebase whenever there is one:#ifdef _MSC_VER#pragma warnings (push)#pragma warning(disable:XXXX)#endif#ifdef __GNUC__// do something to disable the warning in GCC#endif#ifdef __clang__// do something for Clang#endifthe offending line of code here;#ifdef _MSC_VER#pragma warning (pop)#endif#ifdef __GNUC__// restore the GCC warning#endif#ifdef __clang__// restore the Clang warning#endif....And this handles only Visual C++, GCC and Clang - all very common if you are building for Windows (MSVC + GCC), Mac (Clang) and Linux (GCC+Clang). Wonderfully readable code, isn't it?Once you have to deal with this, you will start to appreciate those one-liners, especially for stupid stuff like unused function arguments or local variables.

author: orin
author_rank: Frequent Contributor
timestamp: August 05, 2017, 08:27:15 pm
content: Don't forget the existence of #elif.#if defined(_MSC_VER)// MSC stuff#elif defined(__GNUC__)// GNU stuff#elif defined(__clang__)// clang stuff#endif

author: Kjelt
author_rank: Super Contributor
timestamp: August 05, 2017, 10:21:43 pm
content: QuoteAh yes in that case it is insane, we only have a single c compiler and a lint disable and re-enable is each one comment line so that is not as bad. To each its own.

author: janoc
author_rank: Super Contributor
timestamp: August 07, 2017, 11:37:15 am
content: Quote from: orin on August 05, 2017, 08:27:15 pmYay, way to miss the point ...

author: orin
author_rank: Frequent Contributor
timestamp: August 07, 2017, 05:09:58 pm
content: Quote from: janoc on August 07, 2017, 11:37:15 amNo, I didn't miss the point. I merely pointed out a less verbose method of handling _anything_ that's compiler dependent.Nothing says that the 'tricks' presented work on all compilers and wouldn't necessitate an #if ... #endif stream somewhere. The unused variable problem is better solved in other ways - like not declaring a local variable you aren't going to use or not naming unused function parameters or a compiler dependent UNUSED(x) macro.

author: janoc
author_rank: Super Contributor
timestamp: August 09, 2017, 06:30:04 pm
content: Quote from: orin on August 07, 2017, 05:09:58 pmThe unused function arguments or local variables usually arise in situations when there is a defined interface and there are several variants of the code implementing it, either configuration or machine dependent. So not declaring them may not be an option, the same as not naming unused function parameters - they could be used in another build variant or the naming even mandated by a code style policy (very likely, given that we are talking an environment where these warnings have to be enabled in the first place).Your idea with the #if/#elif/... chain is less verbose however, it is more problematic from the code management point of view. It saves a few lines at the expense of risking merge errors (e.g. when using source revision control), whenever a new supported architecture is being added. Each addition necessitates changing the last #endif to #elif, risking introducing build errors. The #ifdef/#endif pairs are more self contained, that's why you will see these more often.