• Attributes on functions/variables (static, in particular)

    From Kenny McCormack@3:633/10 to All on Fri Nov 14 14:20:47 2025
    Wow! An actual on-topic C question. Don't see much of that around here
    these days...

    My question has to do specifically with "static" - as seen below - but also
    in general with these sorts of attributes of functions and variables.

    Suppose I have code like this:

    /* static */ char *foo(int);
    int somefun(...) { code that uses foo() }
    ...

    static char *foo(int bar) { definition of foo() }

    Is foo() static or not? Does the order matter? Suppose the first reference
    to foo() had static and the second one didn't?

    Or is it a syntax error to have different declarations/definitions like
    this? What if they occur (as they usually will do) in different files (TUs) ?

    Note that this came up in real life - I had to change a function that had
    been static to non-static and was concerned about what would happen if I
    didn't change it in every place. I didn't do much testing and decided to
    post here instead.

    --
    On the subject of racism being depicted in the media, the far right and the far left have
    met up in agreement (sort of like how plus infinity meets up with minus infinity).
    The far left doesn't want it, because they are afraid it will make people racist.
    The far right doesn't want it, because they are afraid it will make people feel bad about being racist.

    --- PyGate Linux v1.5
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Richard Harnden@3:633/10 to All on Fri Nov 14 17:31:14 2025
    On 14/11/2025 14:20, Kenny McCormack wrote:
    Wow! An actual on-topic C question. Don't see much of that around here these days...

    My question has to do specifically with "static" - as seen below - but also in general with these sorts of attributes of functions and variables.

    Suppose I have code like this:

    /* static */ char *foo(int);
    int somefun(...) { code that uses foo() }
    ...

    static char *foo(int bar) { definition of foo() }

    I don't think I've ever had a non-static function prototype early in my
    code, then it being used, and then it's static defininition later.

    I've always had the static definition just before it's used and no
    prototype, static or not, at all. Dunno if that's normal, or just the
    style I learnt.


    Is foo() static or not? Does the order matter? Suppose the first reference to foo() had static and the second one didn't?
    nm should lable exported functions as 'T', and static functions as 't'
    ... I don't know if that helps you.


    Or is it a syntax error to have different declarations/definitions like
    this? What if they occur (as they usually will do) in different files (TUs) ?

    static would make it private to the TU.

    If you remove that and there are multiple foos in different TUs, then
    it's easy to imagine how things could get confused - to the point that
    it'll refuse to link.


    Note that this came up in real life - I had to change a function that had been static to non-static and was concerned about what would happen if I didn't change it in every place. I didn't do much testing and decided to post here instead.

    Probably you need to wait for a followup from someone who knows what
    they're talking about :)




    --- PyGate Linux v1.5
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Kaz Kylheku@3:633/10 to All on Fri Nov 14 17:41:31 2025
    On 2025-11-14, Kenny McCormack <gazelle@shell.xmission.com> wrote:
    Wow! An actual on-topic C question. Don't see much of that around here these days...

    My question has to do specifically with "static" - as seen below - but also in general with these sorts of attributes of functions and variables.

    Suppose I have code like this:

    /* static */ char *foo(int);

    If this is the first declaration of foo, foo gets external linkage.

    int somefun(...) { code that uses foo() }
    ...

    static char *foo(int bar) { definition of foo() }

    This is now giving foo internal linkage in the same scope, which
    is UB.

    In C99, this was given in 6.2.2, paragraph 7:

    "If, within a translation unit, the same identifier appears with both
    internal and external linkage, the behavior is undefined.'

    It's in the same numbered section and paragraph in the n3302 draft.

    Obviously, this situation is easily diagnosable, in principle.

    Is foo() static or not?

    Since the behavior is not defined, there need not even be a
    translated unit.

    Does the order matter? Suppose the first reference
    to foo() had static and the second one didn't?

    The order matters because a file scope declaration without
    a storage class specifier, or even with the storage class
    specifier "extern", inherits the previously declared linkage.

    "extern" or the lack of specifier does not mean "give my identifier
    externa linkage" but "give my identifier the previously declared
    linkage, or else external".

    "static" doesn't do that; it asserts internal linkage.

    Or is it a syntax error to have different declarations/definitions like
    this? What if they occur (as they usually will do) in different files (TUs) ?

    Note that this came up in real life - I had to change a function that had

    I've on rare occasion seen it in the past that GCC warned about an
    identifier being given both linkages.

    It does not require contorted or contrived circumstances in order
    to occur.

    It's suprising that in all the decades that 6.2.2 Paragrah 7 text
    has existed, complex features have been added to the language.

    Yet the simple matter of converting that to a constraint
    has somehow esacaped attention.

    Any half decent compiler should be diagnosing it already,
    making it practically a no-op to implement the requirement
    for that compiler?

    Maybe there are situations in which the situation happens
    (same identifeir appears with both linkages), which are not easily
    diagnosable. I can't think of any at the moment, and
    that wouldn't preclude having both a constraint violation for
    the diagnosable cases, and leaving the 6.2.2/p7 text.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- PyGate Linux v1.5
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Kaz Kylheku@3:633/10 to All on Fri Nov 14 18:05:17 2025
    On 2025-11-14, Richard Harnden <richard.nospam@gmail.invalid> wrote:
    I don't think I've ever had a non-static function prototype early in my code, then it being used, and then it's static defininition later.

    I've always had the static definition just before it's used and no prototype, static or not, at all. Dunno if that's normal, or just the
    style I learnt.

    Things happen in big(ger) projects. You think that the identifer
    foo_parse is free for the taking. You put it into the "foo.h" header of
    the foo module as an external declaration. Oops! There is already a
    static helper called foo_parse in foo.c. Or somewhere else.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- PyGate Linux v1.5
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From David Brown@3:633/10 to All on Sat Nov 15 10:55:06 2025
    On 14/11/2025 15:20, Kenny McCormack wrote:
    Wow! An actual on-topic C question. Don't see much of that around here these days...

    My question has to do specifically with "static" - as seen below - but also in general with these sorts of attributes of functions and variables.

    Suppose I have code like this:

    /* static */ char *foo(int);
    int somefun(...) { code that uses foo() }
    ...

    static char *foo(int bar) { definition of foo() }

    Is foo() static or not? Does the order matter? Suppose the first reference to foo() had static and the second one didn't?

    Or is it a syntax error to have different declarations/definitions like
    this? What if they occur (as they usually will do) in different files (TUs) ?

    Note that this came up in real life - I had to change a function that had been static to non-static and was concerned about what would happen if I didn't change it in every place. I didn't do much testing and decided to post here instead.


    As Kaz said, it is UB to have a non-static declaration and then a later
    static definition or declaration. Compilers are likely to treat that as
    a hard error, though I am not sure that is strictly required by the
    standards. But it means you are unlikely to be able to make this kind
    of mistake without noticing!

    (Slightly bizarrely, you are allowed to have a "static" declaration
    followed later by an explicitly "extern" declaration or definition - the result is static (internal) linkage. I'd far rather that inconsistency
    was an error, but I guess it all goes back to historical reasons.)

    It might not help when you are dealing with existing code, and might not
    be ideal for all projects and code organisations, but I think a good
    practice is that all "exports" for a file should be in "file.h". These function declarations will obviously not be "static" (except perhaps
    small static inline functions declared in the header), and can be
    explicitly marked "extern" if you like. Then inside "file.c", you
    include "file.h" and every function is either defined as "static" or
    matches one of the exported functions in the header. I am not a fan of forward static declarations unless they are absolutely necessary - they
    are just a way to add effort to code writing and maintenance and a big
    risk of inconsistency. But if you /do/ have a forward declaration in "file.c", then you know it must always be "static".

    And if you use gcc, you can enforce this with "-Wmissing-declarations" "-Wredundant-decls".


    --- PyGate Linux v1.5
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)