﻿# About size\_t and ptrdiff\_t

The article explains what size\_t and ptrdiff\_t types are, their purpose, and when to use them\. The following information is especially valuable for developers starting to create 64\-bit applications, where size\_t and ptrdiff\_t types provide high performance, the ability to work with large amounts of data, and portability between different platforms\.

## Introduction

Note that definitions and recommendations in the article relate to the most common current architectures \([IA\-32](https://pvs-studio.com/en/blog/terms/0060/), [Intel 64](https://pvs-studio.com/en/blog/terms/0022/), [IA\-64](https://pvs-studio.com/en/blog/terms/0017/)\)\. The information may be inaccurate in relation to exotic architectures\.

_size\_t_ and _ptrdiff\_t_ types were created to perform correct [address arithmetic](https://pvs-studio.com/en/blog/terms/0005/)\. For a long time, developers assumed that _int_ was the same size as a machine word \(a bit width of a processor\), and it could be used as an index to store object sizes or pointers\. So, address arithmetic was also built with the help of _int_ and _unsigned_ types\. The _int_ type is used in most C and C\+\+ programming tutorials in loop bodies, and as indexes\. Here's an almost canonical example:

```cpp
for (int i = 0; i < n; i++)
  a[i] = 0;
```

As processors were developing and their bit width was increasing, it became unreasonable to further increase the bit width of the _int_ type\. There were many reasons: it saved the memory used, ensures maximum compatibility, and so on\. As a result, several data models describing the relations of the base C and C\+\+ types appeared\. Table N1 shows the main [data models](https://pvs-studio.com/en/blog/terms/0012/) and lists the most popular systems that use them\.

![a0050_size_t_and_ptrdiff_t/image2.png](https://import.viva64.com/docx/blog/a0050_size_t_and_ptrdiff_t/image2.png)

_Table N1\. Data models_

As you can see, it's not that easy to choose the type of variable to store a pointer or the size of an object\. To flawlessly solve this problem, _size\_t_ and _ptrdiff\_t_ types appeared\. They can certainly be used in address arithmetic\. Now, we can consider the following code canonical:

```cpp
for (size_t i = 0; i < n; i++)
  a[i] = 0;
```

It can provide reliability, portability, and performance\. We'll learn why further on\.

## The size\_t type

[_size\_t_](https://pvs-studio.com/en/blog/terms/0044/) is a special unsigned integer type defined in standard libraries of C and C\+\+\. It is the type of the result returned by [_sizeof_](https://timsong-cpp.github.io/cppwp/n4861/expr.sizeof#5) and [_alignof_](https://timsong-cpp.github.io/cppwp/n4861/expr.alignof#2) operators\.

The maximum allowed value of the _size\_t_ type is the _SIZE\_MAX_ constant\.

_size\_t_ can store the maximum size of a theoretically possible array or object\. In other words, the number of bits in _size\_t_ is equal to the number of bits required to store the maximum address in the machine's memory\. For example, on a 32\-bit system, _size\_t_ occupies 32 bits, on a 64\-bit system — 64 bits\. This means that a pointer can safely be placed in the _size\_t_ type \(except for platforms with segment addressing and pointers to class member functions\)\.

With this type, developers don't have to worry about the possible different behavior of integer variables when changing the platform\. The implementation of the standard library takes care of this\. So, the _size\_t_ type is safer and more efficient than ordinary unsigned integer types:

1. This type allows to write loops and counters without worrying about possible overflow when changing platforms\. For example, when the number of required iterations exceeds _UINT\_MAX_;
1. Since [the standard](https://timsong-cpp.github.io/cppwp/n4861/support.types.layout#3) guarantees that _size\_t_ can contain the maximum possible object in the system, this type is used to store the sizes of objects;
1. The same feature makes it possible to use _size\_t_ for array indexing\. Unlike the usual basic integer types, _size\_t_ guarantees that the index value cannot be greater than _SIZE\_MAX_;
1. Since a pointer can usually be safely placed in _size\_t_, it is used for [address arithmetic](https://pvs-studio.com/en/blog/terms/0005/)\. However, for these purposes, it's better to use another unsigned integer type — [_uintptr\_t_](https://pvs-studio.com/en/blog/terms/0050/) — the name says it all;
1. The compiler can build simpler and, therefore, faster code without unnecessary conversions of 32\-bit and 64\-bit data\.

In C, the _size\_t_ type is declared in the following header files: _<stddef\.h\>_, _<stdlib\.h\>_, _<string\.h\>_, _<wchar\.h\>_, _<uchar\.h\>_, _<time\.h\>_, and _<stdio\.h\>_\. In C\+\+, _size\_t_ is declared in the following files: _<cstddef\>_, _<cstdlib\>_, _<cstring\>_, _<cwchar\>_, _<cuchar\>_, _<ctime\>_, and _<cstdio\>_\. The _size\_t_ type is placed in the global namespace and in _std_\. Standard header files of the C language used for backward compatibility can also be included in C\+\+ programs\.

Note\. There's also the [_rsize\_t_](https://pvs-studio.com/en/blog/terms/0095/) type\. It is very similar to the _size\_t_ type\. However, it is designed to store the size of a single object\. In other words, using _rsize\_t_, developers emphasize that they are working with the size of a single object\. The _RSIZE\_MAX_ constant sets the maximum size of a single object\.

## The ptrdiff\_t type

[_ptrdiff\_t_](https://pvs-studio.com/en/blog/terms/0041/) is a special signed integer type defined in the standard libraries of the C and C\+\+ languages\. It is a type of the result of [subtracting pointers](https://timsong-cpp.github.io/cppwp/n4861/expr.add#5)\. The behavior of the type is similar to [_size\_t_](https://pvs-studio.com/en/blog/terms/0044/): on a 32\-bit system, the size of _ptrdiff\_t_ will be 32 bits, on a 64\-bit system — 64 bits\.

Also, when working with standard library containers, the result of subtracting two iterators has the _difference\_type_ type of the container used, which, depending on the standard library, is often equal to _ptrdiff\_t_\.

The _ptrdiff\_t_ type is often used for [address arithmetic](https://pvs-studio.com/en/blog/terms/0005/) and array indexing, if negative values are possible\. Programs that use regular integer types \(_int_\) for this purpose can experience [undefined behavior](https://pvs-studio.com/en/blog/terms/0066/)\. For example, if the index value exceeds _INT\_MAX_\.

For arrays smaller than _PTRDIFF\_MAX_, _ptrdiff\_t_ behaves like an analog of _size\_t_: it can store the size of an array of any type and is very similar to [_intptr\_t_](https://pvs-studio.com/en/blog/terms/0023/) on most platforms\. However, if an array is large enough \(larger than _PTRDIFF\_MAX_ but smaller than _SIZE\_MAX_\), and the difference of its pointers cannot be represented as _ptrdiff\_t_, then the result of subtracting such pointers [is undefined](https://timsong-cpp.github.io/cppwp/n4861/expr.add#5.3)\.

In C, the _ptrdiff\_t_ type is declared in the header file _<stddef\.h\>_\. In C\+\+, its declaration is located in _<cstddef\>_ and is placed in the global namespace and in _std_\. Standard header files of the C language for backward compatibility can also be included in C\+\+ programs\.

## Portability of size\_t and ptrdiff\_t

The _size\_t_ and _ptrdiff\_t_ types allow to write portable code\. The size of _size\_t_ and _ptrdiff\_t_ always matches the size of the pointer\. For this reason, these types should be used as indexes of large arrays for storing pointers and pointer arithmetic\.

Linux application developers often use the _long_ type for this purpose\. This indeed worked within the framework of 32\-bit and 64\-bit data models adopted in Linux\. The size of the _long_ type is the same as the size of the pointer\. However, such code is incompatible with the Windows data model, and, accordingly, it cannot be considered well portable\. In the _LLP64_ model \(Windows x64\), the _long_ type remained 32\-bit\. A more correct solution would be to use _size\_t_ and _ptrdiff\_t_ types\.

Developers working on Windows can use _DWORD\_PTR_, _SIZE\_T, SSIZE\_T_, and so on as an alternative to _size\_t_ and _ptrdiff\_t_\. However, it's better to restrict yourself to _size\_t_, _ptrdiff\_t_, _uintptr\_t_, _intptr\_t_ for greater compatibility\.

## Safety of ptrdiff\_t and size\_t types in address arithmetic

The problems of address arithmetic began to actively manifest themselves with the emerging of 64\-bit systems\. The greatest number of issues when porting 32\-bit applications to 64\-bit systems is associated with types unsuitable for working with pointers and arrays, such as _int_ and _long_\. This is not the only problem of porting applications to 64\-bit systems, but most errors are related to address arithmetic and indexes\. The problems of code migration are described in more detail in [Lessons on the development of 64\-bit C/C\+\+ applications](https://pvs-studio.com/en/blog/lessons/) \[1\]\.

Let's take a look at a simple example:

```cpp
size_t n = ...;
for (int i = 0; i < n; i++)
  a[i] = 0;
```

If we have an array consisting of more than _INT\_MAX_ elements, then this code is incorrect\. When a signed variable overflows, undefined behavior occurs\. In the debug version of the program, [Access Violation](https://pvs-studio.com/en/blog/terms/0063/) is more likely to occur, when the index value overflows\. But the release version, depending on the optimization settings and the code features, can, for example, unexpectedly correctly fill all the elements of the array, creating the illusion of correct operation\! As a result, floating errors show up in the program, appearing or disappearing after the slightest code change\. You can find more information about such phantom errors and their dangers in the following article: [A 64\-bit horse that can count](https://pvs-studio.com/en/blog/posts/cpp/a0043/) \[2\]\.

An example of another hidden error that will manifest itself with a certain combination of input data \(the value of variables _A_ and _B_\):

```cpp
int A = -2;
unsigned B = 1;
int array[5] = { 1, 2, 3, 4, 5 };
int *ptr = array + 3;
ptr = ptr + (A + B); // Error
printf("%i\n", *ptr);
```

This code will be executed successfully in the 32\-bit version and will print number "3" on the screen\. After compiling in 64\-bit mode, code execution will fail\. Let's consider the sequence of code execution and the cause of the error:

* The _A_ variable of the _int_ type is converted to the _unsigned_ type;
* There's an addition of _A_ and _B\._ As a result, we get the 0xFFFFFFFF value of the _unsigned_ type;
* The expression "ptr \+ 0xFFFFFFFF" is calculated\. The result depends on the size of the pointer on the given platform\. In a 32\-bit program, the expression will be equivalent to "ptr \- 1", and we will successfully print number 3\. In a 64\-bit program, the 0xFFFFFFFF value will be added to the pointer\. As a result, the pointer will be far outside the array\.

_size\_t_ and _ptrdiff\_t_ types help avoid these errors\. In the first case, if the type of the _i_ variable is _size\_t_, no overflow will occur\. In the second case, if we use _size\_t_ or _ptrdiff\_t_ types for variables _A_ and _B_, we will print number "3" correctly\.

So, here's a tip: if you're working with pointers or arrays, it's better to use _size\_t_ and _ptrdiff\_t_ types\.

To learn more about errors that you can avoid with the help of _size\_t_ and _ptrdiff\_t_, read the following articles:

* [20 issues of porting C\+\+ code to the 64\-bit platform](https://pvs-studio.com/en/blog/posts/cpp/a0004/) \[3\];
* [Safety of 64\-bit code](https://pvs-studio.com/en/blog/posts/cpp/a0046/) \[4\];
* [Traps detection during migration of C and C\+\+ code to 64\-bit Windows](https://pvs-studio.com/en/blog/posts/cpp/a0012/) \[5\];
* [Undefined behavior is closer than you think](https://pvs-studio.com/en/blog/posts/cpp/0374/) \[6\]\.

## Performance of code that uses ptrdiff\_t and size\_t types

In addition to improving the reliability of the code, _ptrdiff\_t_ and _size\_t_ types in address arithmetic can give additional performance gains\. For example, the _int_ type as an index \(the size of which differs from the size of the pointer\) results in additional data conversion commands in the binary code\. We are talking about 64\-bit code in which the size of pointers became 64 bits, and the size of the _int_ type remained 32\-bit\.

It's difficult to give a brief example demonstrating that _size\_t_ is better than _unsigned_\. To be objective, it is necessary to use the optimizing capabilities of the compiler\. However, the two versions of optimized code often become too dissimilar to easily demonstrate the difference\. We tried to create something close to a simple example, but we succeeded only at the sixth attempt\. Still, the example is not perfect, because it shows not the previously mentioned unnecessary data type conversions, but that the compiler was able to build more efficient code with the help of the _size\_t_ type\. Let's consider the program code that arranges array items in the reverse order:

```cpp
unsigned arraySize;
...
for (unsigned i = 0; i < arraySize / 2; i++)
{
  float value = array[i];
  array[i] = array[arraySize - i - 1];
  array[arraySize - i - 1] = value;
}
```

The variables _arraySize_ and _i_ have the _unsigned_ type\. You can easily replace the type with _size\_t_ and compare a small fragment of assembler code shown in Figure 1\.

![a0050_size_t_and_ptrdiff_t/image4.png](https://import.viva64.com/docx/blog/a0050_size_t_and_ptrdiff_t/image4.png)

_Figure N1\. The comparison of 64\-bit assembler code with unsigned and size\_t types_

The compiler managed to build more concise code, when it used 64\-bit registers\. We don't want to say that the code created with the help of the _unsigned_ type \(text on the left\) will be slower than the code created with the help of the _size\_t_ type \(text on the right\)\. It is rather difficult to compare the speed of code execution on contemporary processors\. However, the example shows that the compiler can build more concise and faster code with the help of 64\-bit types\.

According to our personal experience, a competent replacement of _int_/_unsigned_ types with _ptrdiff\_t_/_size\_t_ can give an additional performance gain of up to 10% on a 64\-bit system\. You can view one of the examples of how _ptrdiff\_t_ and _size\_t_ types increase the performance in the fourth chapter of the following article: [Development of resource\-intensive applications in Visual C\+\+](https://pvs-studio.com/en/blog/posts/a0018/) \[7\]\.

## Code refactoring to switch to ptrdiff\_t and size\_t

As we've already discussed, _ptrdiff\_t_ and _size\_t_ types have a number of advantages for 64\-bit programs\. However, we can't just replace all _unsigned_ types with _size\_t_\. Firstly, this does not guarantee the correctness of the program on a 64\-bit system\. Secondly, most likely, such a replacement will provoke new errors, break the compatibility of data formats, and so on\. Do not forget that such a replacement can significantly increase the amount of memory consumed by the program\. Moreover, an increase in the amount of required memory can slow down the application, because there will be fewer objects in the cache\.

So, the introduction of _ptrdiff\_t_ and _size\_t_ types into the legacy code is a task of gradual thoughtful refactoring that requires a lot of time\. In fact, it is necessary to review the entire code and make the necessary edits\. This approach is actually too expensive and inefficient\. It's better to choose one of 2 following options:

1. Use specialized tools such as [PVS\-Studio](https://pvs-studio.com/en/pvs-studio/)\. This is a static code analyzer that [detects](https://pvs-studio.com/en/docs/warnings/#64CPP) places where it's better to change data types so that the program works correctly and efficiently on 64\-bit systems\.
1. If you don't plan to adapt a 32\-bit program for 64\-bit systems, then there's no point in refactoring data types\. A 32\-bit program will not benefit from _ptrdiff\_t_ and _size\_t_ types\.

## References

1. Andrey Karpov, Evgenii Ryzhkov\. [Lessons on the development of 64\-bit C and C\+\+ applications](https://pvs-studio.com/en/blog/lessons/)\.
1. Andrey Karpov\. [A 64\-bit horse that can count](https://pvs-studio.com/en/blog/posts/cpp/a0043/)\.
1. Andrey Karpov, Evgenii Ryzhkov\. [20 issues of porting C\+\+ code to the 64\-bit platform](https://pvs-studio.com/en/blog/posts/cpp/a0004/)\.
1. Andrey Karpov\. [Safety of 64\-bit code](https://pvs-studio.com/en/blog/posts/cpp/a0046/)\.
1. Andrey Karpov, Evgenii Ryzhkov\. [Traps detection during migration of C and C\+\+ code to 64\-bit Windows](https://pvs-studio.com/en/blog/posts/cpp/a0012/)\.
1. Andrey Karpov\. [Undefined behavior is closer than you think](https://pvs-studio.com/en/blog/posts/cpp/0374/)\.
1. Andrey Karpov, Evgenii Ryzhkov\. [Development of resource\-intensive applications in Visual C\+\+](https://pvs-studio.com/en/blog/posts/a0018/)\.