Is the max value of size_t (SIZE_MAX) defined relative to the other integer types?
Solution 1
The current C standard does not require size_t
to be at least as wide as an int
, and I'm skeptical about any version of the standard ever doing so. size_t
needs to be able to represent any number which might be the size of an object; if the implementation limits object sizes to be 24 bits wide, then size_t
could be a 24-bit unsigned type, regardless of what an int
is.
The GCC warning does not refer to theoretical possibilities. It is checking a particular hardware platform and a particular compiler and runtime. That means it sometimes triggers on code which is trying to be portable. (There are other cases where portable code will trigger optional GCC warnings.) That might not be what you were hoping the warning would do, but there are probably users whose expectations are precisely matched by the implemented behaviour, and the standard provides no guidelines whatsoever for compiler warnings.
As OP mentions in a comment, there is a long history related to this warning. The warning was introduced in version 3.3.2 or so (in 2003), apparently not controlled by any -W
flag. This was reported as bug 12963 by a user who evidently felt, as you do, that the warning discourages portable programming. As can be seen in the bug report, various GCC maintainers (and other well-known members of the community) weighed in with strongly-felt but conflicting opinions. (This is a common dynamic in open source bug reports.) After several years, the decision was made to control the warnings with a flag, and to not enable that flag by default or as part of -Wall
. In the meantime, the -W
option had been renamed -Wextra
, and the newly-created flag (-Wtype-limits
) was added to the -Wextra
collection. To me, this appears to be the correct resolution.
The remainder of this answer contains my personal opinion.
-Wall
, as documented in the GCC manual, does not actually enable all warnings. It enables those warnings "about constructions that some users consider questionable, and that are easy to avoid (or modify to prevent the warning), even in conjunction with macros." There are a number of other conditions which GCC can detect:
Note that some warning flags are not implied by
-Wall
. Some of them warn about constructions that users generally do not consider questionable, but which occasionally you might wish to check for; others warn about constructions that are necessary or hard to avoid in some cases, and there is no simple way to modify the code to suppress the warning. Some of them are enabled by-Wextra
but many of them must be enabled individually.
These distinctions are somewhat arbitrary. For example, I have to grit my teeth every time that GCC decides to "suggest parentheses around ‘&&’ within ‘||’". (It doesn't seem to feel the need to suggest parentheses around ´*´ within ´+´, which doesn't feel different to me.) But I recognize that all of us have different comfort levels with operator precedence, and not all of GCC's suggestions about parentheses seem excessive to me.
But on the whole, the distinction seems reasonable. There are warnings which are generally applicable, and those are enabled with -Wall
, which should always be specified because these warnings almost always demand action to correct a deficiency. There are other warnings which might be useful in particular circumstances, but which also have lots of false positive; these warnings need to be investigated individually because they do not always (or even often) correspond with a problem in your code.
I'm aware that there are people who feel that the mere fact that GCC knows how to warn about some condition is sufficient to demand action to avoid that warning. Everyone is entitled to their stylistic and aesthetic judgements, and it is right and just that such programmers add -Wextra
to their build flags. I am not in that crowd, however. At a given point in a project, I will try a build with a large collection of optional warnings enabled, and consider whether or not to modify my code on the basis of the reports, but I really don't want to spend my development time thinking about non-problems every time I rebuild a file. The -Wtypes-limit
flag falls into this category for me.
Solution 2
Nothing requires the maximum of size_t
to be larger than int
. Such architectures where SIZE_MAX
is <= INT_MAX
are rare though and I doubt GCC would support any of them.
As for the fix, you can use #if
:
#if INT_MAX > SIZE_MAX
if (u_value > SIZE_MAX) { /* Line 10 */
exit(EXIT_FAILURE);
}
#endif
Solution 3
I agree with Remo.D's interpretation.
size_t
is specified to be a standard unsigned integer, but the standard does not restrict its size relative to any of them other than by saying that it must be able to hold at least 65535
.
It can therefor be smaller, equal, or larger in size than unsigned int
.
Related videos on Youtube
Comments
-
Kevin J. Chase over 1 year
I'm writing a library of functions that will safely convert between various numeric types or die trying. My intent is roughly equal parts create-useful-library and learn-C-edge-cases.
My
int
-to-size_t
function is triggering a GCC-Wtype-limits
warning that claims I shouldn't test if anint
is greater thanSIZE_MAX
, because it will never be true. (Another function that convertsint
tossize_t
produces an identical warning aboutSSIZE_MAX
.)My MCVE, with extra comments and baby steps, is:
#include <stdint.h> /* SIZE_MAX */ #include <stdlib.h> /* exit EXIT_FAILURE size_t */ extern size_t i2st(int value) { if (value < 0) { exit(EXIT_FAILURE); } // Safe to cast --- not too small. unsigned int const u_value = (unsigned int) value; if (u_value > SIZE_MAX) { /* Line 10 */ exit(EXIT_FAILURE); } // Safe to cast --- not too big. return (size_t) u_value; }
The Compiler Warnings
I'm getting similar warnings from GCC 4.4.5 on Linux 2.6.34:
$ gcc -std=c99 -pedantic -Wall -Wextra -c -o math_utils.o math_utils.c math_utils.c: In function ‘i2st’: math_utils.c:10: warning: comparison is always false due to limited range of data type
...and also from GCC 4.8.5 on Linux 3.10.0:
math_utils.c: In function ‘i2st’: math_utils.c:10:5: warning: comparison is always false due to limited range of data type [-Wtype-limits] if (u_value > SIZE_MAX) { /* Line 10 */ ^
These warnings don't appear justified to me, at least not in the general case. (I don't deny that the comparison might be "always false" on some particular combination of hardware and compiler.)
The C Standard
The C 1999 standard does not appear to rule out an
int
being greater thanSIZE_MAX
.Section "6.5.3.4 The
sizeof
operator" doesn't addresssize_t
at all, except to describe it as "defined in<stddef.h>
(and other headers)".Section "7.17 Common definitions
<stddef.h>
" definessize_t
as "the unsigned integer type of the result of thesizeof
operator". (Thanks, guys!)Section "7.18.3 Limits of other integer types" is more helpful --- it defines "limit of
size_t
" as:SIZE_MAX
65535
...meaning
SIZE_MAX
could be as small as 65535. Anint
(signed or unsigned) could be much greater than that, depending on the hardware and compiler.Stack Overflow
The accepted answer to "
unsigned int
vs.size_t
" seems to support my interpretation (emphasis added):The
size_t
type may be bigger than, equal to, or smaller than anunsigned int
, and your compiler might make assumptions about it for optimization.This answer cites the same "Section 7.17" of the C standard that I've already quoted.
Other Documents
My searches turned up the Open Group's paper "Data Size Neutrality and 64-bit Support", which claims under "64-bit Data Models" (emphasis added):
ISO/IEC 9899:1990, Programming Languages - C (ISO C) left the definition of the
short int
, theint
, thelong int
, and thepointer
deliberately vague [...] The only constraints were thatint
s must be no smaller thanshort
s, andlong
s must be no smaller thanint
s, andsize_t
must represent the largest unsigned type supported by an implementation. [...] The relationship between the fundamental data types can be expressed as:sizeof(char)
<=sizeof(short)
<=sizeof(int)
<=sizeof(long)
=sizeof(size_t)
If this is true, then testing an
int
againstSIZE_MAX
is indeed futile... but this paper doesn't cite chapter-and-verse, so I can't tell how its authors reached their conclusion. Their own "Base Specification Version 7"sys/types.h
docs don't address this either way.My Question
I understand that
size_t
is unlikely to be narrower than anint
, but does the C standard guarantee that comparingsome_unsigned_int > SIZE_MAX
will always be false? If so, where?Not-Duplicates
There are two semi-duplicates of this question, but they are both asking more general questions about what
size_t
is supposed to represent and when it should / should-not be used."What is
size_t
in C?" does not address the relationship betweensize_t
and the other integer types. Its accepted answer is just a quote from Wikipedia, which doesn't provide any information beyond what I've already found."What is the correct definition of
size_t
?" starts off nearly a duplicate of my question, but then veers off course, asking whensize_t
should be used and why it was introduced. It was closed as a duplicate of the previous question.
-
Kevin J. Chase over 6 yearsI suppose my confusion is in part because that highly-upvoted answer seems to directly contradict the Open Group's paper that I quoted (and the apparent intent of the GCC warning). Am I mis-reading one of them, or is that paper outright wrong about this?
-
Kevin J. Chase over 6 yearsI was going to ask you to cite that claim about the GCC warning, but I think I found it. (It looks like I wasn't the only one frustrated by a warning that essentially tells you to make your code less portable.) According to that bug's resolution, some time in 2007 GCC 4.3 collected these warnings in the new
-Wtype-limits
option. So I suppose the solution is to use-Wno-type-limits
whenever I use-Wextra
. (And myMakefile
grows another paragraph-sized comment just to maintain my sanity...) -
rici over 6 years@kevin: if you look long enough on the internet, you will find something which confirms pretty well any error, regardless how egregious. The errors are not necessarily deliberate lies or sophomoric misconceptions. Sometimes they are just typos. Sometimes they are things which were once factual but 30 years later have little relationship to reality. Sometimes they are archives of innocent errors which were never intended to persist. It is confusing when an obvious error is signed by someone credible, but it happens.
-
Kevin J. Chase over 6 years@machine_1: My reading of it was "The current C standard does not prohibit
size_t
from being narrower than anint
, ...". But I agree, parsing it required some effort. -
rici over 6 years@machine_1: "wider than" and "no narrower than" mean different things. You can't change a
>=
to>
without changing a program's semantics. Anyway, I changed it to "at least as wide as" which seems prolix to me, but perhaps better fits the esthetics of the answer's readers. -
rici over 6 years@kevin: the C standard is a collection of requirements. It does not, in general, explicitly allow or prohibit anything, except in the sense that it (implicitly) allows anything compatible with the requirements and (implicitly) prohibits anything which would make it impossible to comply with the requirements. Stating that the standard does not have a requirement for
X
seems to me to be the most precise way to capture the intent, since it does not rely on deduction. I apologise if this habit complicated your parse. -
M.M over 6 yearsC89 did not require
size_t
be able to hold 65535. That was first specified in C99. -
machine_1 over 6 years@rici Yes, the phrase "at least as wide as" is the correct translation.
-
Antti Haapala -- Слава Україні over 6 years@rici no it doesn't.
size_t
can never be the same type asint
. -
rici over 6 yearsClang doesn't warn if you write
if (INT_MAX > SIZE_MAX && u_value > SIZE_MAX)
, but most gcc versions do. -
vpalmu over 5 yearsThere's a gcc port to the parallax processor. The maximum allocation size is 32kb so size_t really could be a short integer.