Author Topic: C functions with "volatile" return types - what does it mean?  (Read 2372 times)

0 Members and 1 Guest are viewing this topic.

Offline Nominal Animal

  • Super Contributor
  • ***
  • Posts: 7125
  • Country: fi
    • My home page and email address
Re: C functions with "volatile" return types - what does it mean?
« Reply #25 on: October 08, 2024, 11:07:48 pm »
This is actually a pretty good illustration why typedef'ing pointer types in C can lead to unexpected behaviour.

Let's say you have
    volatile char *example(void);
Because the volatile is on the left side of the asterisk, it applies to the pointed to value.  Writing
    char *volatile example(void);
means the pointer itself is volatile, not the pointed to value.

Now, let's say you have
    typedef  char *  mytype;
    volatile mytype example(void);
Do you know what the volatile refers to this time?

Yes, to the pointer, and not to the pointed-to value.  That is, it is equivalent to
    char *volatile example(void);
and not to
    volatile char *example(void);

If you accept that typedef is not just shorthand, that it isn't equivalent to a preprocessor macro definition (#define mytype char * would behave the exact opposite way), and that it actually, really defines a new type, it makes perfect sense.

It also means that defining an opaque cookie type, say
    typedef  void *   handle;
makes perfect sense: it is an unknown thing you obtain from somewhere and pass along as needed, but can never examine in detail, only as far as its address (and even that in limited ways, because of C's pointer rules).  Even though it is a pointer, it points to void, so you cannot dereference it as-is, either.  Doing stuff like
    const handle h = ...;
in a function makes perfect sense too. It means the cookie, the variable h is const, that us programmers promise to not try and change its value in the current scope.  It says nothing about what it might point to.
 
The following users thanked this post: DiTBho

Online SiliconWizard

  • Super Contributor
  • ***
  • Posts: 15607
  • Country: fr
Re: C functions with "volatile" return types - what does it mean?
« Reply #26 on: October 08, 2024, 11:32:53 pm »
Good points. I personally don't think that the typedef'ing here is confusing - actually it's less confusing than the direct declaration. And yes, typedef defines a new type. I understand why you'd point it out though, as indeed, it looks like many C devs (who unfortunately have very varying levels of skills, as we regularly talk about) do not really understand types, typedef and even the point of defining types (which is even more concerning, but hey.)

C declarations are sure a very fun thing to toy with, if you're up for some fun.

Code: [Select]
volatile char * volatile ptr;

const volatile char * volatile ptr;

We can spice things up.

Code: [Select]
const int * (* const foo[10])(volatile char * const);
It's fun to see that this last declaration makes cdecl.org choke: https://cdecl.org/
But it's valid C.
« Last Edit: October 09, 2024, 06:16:45 am by SiliconWizard »
 

Offline ejeffrey

  • Super Contributor
  • ***
  • Posts: 3984
  • Country: us
Re: C functions with "volatile" return types - what does it mean?
« Reply #27 on: October 09, 2024, 04:55:10 am »
I don't see how and why it would be different for C++ users vs. C users. Why really?

Mostly that typical C++ has a lot more thin wrapper functions that are extremely amenable to inlining and not all of them are defined in headers.  Also, it gives more chances for devirtulaization, which is rarely possible or necessary in C.
 

Offline DiTBho

  • Super Contributor
  • ***
  • Posts: 4287
  • Country: gb
Re: C functions with "volatile" return types - what does it mean?
« Reply #28 on: October 10, 2024, 02:44:58 am »
Optimization is your friend, not your enemy.

In 2005 Shirley Manson sang "Sex Is Not the Enemy" (Bleed Like Me)
dressing a lovely "I (L) Nerds" t-shirt

2024, that's a new hype for a t-shirt pretty nerdy t-shirt (Youtube Channel)  ;D
The opposite of courage is not cowardice, it is conformity. Even a dead fish can go with the flow
 

Offline TheCalligrapher

  • Regular Contributor
  • *
  • Posts: 159
  • Country: us
Re: C functions with "volatile" return types - what does it mean?
« Reply #29 on: October 10, 2024, 04:57:16 am »
Code: [Select]
const int * (* const foo[10])(volatile char * const);
It's fun to see that this last declaration makes cdecl.org choke: https://cdecl.org/
But it's valid C.

A minimized example would be

Code: [Select]
void foo(char *const);
cdecl.org apparently rejects top-level `const` on function arguments for some reason.
 

Offline Nominal Animal

  • Super Contributor
  • ***
  • Posts: 7125
  • Country: fi
    • My home page and email address
Re: C functions with "volatile" return types - what does it mean?
« Reply #30 on: October 10, 2024, 05:25:08 am »
cdecl.org apparently rejects top-level `const` on function arguments for some reason.
As far as I understand, in function declarations parameter constness is irrelevant.  That is, declaration
    int sum(int, int);
for implementation
    int sum(int const x, int const y) { return x+y; }
is perfectly valid, and in both cases the variables are of type int; the constness is only relevant to the function implementation (body).
 

Online SiliconWizard

  • Super Contributor
  • ***
  • Posts: 15607
  • Country: fr
Re: C functions with "volatile" return types - what does it mean?
« Reply #31 on: October 10, 2024, 05:36:45 am »
I didn't go as far as trying to debug cdecl, but now that you pinpointed that, a few tests show that it actually rejects only const pointers as function arguments, but not const arguments other than const pointers, probably due to the odd-ish grammar that it doesn't handle correctly. So:

void foo(const int *)
is ok with cdecl, but not:
void foo(int * const)

No need to try and find a justification IMHO, it's clearly just a bug, because it will otherwise happily analyze const arguments in all other cases, like this:
void foo(const int)

and otherwise it's also happy with this:
int * const foo

So, it's just const pointers in the context of function arguments that it rejects.

Regarding relevance, sure it doesn't bring anything for function prototypes only, except of course pointers to const (const xxx *), and is meaningful only inside a function's body as NA mentioned, but that's obviously not a rationale for rejecting that. In terms of code style, writing a prototype that is different from the declaration itself would be quite odd.

For people who would happen to have some time to kill, they provide the source code: https://github.com/ridiculousfish/cdecl-blocks
inspecting the Yacc grammar file may be enough to figure it out. Or not.
« Last Edit: October 10, 2024, 05:39:56 am by SiliconWizard »
 

Offline TheCalligrapher

  • Regular Contributor
  • *
  • Posts: 159
  • Country: us
Re: C functions with "volatile" return types - what does it mean?
« Reply #32 on: October 10, 2024, 05:41:52 am »
cdecl.org apparently rejects top-level `const` on function arguments for some reason.
As far as I understand, in function declarations parameter constness is irrelevant.  That is, declaration
    int sum(int, int);
for implementation
    int sum(int const x, int const y) { return x+y; }
is perfectly valid, and in both cases the variables are of type int; the constness is only relevant to the function implementation (body).

Firstly, it is "irrelevant" in a sense that it does not affect function type, i.e. it does not affect external behavior of the function. The top-level cv-qualifiers applied to the parameters are ignored when determining function type. For example, a pointer of `void (*)(int)` type can be initialized with address of `void (const int)` function without an explicit cast. The types are the same. At the same time, that `const` is perfectly relevant to the internal implementation of the function: a parameter declared `const` is indeed seen as `const` inside the function.

Secondly, relevant or irrelevant, it is still a perfectly valid C declaration. It is weird that cdecl reports it as a "syntax error". It isn't.

P.S. BTW, I remember that a while ago I tried to find the legal wording in the standard that would state that `int sum(int, int);` prototype is a proper match to `int sum(const int, const int) { ... }` definition. Buit I couldn't. I eventually abandoned the question without finding any answer. Although, that was probably about C++, not C...
« Last Edit: October 10, 2024, 05:50:24 am by TheCalligrapher »
 

Offline DiTBho

  • Super Contributor
  • ***
  • Posts: 4287
  • Country: gb
Re: C functions with "volatile" return types - what does it mean?
« Reply #33 on: October 10, 2024, 05:46:49 am »
Secondly, relevant or irrelevant, it is still a perfectly valid C declaration.
It is weird that cdecl reports it as a "syntax error". It isn't.

SierraC: syntax error
AvogetC: syntax error
DiabC: ok
Gcc-v12: ok

four C compilers tried, 50% OK
So, they probably used some compiler that was inclined not to accept that syntax
The opposite of courage is not cowardice, it is conformity. Even a dead fish can go with the flow
 

Offline TheCalligrapher

  • Regular Contributor
  • *
  • Posts: 159
  • Country: us
Re: C functions with "volatile" return types - what does it mean?
« Reply #34 on: October 10, 2024, 06:06:06 am »
Secondly, relevant or irrelevant, it is still a perfectly valid C declaration.
It is weird that cdecl reports it as a "syntax error". It isn't.

SierraC: syntax error
AvogetC: syntax error
DiabC: ok
Gcc-v12: ok

four C compilers tried, 50% OK
So, they probably used some compiler that was inclined not to accept that syntax

What exactly did you compile? Did you try to include a parameter name? E.g. `void foo(int const a)`?

For example, this

Code: [Select]
void foo(int const) {}
is indeed a syntax error in all versions of C before C23. "Classic" C does not permit omitting parameter names in function definitions.
 

Offline Nominal Animal

  • Super Contributor
  • ***
  • Posts: 7125
  • Country: fi
    • My home page and email address
Re: C functions with "volatile" return types - what does it mean?
« Reply #35 on: October 10, 2024, 06:36:46 am »
Firstly, it is "irrelevant" in a sense that it does not affect function type, i.e. it does not affect external behavior of the function. The top-level cv-qualifiers applied to the parameters are ignored when determining function type.
Yep, that was what I tried to say.

At the same time, that `const` is perfectly relevant to the internal implementation of the function: a parameter declared `const` is indeed seen as `const` inside the function.
Exactly.  We are in perfect agreement.

Secondly, relevant or irrelevant, it is still a perfectly valid C declaration. It is weird that cdecl reports it as a "syntax error". It isn't.
Fully agreed.

Apologies if I sounded otherwise; I do think cdecl behaviour here is definitely in error.

BTW, I remember that a while ago I tried to find the legal wording in the standard that would state that `int sum(int, int);` prototype is a proper match to `int sum(const int, const int) { ... }` definition. Buit I couldn't. I eventually abandoned the question without finding any answer. Although, that was probably about C++, not C...
I also tried but couldn't, not for nor against.  On my part, it is more an observation that a specific interpretation of the standard (including const being only meaningful in lvalue-type expressions) is in perfect alignment with all the C compilers I've used.

In terms of code style, writing a prototype that is different from the declaration itself would be quite odd.
Yep, this is exactly why I tend to include the parameter constness even in the declaration in a header file, regardless of what I said above.

Yacc grammar file
When reading .y files I first ask why I need to do this, then I lalr, and my brain turns to bison dung.
 

Offline DiTBho

  • Super Contributor
  • ***
  • Posts: 4287
  • Country: gb
Re: C functions with "volatile" return types - what does it mean?
« Reply #36 on: October 10, 2024, 04:29:21 pm »
What exactly did you compile?
Did you try to include a parameter name?

yup

Code: [Select]
void foo
(
   int* const a
)
{
   ...
}
« Last Edit: October 10, 2024, 04:33:19 pm by DiTBho »
The opposite of courage is not cowardice, it is conformity. Even a dead fish can go with the flow
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf