Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

char *a, *b; what type is (b-a) and how do I printf it?

Tags:

c

pointers

stdio

{
  char *a, *b;

  printf("%lx\n",(b-a));
}

Usually works, in fact, I can't imagine it giving a warning or failing on a 32-bit or 64-bit machine. But is that the proper thing to do for ANSI C and size awareness? I'd like this code to work on every platform possible, including non-Unixes and embedded systems.

like image 214
Southern Hospitality Avatar asked Oct 29 '09 18:10

Southern Hospitality


2 Answers

b - a is a ptrdiff_t, which you can print with %td in your printf format. From the spec section 6.5.6 Additive operators:

When two pointers are subtracted, both shall point to elements of the same array object, or one past the last element of the array object; the result is the difference of the subscripts of the two array elements. The size of the result is implementation-defined, and its type (a signed integer type) is ptrdiff_t defined in the <stddef.h> header.

For printf and related functions, section 7.19.6 Formatted input/output functions:

t Specifies that a following d, i, o, u, x, or X conversion specifier applies to a ptrdiff_t or the corresponding unsigned integer type argument; or that a following n conversion specifier applies to a pointer to a ptrdiff_t argument.

I poked around in the spec some more, and it seems to indicate that a difference of two pointers might not even fit in a ptrdiff_t, in which case behaviour is undefined:

J.2 Undefined behavior
- The result of subtracting two pointers is not representable in an object of type ptrdiff_t (6.5.6).

Though I can't imagine any implementation where that might come up. I guess you could check PTRDIFF_MIN and PTRDIFF_MAX in <stdint.h> to be really sure.

like image 159
Carl Norum Avatar answered Sep 21 '22 15:09

Carl Norum


The result of b - a is only defined when both a and b point to elements of the same char array. This requirement can also be interpreted as a and b pointing to bytes belonging to the same object, since every object can be re-interpreted as a char array.

Otherwise, the result is undefined. I.e. an attempt to subtract such pointers results in undefined behavior.

When the result is defined, it has ptrdiff_t type. ptrdiff_t is a typedef name and what type is hiding behind that typedef name is implementation-defined. The type is known to be signed though.

Also note, that C language does not guarantee that ptrdiff_t is large enough to hold the result of any subtraction, even if the pointers are pointing to the elements of the same array. If the pointers are too far apart for the type ptrdiff_t to accomodate the result, the behavior is undefined.

There's no specific printf format specifier for ptrdiff_t even in C99, so you'll probably be better off converting it to a sufficiently large signed integer type and use a format specifier for that type

printf("%ld\n", (long) (b - a));

Correction: C99 does have a length modifier for ptrdiff_t. The proper way to print the result in C99 would be

printf("%td\n", b - a);

Note that t is a length modifier. It can be combined with d, o, u, x or X conversion specifiers, depending on what output format you want to obtain. In C89/90 you would still have to stick with using a sufficiently large signed type.

P.S. You said that you can't imagine it failing on a 32-bit or 64-bit machine. In fact, it is very easy to imagine (or to actually make it) fail. You see the ptrdiff_t on a 32-bit machine is usually a 32-bit type. Since it is a signed type, it has only 31 bit available to represent the magnitude of the value. If you take two pointers that are farther apart (i.e. require 32 bits to represent the "distance"), the result of b - a will overflow and will be meaningless. In order to prevent this failure you'd need at least 33-bit signed ptrdiff_t on a 32-bit machine, and at least 65-bit signed ptrdiff_t on 64-bit machine. Implementations normally don't do that, they just use the "permission" from the standard to produce undefined behavior on the overflow.

like image 33
AnT Avatar answered Sep 20 '22 15:09

AnT