Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it safe to take the difference of two size_t objects?

Tags:

I'm investigating a standard for my team around using size_t vs int (or long, etc). The biggest drawback I've seen pointed out is that taking the difference of two size_t objects can cause problems (I'm unsure of specific problems -- maybe something wasn't 2s complemented and the signed/unsigned angers the compiler). I wrote a quick program in C++ using the V120 VS2013 compiler that allowed me to do the following:

#include <iostream>  main() {     size_t a = 10;     size_t b = 100;     int result = a - b; } 

The program resulted in -90, which although correct, makes me nervous about type mismatches, signed/unsigned problems, or just plain undefined behavior if the size_t happens to get used in complex math.

My question is if it's safe to do math with size_t objects, specifically, taking the difference? I'm considering using size_t as a standard for things like indexes. I've seen some interesting posts on the topic here, but they don't address the math issue (or I missed it).

What type for subtracting 2 size_t's?

typedef for a signed type that can contain a size_t?

like image 670
PerryC Avatar asked Jan 06 '16 21:01

PerryC


People also ask

Can you subtract Size_t?

Subtracting two std::size_t s will yield a new std::size_t † and its value will be determined by wrapping. In your example, assuming 64 bit size_t , a - b will equal 18446744073709551526 . This does not fit into an (commonly used 32 bit) int , so an implementation defined value is assigned to result .

Should I use Size_t everywhere?

When writing C code you should always use size_t whenever dealing with memory ranges. The int type on the other hand is basically defined as the size of the (signed) integer value that the host machine can use to most efficiently perform integer arithmetic.

When should Size_t be used?

size_t is commonly used for array indexing and loop counting. Programs that use other types, such as unsigned int, for array indexing may fail on, e.g. 64-bit systems when the index exceeds UINT_MAX or if it relies on 32-bit modular arithmetic.

Can Size_t be float?

Since size_t must be an unsigned type, you first need to check explicitly if your float is negative: The behaviour on converting a float that's less than or equal to -1 to an unsigned type is undefined. The second job you need to do is to check that the float is within the upper range of size_t .


1 Answers

This is not guaranteed to work portably, but is not UB either. The code must run without error, but the resulting int value is implementation defined. So as long as you are working on platforms that guarantee the desired behavior, this is fine (as long as the difference can be represented by an int of course), otherwise, just use signed types everywhere (see last paragraph).

Subtracting two std::size_ts will yield a new std::size_t and its value will be determined by wrapping. In your example, assuming 64 bit size_t, a - b will equal 18446744073709551526. This does not fit into an (commonly used 32 bit) int, so an implementation defined value is assigned to result.

To be honest, I would recommend to not use unsigned integers for anything but bit magic. Several members of the standard committee agree with me: https://channel9.msdn.com/Events/GoingNative/2013/Interactive-Panel-Ask-Us-Anything 9:50, 42:40, 1:02:50

Rule of thumb (paraphrasing Chandler Carruth from the above video): If you could count it yourself, use int, otherwise use std::int64_t.


Unless its conversion rank is less than int, e.g. if std::size_t is unsigned short. In that case, the result is an int and everything will work fine (unless int is not wider than short). However

  1. I do not know of any platform that does this.
  2. This would still be platform specific, see first paragraph.
like image 117
Baum mit Augen Avatar answered Sep 20 '22 21:09

Baum mit Augen