N3556
_Any_func - A Universal Function Pointer Storage Type

Draft Proposal,

Previous Revisions:
None
Authors:
Paper Source:
GitHub
Issue Tracking:
GitHub
Project:
ISO/IEC 9899 Programming Languages — C, ISO/IEC JTC1/SC22/WG14
Proposal Category:
Change Request, Feature Request
Target:
C2y

Abstract

1. Revision History

1.1. Revision 0 - May 27th, 2025

2. Introduction & Motivation

The majority of the motivation is the same as it was 30 years ago, 20 years, and 10 years ago. In [n2230], Martin Sebor captured the core of the motivation:

... A void pointer is not convertible to a function pointer, and conversely, function pointers aren’t convertible to void. It is conceivable that an implementation may (and some, in fact, do) define function pointers to have a different size or representation than void, and such conversions could result in the corruption of the opriginal value.

However, C does not specify an analogous generic function pointer. Unlike object types, a pointer to a function of one type can be converted to a pointer to a function of any other type and back with no change. This is a useful guarantee that makes function pointers safely interchangeable (except to call a function, of course). At the same time, the absence of a generic function pointer analogous to void* has led to a proliferation of workarounds in applications, libraries, and implementations alike. ...

Popular distributions, operating systems, and platforms such as POSIX, Windows/NT, and many other platforms require that a data pointer (void*) is the same size as any function pointer (R(*)(Args...)). But, for many other platforms, function pointers are not the same size as data pointers altogether, either because there are multiple data segments and the data pointers are larger or -- less common but still present -- because function pointers are larger than data pointers. Rather than try to force void* to change to patch this hole (which would be a very fundamental ABI break on many platforms), this proposal focuses instead on following in [n2230]'s footsteps and proposing a (core language) type to satisfy this need properly. It then adds the concept of "equivalence" to allow for the use of void* in an increased number of situations, to standardize existing practice.

3. Design

The goal of this paper is to provide a single type, _Any_func, through the core language. It is a core language type that is incomplete by itself, much like void. Additionally, a pointer to _Any_func has the requirement that it is large enough to hold any function pointer type, and is implicitly convertible from and to any such pointer type, much like its void* counterpart. It is modeled directly after void* and modifies the same places in the wording, as well as a few other places.

Then, in noticing void* on popular implementations has all these powers we are trying to give _Any_func* here, we expanded the concept of void* to allow for a larger and wider variety of (implicit) conversions. Recognizing that all platforms may not have the ability to, we made conversions specifically between _Any_func* and void* as well as void* and some_function_type* optional.

Finally, library changes are made to follow along with all of the appropriate changes.

3.1. Is It Callable?

It is NOT callable.

KnR functions had a serious flaw where despite being usable as "omni" function pointer types (ignoring the issue of return values and potential mismatches there): KnR functions had a different ABI than ... functions and were not required to have the same ABI as other function types. For example, a function int func(int arg0, double arg1); is not required to have the exact same calling convention, register allocation, and other important binary details as a KnR function int func2();. Similarly, a function int func3(...); or int func4(int arg0, ...); is not required to have the same ABI details as either func or func2, even under the circumstances that they were all called like so:

int a = 0;
double b = 1;
func(a, b);
func2(a, b);
func3(a, b);
func4(a, b);

Storing func in a KnR function pointer int(*)() and then calling it as such was not required to work in any version of C (it’s explicitly undefined behavior in every version of C, see "6.3.3.3 Pointers" in the latest working draft and similar paragraphs in C23, C17, and further back). It just so happens that it did happen to work (though technically can and could fail in invisible-to-the-Abstract-Machine ways) for the vast majority of existing C code, due to how most ABIs tend to be designed. (There were ABI issues between func2 and func3, in particular when people would declare time_t time() as a KnR function on some platforms while competing with time_t time(time_t* arg) and similar definitions on various extern-and-exported-interface libraries.)

Not having _Any_func be callable avoids this problem entirely, by refusing to attach a binary representation that represents "all function calls". A cast must be inserted, and that prevents small usability errors. This does not mean that a omni-callable type should not exist. It’s just that there’s likely some form of small "shim" or "trampoline" that might need to be created in order to facilitate this correctly (or, in typical cases with most ABIs, just a raw copy). This is something that should be facilitated by a dedicated language ure for that express purpose, rather than something falling out of an accident of syntax.

3.2. Giving void* Additional Powers

Furthermore, as this is defined in the core language, we can also provide a core language predefined macro for whether or not void* -> some_function_type* and vice-versa is an allowable, legal translation. This also allows us to put wording in the core language that blesses such an (implicit) conversion, given that it’s highly prevalent on a wide variety of architectures. It is often seen as a "gotcha" that this conversion doesn’t work, and users have been asking for this change for a very long time. We obviously do not want to make things worse for embedded platforms or make them non-conforming; they are an important bedrock of C.

Therefore, we define a new concept "representation equivalence" so that we can support the notion of conversion not only between _Any_func* and void*, but void* and any function pointer type. This also further helps us because we can specify properties beyond what is currently blessed by the C standard, and make allowances for the popular platforms which form the overwhelming super majority of platforms that developers work on, even in C:

These predefined macros and their definitions are then applied to four new different predefined macros, which has one of the above 3 value macros as its definition:

Each implementation can support the level that is appropriate for their platform. Some basic support is already supported for 2 of the conversions, both as per this proposal and as per the original purpose of the type since 1989: void* to any object pointer (and back) always reports at least __STDC_PTR_REPR_CONVERTIBLE__ (a value of 1), and _Any_func* to any function pointer (and back) always reports at least __STDC_PTR_REPR_CONVERTIBLE__ (a value of 1). However, conversion between void* and _Any_func* need not be supported (reports __STDC_PTR_REPR_INCOMPATIBLE__ (a value of 0)), as that is not guaranteed to work on all platforms. This is useful for implementations that decide they do not want to take on an impossible task where object/data pointers and function/code points address completely different kinds of memory. Implementations that can not handle it (e.g. 16-bit Texas Instrument DSPs or 8-bit multi-address-space chips that have 21 bit function pointers but 16 bit memory addresses). Most modern implementations -- Windows/NT since forever and POSIX since the 90s with XSI extensions (and just simply required by POSIX 2018) -- would have this defined, and users could #error inside of an #if check if their code happens to any more advanced properties.

Finally, implementations that opt into having at least convertible representation equivalence for void* and _Any_func* should be able to use those types in conversions without constraint violations. Similarly, there is additional wording to allow for any function pointer type to work with (implicit) assignment to void* or vice-versa if the representation equivalence is high enough. This standardizes existing practice, where many implementations do not warn or error on the constraint violation of for unrelated pointer types.

3.3. Naming

The name chosen here is the one that had the no clashes in both its reserved form _Foo_bar and it’s header form foo_bar. The following other names were considered and searched for in public (and some private) code repositories for their prevalence as possible name candidates:

Many of these had issues with existing standard library type names (e.g. _Fn and _Func for C++ type names in templates and typedefs for standard or standard-adjacent libraries) or had common names that, even if hidden behind a header include, would result in undue naming burden on potential downstream users (fn_ptr_t or fn or func_t). Other permutations of these names, including ones with _Storage_/_storage or _Void and _void, either clashed with existing code or became undesirably long.

3.4. ...ptr In the Name or a Pointer to Incomplete Type?

The goal of this design was to feel as close to void* as possible, since that is what this type represents. This means that we wanted to have the * present to have it look and feel just like a regular pointer type under regular pointer rules, rather than having it be part of the name. Since this is a core language type and a not a library definition, we preferred to define it closer to void than anything else. It also helps in putting this type into the specification, as all we have to do is mirror exactly the specification style and places that "A pointer to void" and "void*" exist in the current standard. This eases integration and is -- after first explanation -- immediately understandable to those who have been using void* as the omni-object pointer.

3.5. Library Modifications

The library needs further modification to allow for passing this new pointer type in, since %p by itself is specified to handle just void*. Since function pointer types and void* may not be the same size, a new entry is required to not taint the ABI of these functions, particularly of fprintf and fscanf. There is also discussion of uintptr_t and intptr_t; implementations may need additional integer types to handle this, and so these are provided under uintfuncptr_t and intfuncptr_t (and are as optional as their counterparts).

3.5.1. %jp for fprintf and fscanf

There were a bunch of potential candidates but a lot of them had problems both because of existing uses in implementations or down-stream, or they were novel new modifiers. For example, %lp is already being used by CHERI, while %q is already taken by typical POSIX shell-based printf for quotable strings. To further disadvantage %q, it -- despite being a logical new letter next to %p -- is taken by BSD and still has some existing uses, even though the type it’s used for (quad_t) has long since been aliased to a "proper" type by now. Trying to sneak in %Lp is awkward thanks to implementation already moving in on %lp, especially if printing address spaces as part of pointers because a thing where length modifiers and precision get used to represent it. %Ip seems nice for the mnemonic "instruction pointer", but i is already a normal conversion specifier and I is novel in this space as a modifier and a not a standalone conversion specifier.

Thusly, we settled for %jp in this proposal. It can be read mnemonically as "jump pointer", which is reminiscent of assembly jmp and for assembly junkies gives the feeling of both jmp and call. j is also, already, a length modifier and has no known uses in the wild in combination with %p. It is the best we can do given the Wild Wild West of specifiers and modifiers in the C-and-derivative ecosystem, at this point.

3.5.2. (u)intfuncptr_t and printing/scanning macros

Given that we have a new pointer type for functions, individuals will want to manipulate the numeric representation of these pointers. The reason for this is simple: in the GCC extension, void(*)(void) is used as a generic catch-all function pointer storage type, but also has incrementing ability as part of its extension behavior. _Any_func* does not have such powers, and it was not added to the Additive Operators in the wording. Thus, in order to appropriately manipulate such pointers -- while knowing that there is not char* or unsigned char* analogue for this like with void* -- we have to employ the other means of address manipulation through a new integer type. (u)intfuncptr_t is exactly like (u)intptr_t, just with func in the name. It’s optional as well.

The _MAX macros for this type are already taken care of by blanket specification. The only additional specification needed is for _WIDTH, which is straightforward to write.

Finally, PRI(i/u/U/x/X/b/B/o)FUNCPTR macros for printing and similar SCN(i/u/x/b/o)FUNCPTR macros for scanning macros were added to the appropriate headers. This rounds out full library support for I/O with these numbers, mirroring uintptr_t support.

4. Existing Practice

This proposal does not change any existing implementation’s requirements or abilities. It is purely a formalization of existing practice that has existed both at the hardware and platform/ABI levels since time immemorial (at least 1972, by our estimate). Function pointer types have, for a long time, not been convertible to void* and most compilers in their /W4, -Wall, -pedantic, or --conformance modes have been issuing errors for void* and function pointer conversions but allowing it without warning or error in other cases. Several hardware and software platforms provide a unified data address space (Windows for some 31 years, POSIX with XSI since time immemorial/POSIX:2018, and many other platforms). The goal of this proposal is to solidify this existing practice into the C standard while still preserving the principles and practices of exotic hardware that still exists today: that is, DSPs and Microprocessors by exotic chipmakers and platform vendors (e.g., Texas Instruments) can still continue to vend chips with 16-bit data pointers and 23/24-bit function pointers (or otherwise) and typical Harvard Architectures can still be supported under this paradigm.

Therefore, this proposal makes no patches and provides no novel new compiler infrastructure, because all of this compiler infrastructure (a) already exists and (b) is already being taken advantage of by existing code.

5. Wording

The following wording is against the latest draft of the C standard.

5.1. Modify §6.2.5 "Types"

6.5.2Types

...

...

The _Any_func type comprises the concept of a function; it is a function type unlike any other and has no return type and no parameter types. It cannot be called.

...

A pointer to void shall have the same representation and alignment requirements as a pointer to a character type.41) Similarly, pointers to qualified or unqualified versions of compatible types shall have the same representation and alignment requirements. A pointer to _Any_func shall have an alignment and representation suitable for the storage of any function pointer type. All pointers to structure types shall have the same representation and alignment requirements as each other. All pointers to union types shall have the same representation and alignment requirements as each other. Pointers to other types may not have the same representation or alignment requirements.

5.2. Add a new section §6.2.6.3✨"Pointer types" under §6.2.6 "Representation of types"

6.2.6.3✨Pointer types
For certain pointer types, there are three levels of increasing representation equivalence between them:
  • incompatible representation equivalence, where they do not have a common alignment and representation and cannot be converted to one another and back without preserving its value;

  • convertible representation equivalence, where they do not have a common alignment and representation but can be converted to one another and back with preserving its value; or,

  • correspoding representation equivalence, where they have a compatible alignment and representation.

This property is communicative in both conversion directions for the two pointer types. Each of these levels correspond to the predefined macros __STDC_PTR_REPR_INCOMPATIBLE__, __STDC_PTR_REPR_CONVERTIBLE__, and __STDC_PTR_REPR_CORRESPONDING__ (6.10.10.2), respectively. Representation equivalence can be compared against other representation equivalences; higher values subsume the permissions and properties of lower values, except for incompatible representation equivalence which has no permissions and provides no additional properties.

Two pointer types with incompatible representation equivalences cannot be converted implicitly from one to another and back. An explicit conversion from a pointer of the first type to the second type and then back to the first type can possibly result in a pointer with a different representation that does not compare equal to the original.

Two pointer types with convertible representation equivalences can be converted implicitly or explicitly from one to another and back. A conversion, implicit or explicit, from a pointer of the first type to the second type and then back to the first type shall result in a pointer comparing equal to the the original. The alignment and representation of the first and second pointer types can possibly not be be equivalent or similar.

Two pointer types with corresponding representation equivalences have equivalent alignments and representations. A conversion, implicit or explicit, from a pointer of the first type to the second type shall result in two pointers whose object representations are identical.

NOTE 1   Typically, the byte array of two pointers that have corresponding representation equivalences compare equal after conversions, except for any padding bits which have unspecified values.

Outside of what is defined in 6.3.3.3 and 6.5, representation equivalence is implementation defined and any operations or conversions associated with such representation equivalences are conditionally supported. The representation equivalence between:

  • pointers to _Any_func and pointers to function types characterizes the value of __STDC_PTR_COMPAT_ANY_FUNC_AND_FUNC__;

  • pointers to _Any_func and pointers to void characterizes the value of __STDC_PTR_COMPAT_ANY_FUNC_AND_VOID__;

  • pointers to function types and pointers to void characterizes the value of __STDC_PTR_COMPAT_FUNC_AND_VOID__; and,

  • pointers to object types and pointers to void characterizes the value of __STDC_PTR_COMPAT_OBJECT_AND_VOID__.

NOTE 2   The file-scope declarations

static_assert(__STDC_PTR_COMPAT_OBJECT_AND_VOID__
                >= __STDC_PTR_REPR_CONVERTIBLE__);
static_assert(__STDC_PTR_COMPAT_ANY_FUNC_AND_FUNC__
                >= __STDC_PTR_REPR_CONVERTIBLE__);

do not provoke a constraint violation on any implementation. However, the following file scope declarations potentially trigger constraint violations on implementations:

// "Any_func *" may be incompatible to "void *",
// and vice-versa
static_assert(__STDC_PTR_COMPAT_ANY_FUNC_AND_VOID__
                != __STDC_PTR_REPR_INCOMPATIBLE__);
// "void *" may be incompatible to one or more
// function pointer types, and vice-versa
static_assert(__STDC_PTR_COMPAT_FUNC_AND_VOID__
                != __STDC_PTR_REPR_INCOMPATIBLE__);

EXAMPLE    The following program potentially exhibits undefined behavior without corresponding representation equivalence:

#include <stdarg.h>

struct toto* p = nullptr;

int f(...) {
  va_list ap;
  va_start(ap);
  void* arg = va_arg(ap, void*); // possible undefined behavior
  if (arg == p) {
    // this code might not be reached.
    va_end(ap);
    return 0;
  }
  va_end(ap);
  return 1;
}

int main () {
  return f(p); // not guaranteed to return 0
}

More practically, on a hosted implementation, the following program potentially needs corresponding representation equivalence to print with the expected implementation-defined behavior:

#include <stdio.h>

int main () {
  static_assert(__STDC_PTR_COMPAT_OBJECT_AND_VOID__ == __STDC_PTR_REPR_CORRESPONDING__,
    "Corresponding representation equivalence is required.");
  struct toto* p = nullptr;
  printf("%p\n", p);
  return 0;
}

Otherwise, a cast to void* may be required to ensure well-defined behavior on a hosted implementation:

#include <stdio.h>

int main () {
  struct toto* p = nullptr;
  printf("%p\n", (void*)p); // cast first to perform appropriate conversion
  return 0;
}

5.3. Modify §6.3.3.3 "Pointers"

6.3.3.3Pointers
A pointer to void and a pointer to any object type shall have at least convertible representation equivalence (6.2.6.3✨). A pointer to void can be converted to or from a pointer to any object type. A pointer to any object type can be converted to a pointer to void and back again; the result shall compare equal to the original pointer.
...

An integer constant expression with the value 0, such an expression cast to type void *, such an expression cast to type _Any_func *, or the predefined constant nullptr is called a null pointer constant.57) If a null pointer constant or a value of the type nullptr_t (which is necessarily the value nullptr) is converted to a pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal to a pointer to any object or function.

...

A pointer to _Any_func and a pointer to any other function type shall have at least convertible representation equivalence (6.2.6.3✨). A pointer to any function type can be converted to a pointer to _Any_func and back again and vice-versa; the result shall compare equal to the original pointer.

If a pointer to _Any_func and pointer to void have at least convertible representation equivalence (6.2.6.3✨), a pointer to _Any_func can be converted to or from a pointer to void. A pointer to void can be converted to a pointer to _Any_func and back again and vice-versa; the result shall compare equal to the original pointer.

If a pointer to any function type and pointer to void have at least convertible representation equivalence (6.2.6.3✨), a pointer to void can be converted to or from a pointer to any function type. A pointer to any function type can be converted to a pointer to void and back again and vice-versa; the result shall compare equal to the original pointer.

5.4. Modify §6.4.2 "Keywords" to add a new keyword

6.4.2Keywords
Synopsis

keyword: one of

...           ...           ...           ...           while

...           ...           ...           ...           _Any_func

...           ...           ...           ...           _Atomic

...           ...           ...           ...           ...

5.5. Modify §6.5.10 "Equality operators"

6.5.10Equality operators
...
Constraints

One of the following shall hold: ...

  • ...

  • one operand is a pointer to an object type and the other is a pointer to a qualified or unqualified version of void;

  • one operand is a pointer to a function type and the other is a pointer to _Any_func;
  • if pointers to function types and pointers to void have at least convertible representation equivalence, one operand is a pointer to a function type and the other is a pointer to a qualified or unqualified version of void;
  • if pointers to _Any_func and pointers to void have at least convertible representation equivalence, one operand is a pointer to _Any_func and the other is a pointer to void;
  • ...

...

Otherwise, at least one operand is a pointer. If one operand is a pointer and the other is a null pointer constant or has type nullptr_t, they compare equal if the former is a null pointer. If one operand is a pointer to an object type and the other is a pointer to a qualified or unqualified version of void, the former is converted to the type of the latter. If one operand is a pointer to a function type and the other is a pointer to _Any_func, the former is converted to the type of the latter. If pointers to void and pointers to _Any_func have at least convertible representation equivalence, one operand is a pointer to _Any_func, and the other operand is a pointer to qualified or unqualified void, the former is converted to the type of the latter. If pointers to void and pointers to function types have at least convertible representation equivalence, one operand is a pointer to function type, and the other operand is a pointer to qualified or unqualified void, the former is converted to the type of the latter.

5.6. Modify §6.5.16 "Conditional operator"

6.5.16Conditional operator
...
Constraints
...

One of the following shall hold: ...

  • ... ; or

  • one operand is a pointer to an object type and the other is a pointer to a qualified or unqualified version of void . ;

  • if pointer to function types have at least convertible representation equivalence to a pointer to void, one operand is a pointer to a function type and the other is a pointer to a qualified or unqualified version of void;
  • if pointer to _Any_func have at least convertible representation equivalence to a pointer to void, one operand is a pointer to _Any_func and the other is a pointer to a qualified or unqualified version of void; or
  • one operand is a pointer to a function type and the other is a pointer to _Any_func.
...

If both the second and third operands are pointers, the result type is a pointer to a type qualified with all the type qualifiers of the types referenced by both operands; if one is a null pointer constant (other than a pointer) or has type nullptr_t and the other is a pointer, the result type is the pointer type; if both the second and third operands have nullptr_t type, the result also has that type. Furthermore, if both operands are pointers to compatible types or to differently qualified versions of compatible types, the result type is a pointer to an appropriately qualified version of the composite type; if one operand is a null pointer constant, the result has the type of the other operand ; otherwise, one operand is a pointer to void or a qualified version of void, in which case the result type is a pointer to an appropriately qualified version of void . If one operand is a pointer to object type and the other is a pointer to qualified or unqualified void, the result type is a pointer to an appropriately qualified version of void. If one operand is a pointer to function type and the other is a pointer to _Any_func, the result type is a pointer to _Any_func. If one operand is a pointer to function type, the other is a pointer to qualified or unqualified void, and the types of both operands have at least convertible representation equivalence, the result type is a pointer to an appropriately qualified void. If one operand is a pointer to _Any_func, the other is a pointer to qualified or unqualified void, and the types of both operands have at least convertible representation equivalence, the result type is a pointer to an appropriately qualified void.

5.7. Modify §6.5.17.2 "Simple assignment"

6.5.17.2Simple assignment
Constraints

One of the following shall hold: ...

  • ...

  • the left operand has atomic, qualified, or unqualified pointer type, and (considering the type the left operand would have after lvalue conversion) one operand is a pointer to an object type, and the other is a pointer to a qualified or unqualified version of void, and the type pointed to by the left operand has all the qualifiers of the type pointed to by the right operand;

  • the left operand has atomic, qualified, or unqualified pointer type, and (considering the type the left operand would have after lvalue conversion) one operand is a pointer to a function type, and the other is a pointer to a qualified or unqualified version of _Any_func, and the type pointed to by the left operand has all the qualifiers of the type pointed to by the right operand;
  • if a pointer to _Any_func has at least convertible representation equivalence to pointer to void, and the left operand is atomic, qualified, or unqualified pointer to _Any_func, and the other is a pointer to a qualified or unqualified version of void, and the type pointed to by the left operand has all the qualifiers of the type pointed to by the right operand;
  • if pointers to function types have at least convertible representation equivalence to pointer to void, the left operand has atomic, qualified, or unqualified pointer type, and (considering the type the left operand would have after lvalue conversion) one operand is a pointer to an function type, and the other is a pointer to a qualified or unqualified version of void, and the type pointed to by the left operand has all the qualifiers of the type pointed to by the right operand;
  • ...

5.8. Modify §6.10.10.2 "Predefined macros"

6.10.10.2Predefined macros

The following macro names shall be defined by the implementation:

... ...
__STDC_PTR_COMPAT_ANY_FUNC_AND_FUNC__ One of __STDC_PTR_REPR_INCOMPATIBLE__, __STDC_PTR_REPR_CONVERTIBLE__, or __STDC_PTR_REPR_CORRESPONDING__ tied to the level of representation eqvuialence (6.2.6.3✨) between _Any_func * and all function pointer types (6.3.3.3).
__STDC_PTR_COMPAT_ANY_FUNC_AND_VOID__ One of __STDC_PTR_REPR_INCOMPATIBLE__, __STDC_PTR_REPR_CONVERTIBLE__, or __STDC_PTR_REPR_CORRESPONDING__ tied to the level of representation eqvuialence (6.2.6.3✨) between _Any_func * and void * (6.3.3.3).
__STDC_PTR_COMPAT_FUNC_AND_VOID__ One of __STDC_PTR_REPR_INCOMPATIBLE__, __STDC_PTR_REPR_CONVERTIBLE__, or __STDC_PTR_REPR_CORRESPONDING__ tied to the level of representation eqvuialence (6.2.6.3✨) between all function pointer types and void * (6.3.3.3).
__STDC_PTR_COMPAT_OBJECT_AND_VOID__ One of __STDC_PTR_REPR_INCOMPATIBLE__, __STDC_PTR_REPR_CONVERTIBLE__, or __STDC_PTR_REPR_CORRESPONDING__ tied to the level of representation eqvuialence (6.2.6.3✨) between all object pointer types and void * (6.3.3.3).
... ...
__STDC_PTR_REPR_INCOMPATIBLE__ The integer literal 0, indicating incompatible representation equivalence (6.2.6.3✨).
__STDC_PTR_REPR_CONVERTIBLE__ The integer literal 1, indicating convertible representation equivalence (6.2.6.3✨).
__STDC_PTR_REPR_CORRESPONDING__ The integer literal 2, indicating corresponding representation equivalence (6.2.6.3✨).
... ...

5.9. Modify §7.8.2 "Macros for format specifiers"

7.8.2Macros for format specifiers

...

The fprintf macros for signed integers are:

...   ...   ...   ...   PRIdPTR PRIdFUNCPTR

...   ...   ...   ...   PRIiPTR PRIiFUNCPTR

The fprintf macros for unsigned integers are:

...   ...   ...   ...   PRIbPTR PRIbFUNCPTR

...   ...   ...   ...   PRIoPTR PRIoFUNCPTR

...   ...   ...   ...   PRIuPTR PRIuFUNCPTR

...   ...   ...   ...   PRIxPTR PRIxFUNCPTR

...   ...   ...   ...   PRIXPTR PRIXFUNCPTR

The following fprintf macros for unsigned integer types are optional:

...   ...   ...   ...   PRIBPTR PRIBFUNCPTR

...

The fscanf macros for signed integers are

...   ...   ...   ...   SCNdPTR SCNdFUNCPTR

...   ...   ...   ...   SCNiPTR SCNiFUNCPTR

The fscanf macros for unsigned integers are:

...   ...   ...   ...   SCNbPTR SCNbFUNCPTR

...   ...   ...   ...   SCNoPTR SCNoFUNCPTR

...   ...   ...   ...   SCNuPTR SCNuFUNCPTR

...   ...   ...   ...   SCNxPTR SCNxFUNCPTR

5.10. Modify §7.17.6 "Atomic integer types"

7.17.6Atomic integer types

For each line in Table 7.6,307) the atomic type name is declared as a type that has the same representation and alignment requirements as the corresponding direct type.308)

Table 7.6 — Atomic type Type name equivalency
Atomic typename Direct name
... ...
atomic_uintfuncptr_t _Atomic uintfuncptr_t
... ...

5.11. Modify §7.23.2.5 "Integer types capable of holding object pointers"

7.23.2.5Integer types capable of holding object pointers

The following type designates a signed integer type, other than a bit-precise integer type, with the property that any valid pointer to void can be converted to this type, then converted back to pointer to void, and the result will compare equal to the original pointer:

intptr_t

The following type designates an unsigned integer type, other than a bit-precise integer type, with the property that any valid pointer to void can be converted to this type, then converted back to pointer to void, and the result will compare equal to the original pointer:

uintptr_t

The following type designates a signed integer type, other than a bit-precise integer type, with the property that any valid pointer to _Any_func can be converted to this type, then converted back to pointer to _Any_func, and the result will compare equal to the original pointer:

intfuncptr_t

The following type designates an unsigned integer type, other than a bit-precise integer type, with the property that any valid pointer to _Any_func can be converted to this type, then converted back to pointer to _Any_func, and the result will compare equal to the original pointer:

uintfuncptr_t

These types are optional.

5.12. Modify §7.23.3.5 "Width of integer types capable of holding object pointer types"

7.23.3.5Width of integer types capable of holding object pointer types
INTPTR_WIDTH                           exactly UINTPTR_WIDTH
UINTPTR_WIDTH                          16
INTFUNCPTR_WIDTH                       exactly UINTFUNCPTR_WIDTH
UINTFUNCPTR_WIDTH                      8

5.13. Modify §7.24.6.2 "The fprintf function"

7.24.6.2The fprintf function
Synopsis
#include <stdio.h>
int fprintf(FILE * restrict stream, const char * restrict format, ...);
Description

...

The length modifiers and their meanings are:

... ...
j Specifies that a following b, B, d, i, o, u, x, or X conversion specifier applies to an intmax_t or uintmax_t argument; that a following p conversion specifier applies to an _Any_func* argument; or that a following n conversion specifier applies to a pointer to an intmax_t argument.
... ...

The conversion specifiers and their meanings are:

... ...
p The argument shall be a pointer to void , a pointer to _Any_func, or a pointer to a character type. The value of the pointer is converted to a sequence of printing characters, in an implementation-defined manner.
... ...

5.13.1. NOTE: IDENTICAL CHANGES TO fwprintf!

5.13.2. NOTE: UPDATE ANNEX J UNDEFINED BEHAVIOR (Number 34)!

5.14. Modify §7.24.6.3 "The fscanf function"

7.24.6.3The fscanf function
Synopsis
#include <stdio.h>
int fscanf(FILE * restrict stream, const char * restrict format, ...);
Description

...

The length modifiers and their meanings are:

... ...
j Specifies that a following b, d, i, o, u, x, X, or n conversion specifier applies to an argument with type pointer to intmax_t or uintmax_t ; or, a following p conversion specifier applies to an argument with type pointer to pointer of _Any_func .
... ...

In the following, the type of the corresponding argument for a conversion specifier shall be a pointer to a type determined by the length modifiers, if any, or specified by the conversion specifier. The conversion specifiers and their meanings are:

... ...
p Matches an implementation-defined set of sequences, which should be the same as the set of sequences that may be produced by the %p or %jp conversion of the fprintf function. The corresponding argument shall be a pointer to a pointer of void ; or, with the j length modifier, a pointer to pointer of _Any_func . The input item is converted to a pointer value in an implementation-defined manner. If the input item is a value converted earlier during the same program execution, the pointer that results shall compare equal to that value; otherwise the behavior of the %p or %jp conversion is undefined.
... ...

5.14.1. NOTE: IDENTICAL EDITS TO fwscanf!

5.14.2. NOTE: UPDATE ANNEX J UNDEFINED BEHAVIOR (Number 36)!

References

Informative References

[N2230]
Martin Sebor. n2230 - Generic Function Pointer. March 26th, 2018. URL: https://www.open-std.org/JTC1/SC22/WG14/www/docs/n2230.htm