Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Priority of static functions in C

Let's say that you have a file File1.c with a static function Foo and that the function is called within File1.c. Also, in another file (File2.c), you have another Foo function which is not static. I understand that the static function is not visible outside of the file where it is declared and is actually invisible for the linker.

But does that mean that internal calling of Foo function in File1.c is always resolved during compilation?

Are there cases where Foo calling in File1.c can be linked to the global Foo function of File2.c?

like image 475
Jujuke Avatar asked May 14 '19 15:05

Jujuke


1 Answers

Summary

Once you define a static function foo within a translation unit, foo refers to that function for the rest of the translation unit, except that it can be hidden by a non-function (such as an object or type definition) named foo for part of the translation unit. It will not link to an external function named foo.

By tinkering with declarations as explained below, an identifier could in theory refer to a function from another translation unit after a static declaration of the same name in this translation unit. Unfortunately, the behavior is not defined by the C standard, due to C 2018 6.2.2 7:

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

That means you cannot rely on the C standard alone to ensure this behavior, but a C implementation could define it as an extension.

Details

These questions are answered by C’s rules for scope and linkage.

Suppose in File1.c we have a static definition of a function:

static int foo(int x) { return x*x; }

Since the identifier foo is declared outside of any function, it has file scope (C 2018 6.2.1 4). This means the identifier foo is visible and designates this function definition for the remainder of File1.c. Also, since static was used, it has internal linkage (6.2.2 3).

There is an exception to the scope. For scopes inside other scopes, such as the block { … } that defines a function inside a file or a block inside a block, a declaration of the same identifier can hide the outer declaration. So let’s consider redeclaring foo inside a block.

In order to refer to a foo defined outside of File1.c, we would need to declare foo with external linkage, so that this new foo can be linked to the externally defined foo. Is there a way to do that in C?

If we try to declare extern int foo(int x); inside a block, then 6.2.2 4 applies:

For an identifier declared with the storage-class specifier extern in a scope in which a prior declaration of that identifier is visible, if the prior declaration specifies internal or external linkage, the linkage of the identifier at the later declaration is the same as the linkage specified at the prior declaration.

So this declaration would merely redeclare the same foo.

If we declare it without extern, using int foo(int x);, 6.2.2 5 applies:

If the declaration of an identifier for a function has no storage-class specifier, its linkage is determined exactly as if it were declared with the storage-class specifier extern.

So, it seems like we cannot declare a different foo with or without extern. But, wait, we have one more trick. We can make the prior declaration that specifies internal or external linkage invisible by hiding it with a declaration with no linkage. To get a declaration with no linkage, we can declare an object (rather than a function) without extern:

#include <stdio.h>

static int foo(int x) { return x*x; }

void bar(void)
{
    int foo; // Not used except to hide the function foo.
    {
        extern int foo(int x);
        printf("%d\n", foo(3));
    }
}

Since, where extern int foo(int x); appears, the prior declaration of foo with internal linkage is not visible, that first condition in 6.2.2 4 quoted above does not apply, and the remainder of 6.2.2 4 does:

If no prior declaration is visible, or if the prior declaration specifies no linkage, then the identifier has external linkage.

This is “legal” C code. Unfortunately, it is undefined by 6.2.2 7:

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

like image 196
Eric Postpischil Avatar answered Oct 14 '22 08:10

Eric Postpischil