Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to detect negative number assigned to size_t?

Tags:

c++

signed

size-t

This declaration compiles without warnings in g++ -pedantic -Wall (version 4.6.3):

std::size_t foo = -42;

Less visibly bogus is declaring a function with a size_t argument, and calling it with a negative value. Can such a function protect against an inadvertent negative argument (which appears as umpteen quintillion, obeying §4.7/2)?

Incomplete answers:

Just changing size_t to (signed) long discards the semantics and other advantages of size_t.

Changing it to ssize_t is merely POSIX, not Standard.

Changing it to ptrdiff_t is brittle and sometimes broken.

Testing for huge values (high-order bit set, etc) is arbitrary.

like image 259
Camille Goudeseune Avatar asked Mar 29 '13 19:03

Camille Goudeseune


People also ask

Can Size_t have negative values?

The size_t data type is never negative.

Does Strtol work with negative numbers?

If the value is positive, the strtol() function will return LONG_MAX, and the strtoll() function will return LONGLONG_MAX. If the value is negative, the strtol() function will return LONG_MIN, and the strtoll() function will return LONGLONG_MIN.

Can u32 be negative?

The u32 stands for 32-bit unsigned integer type. It is an unsigned integer because it cannot contain a negative value like signed integers. The range of u32 is 0 to 4294967295 . In order to get the smallest value of this integer type, we must use the MIN constant.

What is the difference between Ssize_t and Size_t?

In short, ssize_t is the same as size_t , but is a signed type - read ssize_t as “signed size_t ”. ssize_t is able to represent the number -1 , which is returned by several system calls and library functions as a way to indicate error. For example, the read and write system calls: #include <sys/types.


2 Answers

The problem with issuing a warning for this is that it's not undefined behavior according to the standard. If you convert a signed value to an unsigned type of the same size (or larger), you can later convert that back to a signed value of the original signed type and get the original value1 on any standards-compliant compiler.

In addition, using negative values converted to size_t is fairly common practice for various error conditions -- many system calls return an unsigned (size_t or off_t) value for success or a -1 (converted to unsigned) for an error. So adding such a warning to the compiler would cause spurious warnings for much existing code. POSIX attempts to codify this with ssize_t, but that breaks calls that may be successful with a return value greater than the maximum signed value for ssize_t.


1"original value" here actually means "a bit pattern that compares as equal to the original bit pattern when compared as that signed type" -- padding bits might not be preserved, and if the signed representation has redundant encodings (eg, -0 and +0 in a sign-magnitude representation) it might be 'canonicalized'

like image 61
Chris Dodd Avatar answered Oct 08 '22 06:10

Chris Dodd


The following excerpt is from a private library.

#include <limits.h>

#if __STDC__ == 1 && __STDC_VERSION__ >= 199901L || \
    defined __GNUC__ || defined _MSC_VER
    /* Has long long. */
    #ifdef __GNUC__
        #define CORE_1ULL __extension__ 1ULL
    #else
        #define CORE_1ULL 1ULL
    #endif
    #define CORE_IS_POS(x) ((x) && ((x) & CORE_1ULL << (sizeof (x)*CHAR_BIT - 1)) == 0)
    #define CORE_IS_NEG(x) (((x) & CORE_1ULL << (sizeof (x)*CHAR_BIT - 1)) != 0)
#else
    #define CORE_IS_POS(x) ((x) && ((x) & 1UL << (sizeof (x)*CHAR_BIT - 1)) == 0)
    #define CORE_IS_NEG(x) (((x) & 1UL << (sizeof (x)*CHAR_BIT - 1)) != 0)
#endif

#define CORE_IS_ZPOS(x) (!(x) || CORE_IS_POS(x))
#define CORE_IS_ZNEG(x) (!(x) || CORE_IS_NEG(x))

This should work with all unsigned types.

like image 24
alecov Avatar answered Oct 08 '22 05:10

alecov