Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Note the comment informing programmers that the macro is unsafe. The macro can also be renamed ABS_UNSAFE() to make it painfully apparent that the macro is unsafe.  This compliant solution, like all the compliant solutions for this rule, have undefined behavior if to ABS() is equal to the minimum (most negative) value for the signed integer type.  See INT32-C. Ensure that operations on signed integers do not result in overflow for more information.

Compliant Solution

This compliant solution follows the guidance of PRE00-C. Prefer inline or static functions to function-like macros by defining an inline function iabs() to replace the ABS() macro. Unlike the ABS() macro, which operates on operands of any type, the abs() function accepts arguments only of type int

Code Block
bgColor#ccccff
langc
#include <complex.h>
#include <math.h>
 
static inline int iabs(int x) {
  return (((x) < 0) ? -(x) : (x));
}
 
void func(int n) {
  /* Validate n is within the desired range */
  ++n;
  int m = abs(n);

  /* ... */
}

Compliant Solution

The preferred compliant solution is to declare the ABS() macro using a _Generic selection expression selection. To support all arithmetic data types, this solution also makes use of inline functions to handle integer absolute values. (See PRE00-C. Prefer inline or static functions to function-like macros and PRE12-C. Do not define unsafe macros.) 

According to the C Standard, subclause 6.5.1.1 paragraph 3 [ISO/IEC 9899:2011],

...

Code Block
bgColor#ccccff
langc
#include <complex.h>
#include <math.h>
 
static inline long long llabs(long long v) {
  return v < 0 ? -v : v;
}
static inline long labs(long v) {
  return v < 0 ? -v : v;
}
static inline int iabs(int v) {
  return v < 0 ? -v : v;
}
static inline short sabs(short v) {
  return v < 0 ? -v : v;
}
static inline signed char scabs(signed char v) {
  return v < 0 ? -v : v;
}
 
#define ABS(v)  _Generic(v, signed char : scabs \
                            short : sabs \
                            int : iabs \
                            long : labs \
                            long long : llabs \
                            float : fabsf \
                            double : fabs \
                            long double : fabsl \
                            double complex : cabs \
                            float complex : cabsf \
                            long double complex : cabsl)(v)
 
void func(int n) {
  /* Validate n is within the desired range */
  int m = ABS(++n);
  /* ... */
}

Generic selections were introduced in C11 and are not available in C99 and earlier editions of the C standard. However, some C implementations provide extensions that make it possible to solve the original problem without using functions.

Compliant Solution (GCC)

GCC's __typeof extension makes it possible to declare and assign the value of the macro operand to a temporary of the same type and perform the computation on the temporary, thus guaranteeing that the operand will be evaluated exactly once:

...

Note that relying on such extensions makes code nonportable and goes against violates MSC14-C. Do not introduce unnecessary platform dependencies.

This code comes very close to violating DCL37-C. Do not declare or define a reserved identifier. It technically complies with this rule because it falls under exception DCL37-EX1. However, this code is potentially unsafe if it were invoked with a variable named __tmp. Such calling code would constitute a genuine violation of DCL37-C. Finally, this code is unsafe if it is ever invoked on a platform where __tmp actually has special meaning (see DCL37-C for more information). These are considered acceptable problems, as C provides no mechanism to declare a variable in a scope that is guaranteed to be distinct from all other variables in the same scope.

...