Skip to main content
Redhat Developers  Logo
  • AI

    Get started with AI

    • Red Hat AI
      Accelerate the development and deployment of enterprise AI solutions.
    • AI learning hub
      Explore learning materials and tools, organized by task.
    • AI interactive demos
      Click through scenarios with Red Hat AI, including training LLMs and more.
    • AI/ML learning paths
      Expand your OpenShift AI knowledge using these learning resources.
    • AI quickstarts
      Focused AI use cases designed for fast deployment on Red Hat AI platforms.
    • No-cost AI training
      Foundational Red Hat AI training.

    Featured resources

    • OpenShift AI learning
    • Open source AI for developers
    • AI product application development
    • Open source-powered AI/ML for hybrid cloud
    • AI and Node.js cheat sheet

    Red Hat AI Factory with NVIDIA

    • Red Hat AI Factory with NVIDIA is a co-engineered, enterprise-grade AI solution for building, deploying, and managing AI at scale across hybrid cloud environments.
    • Explore the solution
  • Learn

    Self-guided

    • Documentation
      Find answers, get step-by-step guidance, and learn how to use Red Hat products.
    • Learning paths
      Explore curated walkthroughs for common development tasks.
    • Guided learning
      Receive custom learning paths powered by our AI assistant.
    • See all learning

    Hands-on

    • Developer Sandbox
      Spin up Red Hat's products and technologies without setup or configuration.
    • Interactive labs
      Learn by doing in these hands-on, browser-based experiences.
    • Interactive demos
      Click through product features in these guided tours.

    Browse by topic

    • AI/ML
    • Automation
    • Java
    • Kubernetes
    • Linux
    • See all topics

    Training & certifications

    • Courses and exams
    • Certifications
    • Skills assessments
    • Red Hat Academy
    • Learning subscription
    • Explore training
  • Build

    Get started

    • Red Hat build of Podman Desktop
      A downloadable, local development hub to experiment with our products and builds.
    • Developer Sandbox
      Spin up Red Hat's products and technologies without setup or configuration.

    Download products

    • Access product downloads to start building and testing right away.
    • Red Hat Enterprise Linux
    • Red Hat AI
    • Red Hat OpenShift
    • Red Hat Ansible Automation Platform
    • See all products

    Featured

    • Red Hat build of OpenJDK
    • Red Hat JBoss Enterprise Application Platform
    • Red Hat OpenShift Dev Spaces
    • Red Hat Developer Toolset

    References

    • E-books
    • Documentation
    • Cheat sheets
    • Architecture center
  • Community

    Get involved

    • Events
    • Live AI events
    • Red Hat Summit
    • Red Hat Accelerators
    • Community discussions

    Follow along

    • Articles & blogs
    • Developer newsletter
    • Videos
    • Github

    Get help

    • Customer service
    • Customer support
    • Regional contacts
    • Find a partner

    Join the Red Hat Developer program

    • Download Red Hat products and project builds, access support documentation, learning content, and more.
    • Explore the benefits

New C features in GCC 13

Explore new C language features in the GNU Compiler Collection 13.1 release

May 4, 2023
Marek Polacek
Related topics:
C, C#, C++Compilers
Related products:
Red Hat Enterprise Linux

    The latest major version of the GNU Compiler Collection (GCC), 13.1, was released in April 2023. Like every major GCC release, this version will bring many additions, improvements, bug fixes, and new features. GCC 13 is already the system compiler in Fedora 38. Red Hat Enterprise Linux (RHEL) users will get GCC 13 in the Red Hat GCC Toolset (RHEL 8 and RHEL 9). It's also possible to try GCC 13 on godbolt.org and similar pages.

    This article describes new features implemented in the C front end; it does not discuss developments in the C language itself. It also doesn’t cover recent changes in the C library itself. If you’re interested in the C++ language and what's supported in recent GCC releases, check out these articles:

    • New C++ features in GCC 13
    • New C++ features in GCC 12
    • New C++ features in GCC 10

    The default C dialect in GCC 13 is -std=gnu17. You can use the -std=c2x or -std=gnu2x command-line options to enable C2X features. We use C2X to refer to the next major C standard version; it is expected to become C23.

    C2X features

    GCC 13 has implemented a host of C2X proposals. This section describes the most interesting ones.

    nullptr

    The nullptr constant first appeared in C++11, described in proposal N2431 from 2007. Its purpose was to alleviate the problems with the definition of NULL, which can be defined in a variety of ways: (void *)0 (a pointer constant), 0 (an integer), and so on. This posed problems for overload resolution, generic programming, etc. While C doesn’t have function overloading, the protean definition of NULL still causes headaches. Consider the interaction of _Generic with NULL: it’s not clear which function will be called because it depends on the definition of NULL:

      _Generic (NULL,
                void *: handle_ptr (),
                int: crash (),
                default: nop ());
    

    Unfortunately, there are less contrived problems in practice. For instance, issues occur with conditional operators or when passing NULL to a variadic function (taking ...): in such a case, applying va_arg to the null argument may crash the program if an unexpected definition of NULL is encountered. GCC 13 implements N3042, which brings nullptr to C. Its type is nullptr_t and is defined in <stddef.h>. In C2X, the following assert therefore passes:

    static_assert (_Generic (nullptr,
       nullptr_t: 1,
       void *: 2,
       default: 0) == 1,
       "nullptr_t was selected");
    

    Enhanced enumerations

    Enhanced enumerations is another feature that first appeared in C++11 via N2347. In C, the underlying type of an enum was not specified in the standard. In practice, the type would be determined based on the values of the enumerators. Typically, the type would be unsigned int, or, if any of the values is negative, int. In any case, the selected type must be capable of holding all of the values of the enum. Given this lacuna in the specification, enums have portability issues. To close this gap, C adopted N2963, adopting the C++ syntax:

    enum E : long long { R, G, B } e;
    static_assert (_Generic (e, long long: 1, default: 0) == 1, "E type");

    It seems worth mentioning, however, that specifying the wrong underlying type may lead to subtle problems.  Consider the following:

    enum F : int { A = 0x8000 } f;
    

    On most platforms, this code will work as expected. The precision of int isn’t guaranteed to be at least 32 bits, however; it can validly be 16 bits, in which case the previous example will not compile. Thus a better variant would be to use one of the types defined in <stdint.h>, for example:

    enum F : int_least32_t { A = 0x8000 } f;
    

    (...) function prototypes

    C, prior to C2X, required that a variable-argument function has a named argument before the ellipsis (...). This requirement was the result of historical baggage and is no longer necessary, so N2975 did away with the requirement. (C++ has always allowed foo(...).)

    void f(int, ...); // OK
    void g(...); // OK in C2X
    

    Note, however, that fn(...) is not an unprototyped function, so it is possible to use the va_start and va_arg mechanism to access its arguments. An unprototyped function has the form void u();.

    Such functions were removed in C2X (see below).

    Type inference with auto

    Type deduction is another feature that first appeared in C++11 via N1984. It is a convenient feature that allows the programmer to use the placeholder auto as the type in a declaration. The compiler will then deduce the variable’s type from the initializer:

    auto i = 42;
    

    It is, however, more than just a convenience feature to save typing a few more characters. Consider:

    auto x = foo (y);
    

    Here, the type of foo (y) may depend on y (foo could be a macro using _Generic), so changing y implies changing the type of x. Using auto in the example above means that the programmer doesn’t have to change the rest of the codebase when the type of y is updated. GCC has offered __auto_type since GCC 4.9, whose semantics is fairly close to C2X auto, though not exactly the same, and appears to have been used mostly in standard headers. Unlike C++, auto must be used plain: it cannot be combined with * or [] and similar. Moreover, auto also doesn’t support braces around the initializer. The auto feature is only enabled in C2X mode. In older modes, auto is a redundant storage class specifier which can only be used at block scope.

    The constexpr specifier

    Yet another feature that first appeared in C++ is the constexpr specifier (see for instance N2235, though C++ constexpr has been greatly expanded since). C constexpr was introduced in N3018, with much more limited functionality. Declaring a variable as constexpr guarantees that the variable can be used in various constant-expression contexts. C requires that objects with static storage duration are initialized with constant expressions. It follows that constexpr variables can be used to initialize objects with static storage duration. Another great advantage of constexpr is that various semantic constraints are checked at compile time. Let’s demonstrate both points with an example (note that you must specify -std=c2x or -std=gnu2x to be able to use constexpr):

    constexpr int i = 12;
    static_assert (i == 12);
    
    struct X {
      int bf : i;
    };
    
    struct S {
      long l;
    };
    
    constexpr struct S s = { 1L };
    static_assert (s.l == 1L);
    constexpr unsigned char q = 0xff + i; // initializer not representable in type of object

    Storage-class specifiers in compound literals

    A compound literal is a way to create unnamed objects that typically have automatic storage duration. Because they are lvalues, it is permitted to take their address:

    int *p = (int []){2, 4}; // p points to the first element of an array of two ints
    const int *q = &(const int){1};
    

    Paper N3038 allows using certain storage-class specifiers (things like constexpr, static, thread_local) in compound literals in C2X mode. This is useful to change the lifetime of the compound literal, or to make it a compound literal constant with the constexpr keyword:

    struct S { int i; };
    void
    f (void)
    {
      static struct S s = (constexpr struct S){ 42 };
    }
    
    int *
    g (void)
    {
      return &(static int){ 42 };
    }
    

    Note that even though typedef, extern, and auto are storage-class specifiers, they are not allowed in compound literals.

    C2X typeof

    C2X standardized typeof, a feature that has been supported as a GNU extension for many years which allows the programmer to get the type of an expression as described here. Along with typeof, C2X also adds typeof_unqual, which additionally removes all qualifiers and _Atomic from the resulting type:

    int i;
    volatile int vi;
    extern typeof (vi) vi; // OK, no conflict
    extern typeof_unqual (vi) i; // OK, no conflict
    

    A minor difference between the GNU version and the standard version is the treatment of the noreturn property of a function: the GNU variant of typeof takes noreturn as part of the type of a pointer to function, but the standard version does not.

    Note that C++11 standardized a similar feature under the name decltype, so sadly we wound up with two names for a nearly identical feature.

    New keywords

    This proposal harmonizes C and C++ further by making alignas, alignof, bool, false, static_assert, thread_local, and true ordinary keywords in C2X mode. Therefore this translation unit will compile OK in C2X mode:

    static_assert (true, "");
    

    This change can break existing code, for example

    int alignof = 42;
    

    will not compile in C2X mode.

    The noreturn attribute

    A further compatibility tweak to bring C and C++ closer together. C11 added the _Noreturn function specifier to signal to the compiler that a function never returns to its caller, but _Noreturn works in C only, so C2X N2764 added a standard [[noreturn]] attribute while simultaneously marking _Noreturn as obsolescent.

    [[noreturn]] void exit (int);
    

    Empty initializer braces

    C2X standardized empty initializer braces ({}) and GCC 13 implements this proposal. Some cases were already supported as a GNU extension (e.g., initializing an array or a structure), but newly it’s possible to use {} to initialize a scalar variable or a variable-length array as well:

    int i = {};
    int arr[10] = {};
    struct S { int i; };
    struct S s = {};
    
    void
    g (void)
    {
      int n = 10;
      int vla[n] = {};
    }
    

    unreachable macro

    C2X brings the unreachable() macro, defined in <stddef.h>, which is a convenient shorthand for the GCC built-in function __builtin_unreachable():

    #include <stddef.h>
    
    int foo (int x)
    {
      if (x < 0)
    unreachable ();
      return x & 1;
    }
    

    Unprototyped functions removed

    Unprototyped functions in C were of the form int foo(), which is a function foo that returns an integer which takes an unspecified number of arguments of unspecified types. This is very dangerous because the compiler can’t perform any checking when such a function is used.

    In C2X, int foo() is equivalent to int foo(void), which is a function foo that returns an integer and takes no arguments.

    New warnings

    The C front end has gained some new warnings in GCC 13. For instance, -Wxor-used-as-pow, which was described in the C++ part of the GCC 13 blog post. There’s a new warning specific for the C front end.

    -Wenum-int-mismatch

    In C, an enumerated type is compatible with char, a signed integer type, or an unsigned integer type, so the following code compiles if the underlying type of enum E is int:

    enum E { l = -1, z = 0, g = 1 };
    int foo(void);
    enum E foo(void) { return z; }
    

    However, as I previously noted, the choice of the underlying type of the enum is implementation-defined. Since the code above is likely a mistake and constitutes a portability problem (the code will not compile if a different type than int is chosen to be the underlying type), GCC 13 implements a new warning which warns about enum/integer type mismatches. For the code above the warning looks like the following:

    q.c:5:10: warning: conflicting types for ‘foo’ due to enum/integer mismatch; have ‘enum E(void)’ [-Wenum-int-mismatch]
    5 |   enum E foo(void) { return z; }
      |         ^~~
    
    q.c:4:7: note: previous declaration of ‘foo’ with type ‘int(void)’
    4 |   int foo(void);
      |       ^~~
    

    Conclusion

    GCC 13 implements many C2X proposals. These proposals align the C and C++ languages a little bit closer to each other by entwining certain features, and make programming in C easier and more secure.

    Last updated: April 4, 2024

    Related Posts

    • New C++ features in GCC 13

    • Prevent Trojan Source attacks with GCC 12

    • Porting your code to C++17 with GCC 11

    • How we expanded GCC value range propagation to floats

    • Use source-level annotations to help GCC detect buffer overflows

    • Improvements to static analysis in the GCC 14 compiler

    Recent Posts

    • Debugging image mode with Red Hat OpenShift 4.20: A practical guide

    • EvalHub: Because "looks good to me" isn't a benchmark

    • SQL Server HA on RHEL: Meet Pacemaker HA Agent v2 (tech preview)

    • Deploy with confidence: Continuous integration and continuous delivery for agentic AI

    • Every layer counts: Defense in depth for AI agents with Red Hat AI

    What’s up next?

    cheat sheet cover image

    Ready to level up your Linux knowledge? Our Intermediate Linux Cheat Sheet presents a collection of Linux commands and executables for developers and system administrators who want to move beyond the basics.

    Get the cheat sheet
    Red Hat Developers logo LinkedIn YouTube Twitter Facebook

    Platforms

    • Red Hat AI
    • Red Hat Enterprise Linux
    • Red Hat OpenShift
    • Red Hat Ansible Automation Platform
    • See all products

    Build

    • Developer Sandbox
    • Developer tools
    • Interactive tutorials
    • API catalog

    Quicklinks

    • Learning resources
    • E-books
    • Cheat sheets
    • Blog
    • Events
    • Newsletter

    Communicate

    • About us
    • Contact sales
    • Find a partner
    • Report a website issue
    • Site status dashboard
    • Report a security problem

    RED HAT DEVELOPER

    Build here. Go anywhere.

    We serve the builders. The problem solvers who create careers with code.

    Join us if you’re a developer, software engineer, web designer, front-end designer, UX designer, computer scientist, architect, tester, product manager, project manager or team lead.

    Sign me up

    Red Hat legal and privacy links

    • About Red Hat
    • Jobs
    • Events
    • Locations
    • Contact Red Hat
    • Red Hat Blog
    • Inclusion at Red Hat
    • Cool Stuff Store
    • Red Hat Summit
    © 2026 Red Hat

    Red Hat legal and privacy links

    • Privacy statement
    • Terms of use
    • All policies and guidelines
    • Digital accessibility

    Chat Support

    Please log in with your Red Hat account to access chat support.