Does the C++ standard mandate that C-linkage functions are `noexcept`?

Marc van Leeuwen's answer is correct: looking up the current working draft, nothing seems to mandate that that functions declared extern "C" be implicitly noexcept. It is interesting to know that standard C++ prohibits that the C standard library functions within the C++ standard library from throwing. Those function are themselves usually specified as extern "C" (but this is implementation defined see 16.4.3.3-2). Take a look at clause 16.4.6.13 [Restriction on exception handling] and the accompanying footnotes 174 and 175.

Functions from the C standard library shall not throw exceptions [footnote 174] except when such a function calls a program-supplied function that throws an exception.[footnote 175]

Footnote 174:

  1. That is, the C library functions can all be treated as if they are marked noexcept. This allows implementations to make performance optimizations based on the absence of exceptions at runtime.

Footnote 175:

The functions qsort() and bsearch() ([alg.c.library]) meet this condition.

That being said, following the same strategy as the standard library is usually a good design guideline, and for reasons mention in Marc van Leeuwen's answer I think it's a good idea that user defined extern "C" functions be also specified with noexcept, unless it is handed a pointer to C++ function as callback argument like qsort and the likes. I made a small experiment with clang10, gcc10 with the following code:

#include <cstring>
#include <cstdlib>
#include <iostream>

extern "C" int cmp(const void* lhs, const void* rhs) noexcept;
extern "C" int non_throwing();

int main()
{
    constexpr int src[] = {10, 9, 8, 7, 6, 5};
    constexpr auto sz = sizeof *src;
    constexpr auto count = sizeof src / sz;

    int dest[count];
    int key = 7;

    std::cout << std::boolalpha
    // noexcept is unevaluated so no worries about UB here
        << "non_throwing: " << noexcept(non_throwing()) << '\n'
        << "memcpy: " << noexcept(std::memcpy(dest, src, sizeof dest)) << '\n'
        << "malloc: "<< noexcept(std::malloc(16u)) << '\n'
        << "free: " << noexcept(std::free(dest)) << '\n'
        << "exit: " << noexcept(std::exit(0)) << '\n'
        << "atexit: " << noexcept(std::atexit(nullptr)) << '\n'
        << "qsort: " << noexcept(std::qsort(dest, count, sz, cmp)) << '\n' // should fail
        << "bsearch: " << noexcept(std::bsearch(&key, dest, count, sz, cmp)) << '\n'; // should fail
}

The output for both gcc10 and clang10 was:

non_throwing: false
memcpy: true
malloc: true
free: true
exit: true
atexit: true
qsort: false
bsearch: false

For msvc142, if compiled with /EHsc then all outputs are obviously true. And with /EHs, all outputs are false, which makes the 'c' in /EHsc necessary for strict conformance.


As far as I can tell there is no guarantee that function defined with "C" linkage will not throw exceptions. The standard allows a C++ program both to call an external function with "C" language linkage, and to define functions written in C++ that have "C" language linkage. Therefore there is nothing to prevent a C++ program from calling a function with "C" language linkage that is actually written in C++ (in another compilation unit maybe, although even this is not necessary). It would be a strange thing to do, but it is hard to rule out. Also I don't see where in the standard it says that doing so would lead to undefined behaviour (in fact since the Standard cannot define the behaviour of function not written in C++, this would be the only usage where there is not formally undefined behaviour).

As a consequence I think it would be an error to assume that "C" linkage implies noexcept.


Um, I assume extern "C" just use C-linkage, not C function. It prevents the compiler from doing C++ name mangling.

More directly - Suppose this code.

// foo.cpp
extern "C" void foo()
{
    throw 1;
}

// bar.cpp
extern "C" void foo();
void bar()
{
    try
    {
        foo();
    }
    catch (int)
    {
        // yeah!
    }
}