802

I am getting confused with size_t in C. I know that it is returned by the sizeof operator. But what exactly is it? Is it a data type?

Let's say I have a for loop:

for(i = 0; i < some_size; i++)

Should I use int i; or size_t i;?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Vijay
  • 65,327
  • 90
  • 227
  • 319
  • 17
    If those are your only options, use `int` if `some_size` is signed, `size_t` if it is unsigned. – Nate Mar 31 '10 at 05:59
  • 10
    @Nate That is incorrect. POSIX has a ssize_t type but the actually correct type to use is ptrdiff_t. – Molly Stewart-Gallus Mar 19 '17 at 18:46
  • 6
    The answers are not as clear as in [Low-Level Programming: C, Assembly, and Program Execution on Intel® 64](https://books.google.pt/books?id=qjUqDwAAQBAJ&pg=PA156&dq=size_t+array+index&hl=en&sa=X&ved=0ahUKEwiJv9WK5dHnAhV15OAKHbxhC2kQ6AEIOzAC#v=onepage&q=size_t%20array%20index&f=false). As stated in the book, using an index `int i` may not be enough to address a huge array. So by using `size_t i` you can address more indices, so even if you have a huge array that should not be a problem. `size_t` is a data type: usually a `unsigned long int` but this depends on your system. – bruno Feb 14 '20 at 20:06

15 Answers15

563

From Wikipedia:

According to the 1999 ISO C standard (C99), size_t is an unsigned integer type of at least 16 bit (see sections 7.17 and 7.18.3).

size_tis an unsigned data type defined by several C/C++ standards, e.g. the C99 ISO/IEC 9899 standard, that is defined in stddef.h.1 It can be further imported by inclusion of stdlib.h as this file internally sub includes stddef.h.

This type is used to represent the size of an object. Library functions that take or return sizes expect them to be of type or have the return type of size_t. Further, the most frequently used compiler-based operator sizeof should evaluate to a constant value that is compatible with size_t.

As an implication, size_t is a type guaranteed to hold any array index.

Community
  • 1
  • 1
sblom
  • 26,911
  • 4
  • 71
  • 95
  • 7
    "Library functions that take or return sizes expect them to be of type ... size_t" Except that stat() uses off_t for the size of a file – Draemon May 26 '10 at 22:12
  • 77
    @Draemon That comment reflects a fundamental confusion. `size_t` is for objects in memory. The C standard doesn't even define `stat()` or `off_t` (those are POSIX definitions) or anything to do with disks or file systems - it stops itself at `FILE` streams. Virtual memory management is completely different from file systems and file management as far as size requirements go, so mentioning `off_t` is irrelevant here. – jw013 Jun 10 '13 at 19:57
  • 4
    @jw013: I'd hardly call it a fundamental confusion, but you make an interesting point. Still, the quoted text doesn't say "sizes of in-memory objects", and "offset" is hardly a good name for a size type regardless of where it happens to be stored. – Draemon Jun 13 '13 at 22:50
  • 45
    @Draemon Good point. This answer quotes Wikipedia, which in this case doesn't have the best explanation, in my opinion. The C standard itself is much more clear: it defines `size_t` as the type of the result of the `sizeof` operator (7.17p2 about ``). Section 6.5 explains exactly how C expressions work (6.5.3.4 for `sizeof`). Since you cannot apply `sizeof` to a disk file (mostly because C doesn't even define how disks and files work), there is no room for confusion. In other words, blame Wikipedia (and this answer for quoting Wikipedia and not the actual C standard). – jw013 Jun 13 '13 at 22:57
  • 3
    @Draemon - I would also agree with the "fundamental confusion" assessment. If you haven't read the C/C++ standards, you might think "object" refers to "object oriented programming," which it does not. Read the C standard, which has none of those OOP objects, but yet has objects, and find out. The answer may surprise you! – Heath Hunnicutt Oct 29 '13 at 02:26
  • It's also worth noting that `size_t` is (almost certainly) a typedef for some existing unsigned integer type. For example, `size_t` is commonly the same type as `unsigned long` (a typedef creates an alias for an existing type, not a new type). But you shouldn't *assume* that `size_t` is the same as any particular type, since it can vary from system to system. – Keith Thompson Aug 06 '15 at 19:06
  • so 16 bit means its just an unsigned short? – Ungeheuer Mar 31 '17 at 00:22
  • Well, sizeof tells me that it's 8 bytes, so definitely bigger than 16 bits, at least for me. – Ungeheuer Mar 31 '17 at 00:31
  • 1
    Could you please update the answer to tackle the second part of the question 'Should I use int i; or size_t i;?' – Manuel Selva Sep 16 '19 at 21:08
  • @KeithThompson: If `size_t` weren't a typedef for some already-existing integer type, what type of result could `sizeof` yield in a compilation unit that doesn't include stddef.h? – supercat Aug 12 '21 at 21:10
  • @supercat `sizeof` yields a result of some implementation-defined unsigned integer type. For example, a particular compiler might have `sizeof` yield `unsigned long`. The compiler doesn't need to care about the name `size_t` or about `stddef.h`; it just generates code to yield an `unsigned long` value. It's difficult to imagine how `size_t` could be defined as anything other than a typedef for some existing integer type, but the standard doesn't explicitly say it's a typedef. As far as I know it's a typedef in all existing implementations. – Keith Thompson Aug 12 '21 at 21:19
  • The definition in `` is how the implementation tells the user what the type is. – Keith Thompson Aug 12 '21 at 21:19
  • @KeithThompson: The type `size_t` is required to behave in a manner indistinguishable from the type produced by the `sizeof` operator, and the latter must exist whether or not `stddef.h` is included. Given a declaration like `typedef int woozle; woozle x;` a compiler could, for purposes of internal record keeping, treat `woozle` as a new type which behaves in a manner identical to `int`, but as far as the Standard is concerned, `x` would be of type `int`. – supercat Aug 12 '21 at 22:53
  • @KeithThompson: BTW, it's too bad the Standard didn't include a `typeof` operator (which would have enhanced the usefulness of, but been more useful than, the `offsetof` macro), since the definition of `size_t` could then be standardized to `typeof sizeof 1`, and `ptrdiff_t` to `typeof (""-"")`. – supercat Aug 12 '21 at 22:56
  • @supercat The standard says, "The value of the result of both operators [`sizeof` and `_Alignof`] is implementation-defined, and its type (an unsigned integer type) is **`size_t`**, defined in **``** (and other headers)." Given `typedef int woozle;`, `int` and `woozle` are quite simply the same type. If a compiler internally treats `int` and `woozle` as distinct *thingies*, then a *thingy* is not what C calls a "type". A `typeof` operator could be useful, but figuring out how to define `size_t` when creating a `` header is a solved problem. – Keith Thompson Aug 13 '21 at 00:14
  • @Ungeheuer, 16 bits doesn't mean unsigned short, it's platform dependent. But the quote says "at least 16 bits," although the actual standard doesn't say anything about 16 bits that I can find. It states, regarding the minimum size of `size_t`: "SIZE_MAX >= 65535", which _implies_ 16 bits or more since C99. – SO_fix_the_vote_sorting_bug Nov 15 '22 at 23:15
281

size_t is an unsigned type. So, it cannot represent any negative values(<0). You use it when you are counting something, and are sure that it cannot be negative. For example, strlen() returns a size_t because the length of a string has to be at least 0.

In your example, if your loop index is going to be always greater than 0, it might make sense to use size_t, or any other unsigned data type.

When you use a size_t object, you have to make sure that in all the contexts it is used, including arithmetic, you want non-negative values. For example, let's say you have:

size_t s1 = strlen(str1);
size_t s2 = strlen(str2);

and you want to find the difference of the lengths of str2 and str1. You cannot do:

int diff = s2 - s1; /* bad */

This is because the value assigned to diff is always going to be a positive number, even when s2 < s1, because the calculation is done with unsigned types. In this case, depending upon what your use case is, you might be better off using int (or long long) for s1 and s2.

There are some functions in C/POSIX that could/should use size_t, but don't because of historical reasons. For example, the second parameter to fgets should ideally be size_t, but is int.

Gaurang Tandon
  • 6,504
  • 11
  • 47
  • 84
Alok Singhal
  • 93,253
  • 21
  • 125
  • 158
  • 19
    @Alok: Two questions: 1) what is the size of [`size_t`](http://codepad.org/osT7Ex1q#output)? 2) why should I prefer `size_t` over something like `unsigned int`? – Lazer Jun 08 '10 at 18:41
  • 1
    `size_t` isn't guaranteed to be the same thing as `unsigned int` (you seem to be implying that they're the same). – Brendan Long Jun 08 '10 at 19:11
  • 3
    @Lazer: the size of `size_t` is `sizeof(size_t)`. The C standard guarantees that `SIZE_MAX` will be at least 65535. `size_t` is the type returned by `sizeof` operator, and is used in the standard library (for example `strlen` returns `size_t`). As Brendan said, `size_t` need not be the same as `unsigned int`. – Alok Singhal Jun 09 '10 at 05:56
  • @Alok, @Brendan Long: When you say `size_t` is not the same as `unsigned int`, I guess you mean that it is not guaranteed to be an `int`, though it actually is `unsigned`. Is that true? – Lazer Jun 13 '10 at 12:05
  • 5
    @Lazer - yes, `size_t` is guaranteed to be an unsigned type. – Alok Singhal Jun 13 '10 at 14:37
  • s2 - s1 isn't always going to be a positive number. – jsimmons Jun 13 '11 at 04:05
  • @jsimmons: that was my point. If `s1` and `s2` are both of an unsigned type (`size_t` for example), the result `s1-s2` *cannot* be negative, even when `s1 < s2`. This is because unsigned types don't have negative values. When you assign that difference to an `int`, the result is implementation-defined. That was the whole point about saying that one doesn't want to take the difference of the unsigned types in my example, because one has to be able to portably get the possible negative difference. – Alok Singhal Jun 13 '11 at 23:35
  • 6
    Keep in mind that on 64-bit Linux int is always 32-bit but size_t is 64-bit. So size_t and int are NOT interchangeable. – dtoux Oct 30 '13 at 02:41
  • 1
    If for what ever reasons you need size to be signed, there is ssize_t type on Linux. – dtoux Oct 30 '13 at 02:41
  • This is not how two's complement works! `size_t s2 = 5, s1 = 20; int diff = s2 - s1; printf("%d: %x\n", diff, diff < 0);` This prints exactly what you'd expect: `"-15: 1"` – Jason Oster Jul 01 '15 at 22:44
  • 4
    @JasonOster, two's complement is not a requirement in the C standard. If the value of `s2 - s1` overflows an `int`, the behavior is undefined. – Alok Singhal Jul 06 '15 at 00:29
  • 1
    @AlokSinghal To be pedantic about the C specification, you are correct. The reality is that one's complement is limited to a small number of historical mainframes and a few specialty applications like ADCs. Two's complement is so prevalent, in fact, that it is by now a _de facto_ standard. Integer overflows can be dangerous, but the scope goes beyond mixing signs. – Jason Oster Jul 06 '15 at 04:39
  • @AlokSinghal `s2 - s1` is not an overflow because both operands are unsigned. The conversion that happens when assigned to `int` is implementation defined, and in practice well defined. – xiver77 Jun 09 '22 at 01:43
  • The implementation defined behaviour also covers the case when the original unsigned value does not fit in an `int`. – xiver77 Jun 09 '22 at 01:45
106

size_t is a type that can hold any array index.

Depending on the implementation, it can be any of:

unsigned char

unsigned short

unsigned int

unsigned long

unsigned long long

Here's how size_t is defined in stddef.h of my machine:

typedef unsigned long size_t;
Arjun Sreedharan
  • 11,003
  • 2
  • 26
  • 34
  • 5
    @chux: Indeed, just because one implementation defines it as such doesn't mean all do. Case in point: 64-bit Windows. `unsigned long` is 32-bit, `size_t` is 64-bit. – Tim Čas Dec 28 '14 at 21:40
  • 1
    is it true that `size_t` is always 32bits on 32-bits machine, 64bits likewise? – John Wu Aug 24 '16 at 07:09
  • "According to the 1999 ISO C standard (C99), size_t is an unsigned integer type of at least 16 bit (see sections 7.17 and 7.18.3)." So it can't be an `unsigned char`? – jameshfisher Nov 29 '16 at 22:35
  • @jameshfisher I am not sure the 16 bit restriction is true. `uint_least16_t` is what's at least 16 bits. About, `size_t`, the standard says "unsigned integral type of the result of the sizeof operator" and "The sizeof operator yields the size (in bytes) of its operand". – Arjun Sreedharan Nov 30 '16 at 05:51
  • 4
    @jameshfisher who says `unsigned char` cannot be 16 bits?! – Antti Haapala -- Слава Україні Oct 16 '17 at 07:52
93

If you are the empirical type,

echo | gcc -E -xc -include 'stddef.h' - | grep size_t

Output for Ubuntu 14.04 64-bit GCC 4.8:

typedef long unsigned int size_t;

Note that stddef.h is provided by GCC and not glibc under src/gcc/ginclude/stddef.h in GCC 4.2.

Interesting C99 appearances

  • malloc takes size_t as an argument, so it determines the maximum size that may be allocated.

    And since it is also returned by sizeof, I think it limits the maximum size of any array.

    See also: What is the maximum size of an array in C?

Community
  • 1
  • 1
Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
  • Is the first command supposed to be `-x c` with a space? And to anyone else who didn't understand the command at first, if I understand correctly, the `-` argument reads from standard input, hence the pipe from `echo` to get an empty source file. – Danny Mar 31 '23 at 20:08
28

The manpage for types.h says:

size_t shall be an unsigned integer type

codaddict
  • 445,704
  • 82
  • 492
  • 529
26

To go into why size_t needed to exist and how we got here:

In pragmatic terms, size_t and ptrdiff_t are guaranteed to be 64 bits wide on a 64-bit implementation, 32 bits wide on a 32-bit implementation, and so on. They could not force any existing type to mean that, on every compiler, without breaking legacy code.

A size_t or ptrdiff_t is not necessarily the same as an intptr_t or uintptr_t. They were different on certain architectures that were still in use when size_t and ptrdiff_t were added to the Standard in the late 1980s, and becoming obsolete when C99 added many new types but not gone yet (such as 16-bit Windows). The x86 in 16-bit protected mode had a segmented memory where the largest possible array or structure could be only 65,536 bytes in size, but a far pointer needed to be 32 bits wide, wider than the registers. On those, intptr_t would have been 32 bits wide but size_t and ptrdiff_t could be 16 bits wide and fit in a register. And who knew what kind of operating system might be written in the future? In theory, the i386 architecture offers a 32-bit segmentation model with 48-bit pointers that no operating system has ever actually used.

The type of a memory offset could not be long because far too much legacy code assumes that long is exactly 32 bits wide. This assumption was even built into the UNIX and Windows APIs. Unfortunately, a lot of other legacy code also assumed that a long is wide enough to hold a pointer, a file offset, the number of seconds that have elapsed since 1970, and so on. POSIX now provides a standardized way to force the latter assumption to be true instead of the former, but neither is a portable assumption to make.

It couldn’t be int because only a tiny handful of compilers in the ’90s made int 64 bits wide. Then they really got weird by keeping long 32 bits wide. The next revision of the Standard declared it illegal for int to be wider than long, but int is still 32 bits wide on most 64-bit systems.

It couldn’t be long long int, which anyway was added later, since that was created to be at least 64 bits wide even on 32-bit systems.

So, a new type was needed. Even if it weren’t, all those other types meant something other than an offset within an array or object. And if there was one lesson from the fiasco of 32-to-64-bit migration, it was to be specific about what properties a type needed to have, and not use one that meant different things in different programs.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Davislor
  • 14,674
  • 2
  • 34
  • 49
  • 1
    Disagree with "`size_t` and `ptrdiff_t` are guaranteed to be 64 bits wide on a 64-bit implementation", etc. The _guarantee_ is overstated. The range of `size_t` is primarily driven by the memory capacity of the implementation. "a n-bit implementation" is primarily the native processor width of integers. Certainly many implementations use a similar size memory and processor bus width, but wide native integers with scant memory or narrow processors with lots of memory exist and do drive these two implementation properties apart. – chux - Reinstate Monica Jan 23 '20 at 23:02
  • No. To say 'a n-bit implementation is primarily the native processor width of integers' is just plain wrong. An n-bit implementation in the context of the C compiler refers to the width of a pointer on the OS (or more correctly, the width of a pointer in the current architecture mode of the OS that the software intends to run in, such as the case of compilation of a 32-bit app for a 64-bit OS with a 32-bit compatibility mode), regardless of hardware. – Nathan Wiebe Nov 20 '21 at 14:59
  • 64-bit capable hardware has been around for a long time, but some instances still call for either a) running code compiled for an OS's 32-bit compatibility mode (aka a 32-bit application on a 64-bit OS), or even b) reverting to a 32-bit OS. This is not uncommon because a lot of legacy apps have not been thoroughly refactored and retested to ensure pointer manipulation never makes any 32-bit assumptions, thus truncating the upper 32-bits of a pointer (something that used to be common, but it's now completely avoidable thanks to tools like size_t and uintptr_t.). – Nathan Wiebe Nov 20 '21 at 14:59
20

Since nobody has yet mentioned it, the primary linguistic significance of size_t is that the sizeof operator returns a value of that type. Likewise, the primary significance of ptrdiff_t is that subtracting one pointer from another will yield a value of that type. Library functions that accept it do so because it will allow such functions to work with objects whose size exceeds UINT_MAX on systems where such objects could exist, without forcing callers to waste code passing a value larger than "unsigned int" on systems where the larger type would suffice for all possible objects.

supercat
  • 77,689
  • 9
  • 166
  • 211
  • My question has always been: If sizeof never existed, would there be a need for size_t? – Dean P May 01 '19 at 09:56
  • @DeanP: Perhaps not, though there would then be a question of what argument type should be used for things like `malloc()`. Personally, I would have liked to have seen versions which take arguments of type `int`, `long`, and `long long`, with some implementations promoting shorter types and others implementing e.g. `lmalloc(long n) {return (n < 0 || n > 32767) ? 0 : imalloc(n);}` [on some platforms, calling to `imalloc(123)` would be cheaper than calling `lmalloc(123);`, and even on a platform where `size_t` is 16 bits, code which wants to allocate size computed in a ` long` value... – supercat May 01 '19 at 15:07
  • ...should be able to rely upon the allocation failing if the value is bigger than the allocator can handle. – supercat May 01 '19 at 15:08
10

size_t and int are not interchangeable. For instance on 64-bit Linux size_t is 64-bit in size (i.e. sizeof(void*)) but int is 32-bit.

Also note that size_t is unsigned. If you need signed version then there is ssize_t on some platforms and it would be more relevant to your example.

As a general rule I would suggest using int for most general cases and only use size_t/ssize_t when there is a specific need for it (with mmap() for example).

dtoux
  • 1,754
  • 3
  • 21
  • 38
9

size_t is an unsigned integer data type which can assign only 0 and greater than 0 integer values. It measure bytes of any object's size and is returned by sizeof operator.

const is the syntax representation of size_t, but without const you can run the program.

const size_t number;

size_t regularly used for array indexing and loop counting. If the compiler is 32-bit it would work on unsigned int. If the compiler is 64-bit it would work on unsigned long long int also. There for maximum size of size_t depending on the compiler type.

size_t already defined in the <stdio.h> header file, but it can also be defined by the <stddef.h>, <stdlib.h>, <string.h>, <time.h>, and <wchar.h> headers.

Example (with const)

#include <stdio.h>

int main()
{
    const size_t value = 200;
    size_t i;
    int arr[value];

    for (i = 0 ; i < value ; ++i)
    {
        arr[i] = i;
    }

    size_t size = sizeof(arr);
    printf("size = %zu\n", size);
}

Output: size = 800


Example (without const)

#include <stdio.h>

int main()
{
    size_t value = 200;
    size_t i;
    int arr[value];

    for (i = 0; i < value; ++i)
    {
        arr[i] = i;
    }

    size_t size = sizeof(arr);
    printf("size = %zu\n", size);
}

Output: size = 800

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Kalana
  • 5,631
  • 7
  • 30
  • 51
7

size_t is a typedef which is used to represent the size of any object in bytes. (Typedefs are used to create an additional name/alias for another data type, but does not create a new type.)

Find it defined in stddef.h as follows:

typedef unsigned long long size_t;

size_t is also defined in the <stdio.h>.

size_t is used as the return type by the sizeof operator.

Use size_t, in conjunction with sizeof, to define the data type of the array size argument as follows:

#include <stdio.h>

void disp_ary(int *ary, size_t ary_size)
{
    for (int i = 0; i < ary_size; i++)
    {
        printf("%d ", ary[i]);
    }
}
 
int main(void)
{
    int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
    int ary_size = sizeof(arr)/sizeof(int);
    disp_ary(arr, ary_size);
    return 0;
}

size_t is guaranteed to be big enough to contain the size of the biggest object the host system can handle.

Note that an array's size limitation is really a factor the system's stack size limitations where this code is compiled and executed. You should be able to adjust the stack size at link time (see ld commands's --stack-size parameter).

To give you an idea of approximate stack sizes:

  • 4K on an embedded device
  • 1M on Win10
  • 7.4M on Linux

Many C library functions like malloc, memcpy and strlen declare their arguments and return type as size_t.

size_t affords the programmer with the ability to deal with different types, by adding/subtracting the number of elements required instead of using the offset in bytes.

Let's get a deeper appreciate for what size_t can do for us by examining its usage in pointer arithmetic operations of a C string and an integer array:

Here's an example using a C string:

const char* reverse(char *orig)
{
  size_t len = strlen(orig);
  char *rev = orig + len - 1;
  while (rev >= orig)
  {
    printf("%c", *rev);
    rev = rev - 1;  // <= See below
  }
  return rev;
}

int main() {
  char *string = "123";
  printf("%c", reverse(string));
}
// Output: 321

0x7ff626939004 "123"  // <= orig
0x7ff626939006 "3"    // <= rev - 1 of 3
0x7ff626939005 "23"   // <= rev - 2 of 3
0x7ff626939004 "123"  // <= rev - 3 of 3
0x7ff6aade9003 ""     // <= rev is indeterminant. This can be exploited as an out of bounds bug to read memory contents that this program has no business reading.

That's not very helpful in understanding the benefits of using size_t since a character is one byte, regardless of your architecture.

When we're dealing with numerical types, size_t becomes very beneficial.

size_t type is like an integer with benefits that can hold a physical memory address; That address changes its size according to the type of platform in which it is executed.

Here's how we can leverage sizeof and size_t when passing an array of ints:

void print_reverse(int *orig, size_t ary_size)
{
  int *rev = orig + ary_size - 1;
  while (rev >= orig)
  {
    printf("%i", *rev);
    rev = rev - 1;
  }
}

int main()
{
  int nums[] = {1, 2, 3};
  print_reverse(nums, sizeof(nums)/sizeof(*nums));

  return 0;
}

0x617d3ffb44 1  // <= orig
0x617d3ffb4c 3  // <= rev - 1 of 3
0x617d3ffb48 2  // <= rev - 2 of 3
0x617d3ffb44 1  // <= rev - 3 of 3

Above, we see than an int takes 4 bytes (and since there are 8 bits per byte, an int occupies 32 bits).

If we were to create an array of longs we'd discover that a long takes 64 bits on a linux64 operating system, but only 32 bits on a Win64 system. Hence, using t_size, will save a lot of coding and potential bugs, especially when running C code that performs Address Arithmetic on different architectures.

So the moral of this story is "Use size_t and let your C-compiler do the error-prone work of pointer arithmetic."

l3x
  • 30,760
  • 1
  • 55
  • 36
4

size_t is unsigned integer data type. On systems using the GNU C Library, this will be unsigned int or unsigned long int. size_t is commonly used for array indexing and loop counting.

Prince
  • 61
  • 3
3

In general, if you are starting at 0 and going upward, always use an unsigned type to avoid an overflow taking you into a negative value situation. This is critically important, because if your array bounds happens to be less than the max of your loop, but your loop max happens to be greater than the max of your type, you will wrap around negative and you may experience a segmentation fault (SIGSEGV). So, in general, never use int for a loop starting at 0 and going upwards. Use an unsigned.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Mark
  • 1,124
  • 1
  • 14
  • 21
  • 5
    I cannot accept your argumentation. You say it is better that the overflow bug silently leads to accessing valid data within your array? – maf-soft May 10 '16 at 13:55
  • 2
    @maf-soft is correct. if the error goes undetected it makes it worse than a program crash. why did this answer got upvotes? – yoyo_fun Mar 15 '17 at 21:11
  • If it accesses valid data in your array then it's not a bug because unsigned type won't overflow at the limit signed type will. What is this logic guys? Let's say for some reason you use char to iterate over 256 element array... signed will overflow at 127 and 128th element will sigsegv, but if you use unsigned, then it will go through entire array as intended. Then again, when you are using an int, your arrays won't really be bigger than 2 billion elements so either way it does not matter... – Purple Ice Oct 28 '18 at 14:30
  • 3
    I can't imagine any situation in which integer overflow isn't a bug, whether it wraps around positive or negative. Just because you don't get a segfault doesn't mean you see correct behavior! And you can experience a segmentation fault, or not, whether your offset is positive or negative; it all depends on your memory layout. @PurpleIce, I don't think you're saying the same thing as this answer; your argument looks to be that you should choose a data type large enough to hold the largest value you want to put in it, which is just plain common sense. – Soren Bjornstad Apr 01 '19 at 11:17
  • 1
    That said, I do prefer using an unsigned type for loop indices *semantically*; if your variable is never going to be negative, then you might as well indicate that in the type you choose. It could also allow the compiler to spot a bug where the value ended up negative, though GCC at least is pretty terrible at spotting this particular mistake (on one occasion I initialized an unsigned to -1 and didn't get a warning). Similarly, a size_t is semantically appropriate for array indices. – Soren Bjornstad Apr 01 '19 at 11:17
3

This is a platform-specific typedef. For example, on a particular machine, it might be unsigned int or unsigned long. You should use this definition for more portability of your code.

Artem
  • 373
  • 2
  • 6
2

size_t or any unsigned type might be seen used as loop variable as loop variables are typically greater than or equal to 0.

When we use a size_t object, we have to make sure that in all the contexts it is used, including arithmetic, we want only non-negative values. For instance, following program would definitely give the unexpected result:

// C program to demonstrate that size_t or
// any unsigned int type should be used 
// carefully when used in a loop

#include<stdio.h>
int main()
{
const size_t N = 10;
int a[N];

// This is fine
for (size_t n = 0; n < N; ++n)
a[n] = n;

// But reverse cycles are tricky for unsigned 
// types as can lead to infinite loop
for (size_t n = N-1; n >= 0; --n)
printf("%d ", a[n]);
}

Output
Infinite loop and then segmentation fault
-4

From my understanding, size_t is an unsigned integer whose bit size is large enough to hold a pointer of the native architecture.

So:

sizeof(size_t) >= sizeof(void*)
newfurniturey
  • 37,556
  • 9
  • 94
  • 102
  • 17
    Not true. The pointer size can be bigger than the `size_t`. Several example: C compilers on x86 real mode can have 32 bit `FAR` or `HUGE` pointers but size_t is still 16 bits. Another example: Watcom C used to have a special fat pointer for extended memory that was 48 bits wide, but `size_t` was not. On embedded controller with Harvard architecture, you have no correlation either, because both concerns different address spaces. – Patrick Schlüter Jul 26 '13 at 12:26
  • 1
    And on that http://stackoverflow.com/questions/1572099/size-t-is-pointer-size-in-practice there are more examples AS/400 with 128 bit pointers and 32 bit `size_t` – Patrick Schlüter Jul 26 '13 at 12:30
  • 2
    This is blatantly false. However, let's keep it here – Antti Haapala -- Слава Україні Oct 16 '17 at 07:53