Skip to content

Commit b3e9fa0

Browse files
committed
C++ std::size_t: std::uintptr_t, std::intptr_t, std::string::npos
1 parent 4df2ba3 commit b3e9fa0

1 file changed

Lines changed: 56 additions & 14 deletions

File tree

_articles/c++_std_size_t.md

Lines changed: 56 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ for (unsigned int i = 0; i < largeVector.size(); ++i) { ... }
3030

3131
## Héritage du C: ``size_t``
3232

33-
Avant le C++, le **langage C** a introduit [**``size_t``**](https://en.cppreference.com/w/c/types/size_t.html) (via [``<stddef.h>``](https://en.cppreference.com/w/c/header/stddef.html)) comme **type standard pour toute manipulation de tailles mémoire**. Le standard **POSIX** (fondé sur le C) l'a ensuite intégré et intégré dans ses propres interfaces, notamment via le header système [``<sys/types.h>``](https://man7.org/linux/man-pages/man3/size_t.3type.html).
33+
Avant le C++, le **langage C** a introduit [**``size_t``**](https://en.cppreference.com/w/c/types/size_t.html) (via [``<stddef.h>``](https://en.cppreference.com/w/c/header/stddef.html)) comme **type standard pour toute manipulation de tailles mémoire**. Le standard **POSIX** (fondé sur le C) l'utilise dans ses propres interfaces, notamment via le header système [``<sys/types.h>``](https://man7.org/linux/man-pages/man3/size_t.3type.html).
3434

3535
Il est omniprésent dans la bibliothèque standard C:
3636
- **Allocation**: [``void* malloc(size_t size);``](https://man7.org/linux/man-pages/man3/malloc.3.html)
@@ -175,12 +175,12 @@ for (std::size_t i = v.size() - 1; i >= 0; --i) { ... }
175175

176176
Le type [**``ssize_t``**](https://man7.org/linux/man-pages/man3/size_t.3type.html) est un type historique des systèmes **POSIX** (Linux/Unix). Il est couramment utilisé dans les fonctions système (comme [``read``](https://man7.org/linux/man-pages/man2/read.2.html) ou [``write``](https://man7.org/linux/man-pages/man2/write.2.html)) pour retourner soit une taille, soit une erreur (via une valeur négative).
177177

178-
| type | type réel | Portabilité |
179-
| --------- | :----------------------: | ------------------------------ |
180-
| `size_t` | *implementation-defined* | oui (standard, ISO C et POSIX) |
181-
| `ssize_t` | *implementation-defined* | non (POSIX uniquement) |
182-
| `SIZE_T` | ULONG_PTR | non (API Windows) |
183-
| `SSIZE_T` | ULONG_PTR | non (API Windows) |
178+
| type | type réel | Portabilité |
179+
| --------- | :----------------------: | ----------------------------------- |
180+
| `size_t` | *implementation-defined* | oui (standard, ISO&nbsp;C et POSIX) |
181+
| `ssize_t` | *implementation-defined* | non (POSIX uniquement) |
182+
| `SIZE_T` | ULONG_PTR | non (API Windows) |
183+
| `SSIZE_T` | ULONG_PTR | non (API Windows) |
184184

185185
``ssize_t`` n'est donc **pas un bon candidat** pour représenter une **différence/distance** en C.
186186

@@ -256,25 +256,36 @@ Il peut être formellement défini via l'expression suivante:
256256
using ptrdiff_t = decltype(static_cast<int*>(nullptr) - static_cast<int*>(nullptr));
257257
{% endhighlight %}
258258

259-
| type | type réel | Portabilité |
260-
| :--- | :---: | :--- |
261-
| **``std::size_t``** | *implementation-defined* | oui (standard ISO C++) |
262-
| **``std::ptrdiff_t``** | *implementation-defined* | oui (standard ISO C++) |
259+
| type | type réel | Portabilité | Usage sémantique |
260+
| :--- | :---: | :--- | :--- |
261+
| **``std::size_t``** | *implementation-defined* | oui (standard ISO&nbsp;C++) | **Indexes**, **quantités** et **tailles** d'objets dans un conteneur ou tableau |
262+
| **``std::ptrdiff_t``** | *implementation-defined* | oui (standard ISO&nbsp;C++) | **Différences** entre **deux pointeurs** |
263263

264264
Sa largeur est garantie de faire au moins **17 bits** ([The bit width of std::ptrdiff_t is not less than 17. (since C++11)](https://en.cppreference.com/w/cpp/types/ptrdiff_t.html))
265265

266266
> Concernant les **plages de valeurs** de ``std::size_t`` et ``std::ptrdiff_t``, le standard C++ donne les mêmes garanties que le standard C.<br>
267267
> ``std::size_t`` n'est **pas formellement compris** dans ``std::ptrdiff_t``, mais ce n'est **pas un problème** pour autant. Nous en avons parlé [**ici**](#limitation-de-la-plage-de-valeurs).
268268
269+
### ``std::uintptr_t`` et ``std::intptr_t``
270+
271+
Bien que ``std::size_t`` soit souvent utilisé pour des **offset mémoire** au sein d'un même objet (comme avec la macro [``offsetof``](https://en.cppreference.com/w/cpp/types/offsetof.html) qui donne un ``std::size_t``), il n'est pas destiné à faire des calculs d'adresses complexes. Pour convertir un pointeur en entier afin d'effectuer de l'arithmétique bas niveau (masquage de bits, calcul d'alignement, etc), préférez [**``std::uintptr_t``**](https://en.cppreference.com/w/cpp/types/integer.html) ou [**``std::intptr_t``**](https://en.cppreference.com/w/cpp/types/integer.html), qui est garanti d'être assez large pour contenir un pointeur.
272+
273+
| type | type réel | Portabilité | Usage sémantique |
274+
| :--- | :---: | :--- | :--- |
275+
| **``std::size_t``** | *implementation-defined* | oui (standard ISO&nbsp;C++) | **Indexes**, **quantités** et **tailles** d'objets dans un conteneur ou tableau |
276+
| **``std::ptrdiff_t``** | *implementation-defined* | oui (standard ISO&nbsp;C++) | **Différences** entre **deux pointeurs** |
277+
| **``std::uintptr_t``** | *implementation-defined* | oui (standard ISO&nbsp;C++) | **Arithmétique sur pointeurs** |
278+
| **``std::intptr_t``** | *implementation-defined* | oui (standard ISO&nbsp;C++) | **Arithmétique** sur pointeurs avec valeurs **négatives** |
279+
269280
## Dans la STL (Standard Template Library)
270281

271282
Les conteneurs de la STL (``vector``, ``list``, ``string``, etc) définissent des **alias internes** pour **garantir la généricité** du code.
272283

273284
Ils sont **visibles en public** dans les classes, et dans presque toutes les **signatures de fonctions** membres:
274285

275-
- **``T::size_type``**: Type non signé pour représenter le **nombre d'éléments stockés**. C'est notamment le type de retour de la méthode [**``std::vector<T>::size()``**](https://en.cppreference.com/w/cpp/container/vector/size) et le type attendu par l'opérateur [**``std::vector<T>::operator[]``**](https://en.cppreference.com/w/cpp/container/vector/operator_at).
286+
- **``T::size_type``**: Type non signé pour représenter le **nombre d'éléments stockés**. C'est notamment le type de retour de la méthode [**``std::vector<T>::size()``**](https://en.cppreference.com/w/cpp/container/vector/size) et le type attendu par l'opérateur [**``std::vector<T>::operator[]``**](https://en.cppreference.com/w/cpp/container/vector/operator_at). Ce type est très souvent ``std::size_t`` par défaut.
276287

277-
- **``T::difference_type``**: Type signé pour les distances. C'est le type retourné par l'opérateur de **soustraction entre deux itérateurs** (``it2 - it1``) ou par la fonction [**``std::distance``**](https://en.cppreference.com/w/cpp/iterator/distance).
288+
- **``T::difference_type``**: Type signé pour les distances. C'est le type retourné par l'opérateur de **soustraction entre deux itérateurs** (``it2 - it1``) ou par la fonction [**``std::distance``**](https://en.cppreference.com/w/cpp/iterator/distance). Ce type est très souvent ``std::ptrdiff_t`` par défaut.
278289

279290
{% highlight cpp %}
280291
std::vector<int> numbers = {10, 20, 30};
@@ -301,6 +312,37 @@ auto distance = numbers.end() - numbers.begin();
301312

302313
Il prend le type retourné par les fonctions, [**sans risque de conversion maladroite**](#auto-complique-la-lecture-du-code).
303314

315+
### Valeur sentinelle de ``std::string``
316+
317+
318+
La fonction [``std::string::find``](https://en.cppreference.com/w/cpp/string/basic_string/find.html) retourne une valeur de type ``std::string::size_type``. Cet alias correspond à ``std::allocator_traits<Allocator>::size_type``, dont le type réel est **systématiquement [``std::size_t``](https://en.cppreference.com/w/cpp/memory/allocator.html)** pour [l'allocateur par défaut](https://en.cppreference.com/w/cpp/memory/allocator.html).
319+
320+
> Pour être exact, ``std::string`` est un alias de la classe template [``std::basic_string<CharT, Traits, Allocator>``](https://en.cppreference.com/w/cpp/string/basic_string.html). Cette précision est utile car les types que nous allons manipuler en dépendent.
321+
322+
Cette fonction [``std::string::find``](https://en.cppreference.com/w/cpp/string/basic_string/find.html) retourne la **position de l'élément trouvé**:
323+
324+
{% highlight cpp %}
325+
auto string = std::string{"Hello World!"};
326+
std::size_t position = string.find("World"); // position vaut 5
327+
{% endhighlight %}
328+
329+
La STL propose une **valeur sentinelle** pour indiquer que **la chaîne recherchée n'a pas été trouvée**:
330+
{% highlight cpp linenos highlight_lines="4" %}
331+
auto string = std::string{"Hello World!"};
332+
std::size_t position = string.find("Word"); // position vaut std::string::npos
333+
334+
if (position == std::string::npos)
335+
{
336+
std::println("Chaîne non trouvée");
337+
}
338+
{% endhighlight %}
339+
340+
[**``std::string::npos``**](https://en.cppreference.com/w/cpp/string/basic_string/npos) est une constante de type **``std::size_t``**. Cette **valeur sentinelle** vaut **``-1``**.
341+
342+
``-1`` dans un type non-signé ? C'est parfaitement légal: [dans l'**arithmétique non signée**, les **overflow/underflow** ont la **garantie de boucler**](#le-mélange-signé--non-signé). ``-1`` devient donc **la plus grande valeur possible** de ``std::size_t`` (qu'on ne peut pas formellement citer car le standard [ne garantit pas de largeur exacte pour ce type](#dépendance-à-labi-et-au-modèle-de-données)).
343+
344+
Ce mécanisme, parfois source de bugs, est utilisé ici pour **garantir une valeur sentinelle impossible à atteindre** pour une taille réelle de chaîne.
345+
304346
## Literals (Depuis C++23)
305347

306348
Le C++23 introduit des [**literals**](/articles/c++/literals#size-literal-c23) pour manipuler ces types sans conversion implicite:
@@ -532,7 +574,7 @@ Les [C++ Core Guidelines](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuid
532574

533575
### Ne mélangez pas signé et non signé (ES.100)
534576

535-
Le principe est simple: [**Don't mix signed and unsigned arithmetic**](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#res-mix). Le mélange provoque des **conversions silencieuses** et des bugs difficiles à tracer. Nous l'avons illustré avec les [frictions de Qt](#les-comparaisons-mixtes).
577+
Le principe est simple: Ne mélangez pas l'arithmétique signée et non-signée ([**Don't mix signed and unsigned arithmetic**](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#res-mix)). Le mélange provoque des **conversions silencieuses** et des bugs difficiles à tracer. Nous l'avons illustré avec les [frictions de Qt](#les-comparaisons-mixtes).
536578

537579
### Préférez le signé pour les index (ES.107)
538580

0 commit comments

Comments
 (0)