简体   繁体   中英

Pointer arithmetic on pointers to variably-modified types

Is the following valid C code? ( godbolt )

#include <stddef.h>

ptrdiff_t f(size_t n, void *x, void *y)
{
    if (!n) return 0;
    typedef unsigned char element[n];
    element *a = x, *b = y;
    return a - b;
}

With -Werror=pointer-arith clang loudly complains about

<source>:8:14: error: subtraction of pointers to type 'element' (aka 'unsigned char [n]') of zero size has undefined behavior [-Werror,-Wpointer-arith]
    return a - b;
           ~ ^ ~

while gcc compiles the code without complaint.

What is the undefined behavior that clang thinks is occuring? The possibility of the subtraction being zero and therefore not a valid pointer to an element of the array or something different? There's no array access being performed, right? So that shouldn't be the case...

If the code does exhibit undefined behavior, is there a simple way to modify the code to be fully conforming, while still using pointers to VM-types?

The code posted is valid C code if VLAs are supported by your compiler. Note that VLAs introduced in C99 have become optional in the latest version of the C Standard.

Both gcc and clang compile the code correctly as can be verified using Godbolt's compiler explorer .

Yet clang issues a warning regarding potential undefined behavior if the value of the n argument happens to be null, failing to identify that this case has been handled with an explicit test. The problem is not the value of the subtraction but the size of the type, which would be 0 if n == 0 . This warning is not really a bug, more a quality of implementation issue.

It is also arguable that a - b is only defined if both a and b point to the same array or just past the last element of it. Hence x and y must verify this constraint and have type unsigned char (*)[n] or compatible. There is an exception to this rule for accessing any type as an array of character type, so passing pointers to the same array of int would be fine, but it would be incorrect (although probably harmless) to call f this way:

int x, y;
ptrdiff_t dist = f(sizeof(int), &x, &y);

Compilers are free to issue diagnostic messages to attract the programmer's attention on potential problems, indeed such warnings are life savers in many cases for beginners and advanced programmers alike. Compiler options such as -Wall , -Werror and -Weverything are quite useful, but in this particular case, one will need to add -Wno-pointer-arith to allow clang to compile this function if -Werror is also active.

Note also that the same result can be obtained with a C89 function:

ptrdiff_t f89(size_t n, void *x, void *y)
{
    if (!n) return 0;
    unsigned char *a = x, *b = y;
    return (a - b) / n;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM