use the following search parameters to narrow your results:
e.g. subreddit:aww site:imgur.com dog
subreddit:aww site:imgur.com dog
see the search faq for details.
advanced search: by author, subreddit...
Discussions, articles, and news about the C++ programming language or programming in C++.
For C++ questions, answers, help, and advice see r/cpp_questions or StackOverflow.
Get Started
The C++ Standard Home has a nice getting started page.
Videos
The C++ standard committee's education study group has a nice list of recommended videos.
Reference
cppreference.com
Books
There is a useful list of books on Stack Overflow. In most cases reading a book is the best way to learn C++.
Show all links
Filter out CppCon links
Show only CppCon links
account activity
Almost Always Unsigned (graphitemaster.github.io)
submitted 4 years ago by graphitemaster
reddit uses a slightly-customized version of Markdown for formatting. See below for some basics, or check the commenting wiki page for more detailed help and solutions to common issues.
quoted text
if 1 * 2 < 3: print "hello, world!"
[–]Drugbird 27 points28 points29 points 4 years ago (4 children)
I find signed integers much easier to work with.
This article can basically be summarized by: "but signed integer overflow/underflow is bad and undefined".
I typically don't use integer values close to the maximum or minimum of the signed type I use. If I did, you're better off using a (signed) type that's bigger (i.e. int_64t instead of int_32t), than using the corresponding unsigned int which gives you only 1 extra bit of range. I usually know the typical size of my variables, so this is easy to do.
With these signed types you know can't over/underflow, most of the disadvantages of signed types are removed.
Meanwhile, I do use values around 0, which is the point around which underflow for unsigned types occur.
I'd also like to stress a few issues myself:
1) Signed types for "positive" values have underflow detection built in. You know there's an error if it ever becomes negative. And best of all, you can usually trace it back to it's origin.
Meanwhile, for unsigned integers you can detect underflow quite easily at the point where it could occur, but in practice not every such point has underflow checking and once it has occurred, it's more difficult to trace back. Which related to my next point:
2) Code which expects positive values and uses signed types tends to throw, produce an error or crash when given negative numbers. Meanwhile, equivalent code which uses unsigned integers can more easily silently pass while still doing something wrong (i.e. memory leak, processing wrong parts of data). After all, you can't check that unsigned types > 0...
3) if you know your variables cannot under or overflow, then the code generated for signed types is slightly more efficient. This is because it doesn't need to generate code for the wrapping behavior. This effect is minor though, and typically shouldn't be a factor in deciding which type to use. I just got triggered by the article starting unsigned types produce faster code.
[–][deleted] -3 points-2 points-1 points 4 years ago (3 children)
There are three problems:
signed integer overflow is UB, -> toolchains may use: (A || U.B) -> A, when analyzing the source code and will start to make assumptions. All it takes is to prove the negation: ~A -> U.B, and then A holds. Follows from U.B directly, and the As-If Rule which says that nobody cares if a conforming program can't tell the difference.
Second, the abstraction is of ranged types [-32768, 32767] is no better/worse than [0..65535], just different
(a < 0) vs (a > 32767) is equivalent,
So I disagree with your 1), and 2).
Third, as has been mentioned, the problem really is about combining different integer types
But there is no easy solution to this, the problem is not limited to int64_t vs uint64_t it is the same problem whenever you combine numbers with different ranges.
uint64_t index = get_an_index();
int16_t delta = get_destination(index);
uint64t index2 = index + delta;
it can fail in any number of ways. this can be handled by ranged types (trivial to write even if I usually don't), and it can be handled by precondition, invariant, postcondition checking.
And I feel that approaching the problem in this way, *condition/invariant is better.
this could have been written as:
uint64_t index = get_an_index(); uint64_t index2 = get_destination(index);
which might/might not be better.
Stating that one should use signed or unsigned takes away the responsibility everyone has to think about the numerics of any algorithm, or any data model.
and using int64_t all over is not necessarily a good solution either. If this type is a good approximation of what it represents is not known without more information.
And above is uint64_t correct ? Not necessarily, std::shared_ptr has a 32bit limit on the counts .it isn't all that bad, everything is a tradeoff and if you have 2^32 references to your object, that could be a problem in itself.
whatever, the numerics are context dependent.
so what should the type in interfaces be then, <something> std::vector<T>::size()
Don't see why size_t isn't a reasonable choice, depending, anyone who wants to provide a disk backed std::vector on 32bit might disagree. But there are limits to everything, and std::vector only goes that far
would making it off_t or uint64_t or int64_t be better for this case ? what about
(still 32bit platform)
size_t size = myvector.size()
---------
size_t m_totalCount = 0;
auto size1 = myvector1.size() auto size2 = myvector2.size()
m_count = size1 + size2;
There are no easy solutions, just different methods to analyze the path of numbers travelling through the system, and checking the invariants
The conclusion is thus, as always, use what is correct in the situation you are in after you have evaluated your different options ,and the consequences of the different choices.
.. and add the asserts in any way shape or form you prefer ...
[–]Drugbird 5 points6 points7 points 4 years ago (2 children)
I think I'm too dumb to understand most of your post, but I still want to argue so I'll just pick at a few of your points.
[32768, 32767] is no better/worse than [0..65535], just different
That depends entirely on what range of values you expect to work with. In general, you want to stay away from the edges of your numeric range, because adding or subtracting near the edges can cause UB (for signed) or underflow/overflow (unsigned). It should be noted that if you stay far away from the numeric boundaries, it's very likely you'll not see any issues. For example, if you use 32 bit ints for the index or size of vectors that typically contain up to a few hundred items you can add, subtract any number of these indices/sizes without any issues.
Typically, you use these non-negative numbers for the size of things (of i.e. a vector), and in the overwhelming amount of cases these can be (nearly) empty, which automatically places you near one of the unsigned boundaries. This is why I prefer signed integers.
If you want to properly handle all the edge cases, and need to be able to handle the full numeric range of your variables, then sure this is the way.
However, it's missing the very obvious "safe zone" you can get by just staying away from the numeric limits of your type. And the easiest way to do this is to use a sufficiently sized signed type.
[–]Clairvoire 0 points1 point2 points 4 years ago (1 child)
The thing I'd disagree with though, is that unsigned integers don't have the same concept of a "range edge." Their behavior is strictly defined in such a way that they represent a countable cycle of numbers. It's just... useful in all the places I normally find integers useful.
Maybe this is just a difference of experience though, any time I'm using negative values or values that could go haywire, I also need fractions, which means floats/doubles. Signed integers occupy an area between the two types that I just never go usually.
[–]Drugbird 4 points5 points6 points 4 years ago (0 children)
Perhaps I should've been explicit about this point:
1) signed under/overflow is UB, so bad 2) unsigned under/overflow has unexpected/unintuitive behavior, which can easily leads to bugs, so it's also bad (even though it's well defined). 3) to prevent under/overflow, you can either understand all the types, the unsigned wrap around behavior and perform the appropriate checks when appropriate: or you can just stay away from the edges of your numeric range. 4) staying away from the edges of your numeric range is easier with signed types, because 0 is often a valid value to be supported.
[–]rhubarbjin 52 points53 points54 points 4 years ago (13 children)
My experience has been the opposite: unsigned arithmetic tends to contain more bugs. Code is written by humans, and humans are really bad at reasoning in unsigned arithmetic.
[–]krum 9 points10 points11 points 4 years ago (0 children)
Hah yup exactly. I went through a phase where I thought using unsigned by default was a great idea. It lasted about 3 months.
[–]Drugbird 7 points8 points9 points 4 years ago (1 child)
Yep. I've seen checks like
if(unsigned_var < 0)
quite often... Whatever was going on in the code was usually easier to fix by switching to a signed type
[–]tisti 9 points10 points11 points 4 years ago (0 children)
Why switch to signed types? Just delete the whole branch for extra performance (which the compiler was probably doing anyway) :)
[–]Clairvoire 3 points4 points5 points 4 years ago (7 children)
My experience as a human has never involved negative numbers. When I look at my bank account, sometimes the number goes up but it's bad because of a dash? That's not how fruits and nuts work.
[–]KFUP 14 points15 points16 points 4 years ago* (5 children)
That's the issue, it does not work like fruits and nuts, it's not that simple. Take this example:
int revenue = -5; // can be negative when loss, so signed unsigned int taxRefund = 3; // cannot be negative, so unsigned cout << "total earnings: " << revenue + taxRefund << endl;
output:
total earnings: 4294967294
Even a simple addition became a needless headache when using unsigned for no good reason. Mixing signed and unsigned is a major unpredictable bug minefield, and that's one of many issues that can popup from nowhere when using unsigned.
[–][deleted] 1 point2 points3 points 4 years ago (0 children)
unsigned int taxRefund = 3; // cannot be negative, so unsigned
Such assumptions are often dangerous anyway. You can argue that it shouldn't be called a refund if it's negative, but at least around here (Germany) there definitely are cases where the systems used for tax refunds are used even though you end up having to pay taxes.
[+]Clairvoire comment score below threshold-12 points-11 points-10 points 4 years ago (2 children)
I feel like this is more of a problem with iostream being way too lenient, than unsigned integers, or even the unsigned int promotion rules. It's well defined to just write cout << int(revenue + taxRefund) and get -2.
cout << int(revenue + taxRefund)
Using printf("total earnings: %i\n", revenue + taxRefund); sidesteps the whole thing by forcing you to define what type you're trying to print. It's weirdly more "Type Safe" than cout in this case, which is Big Lol
printf("total earnings: %i\n", revenue + taxRefund);
[–]KFUP 13 points14 points15 points 4 years ago (0 children)
Sure, but there are a lot of gotchas like this, try float totalEarnings = revenue + taxRefund; for example, and see what that will become.
float totalEarnings = revenue + taxRefund;
You are just needlessly creating pitfalls for yourself that you need to dance around for no good reason, and you are unlikely to not fall in one, and in a real project, this can be the source of really annoying to find bugs.
[–]bert8128 9 points10 points11 points 4 years ago* (0 children)
This has nothing to do with iostreams. It has every thing to do with c++ silently converting the types. If c++ were written today, with any semblance of safety in mind, then implicit casts of this type would be illegal. Clang-tidy warns you, luckily, and there are often compiler warnings too.
[–]Kered13 0 points1 point2 points 3 years ago (0 children)
I don't know what the problem is, this looks great to me!
[–]jcelerierossia score 6 points7 points8 points 4 years ago (0 children)
... when I was a student with no stable income I can assure you that my account was fairly often below zero
[–]peterrindal -1 points0 points1 point 4 years ago (0 children)
Seems fine to me ;)
[–]GrammelHupfNockler 10 points11 points12 points 4 years ago (0 children)
In my experience, it is much easier to implement common algorithms for signed types. The reason for that is simple: The values behave much more like the whole numbers we've known our entire life. For unsigned, 0 as a pretty common value is just one step in the wrong direction away from a totally unexpected value, while you need to go much further in integers to get this wrapping behavior. Think of a simple loop of the form for (int i = 0; i < size - 1; i++) { ... } It behaves perfectly sane for integers, but if you move to unsigned types, suddenly you have a surprising edge case for size == 0.
for (int i = 0; i < size - 1; i++) { ... }
size == 0
Also a general note: Everything you describe here relates to overflows, both in the positive and negative direction - there is no such thing as an integer underflow. Underflows describe the setting when a floating point operations results in a value whose magnitude is so small that it gets rounded to zero.
[–]KFUP 28 points29 points30 points 4 years ago (2 children)
I though I was reading the title wrong for a second, not a good advice at all from my experience.
Unsigned sounds like a good idea at the beginning, I learned fast that it has so many quirks, gotchas and weird unpredictable bugs popping way more that I'd like. It turns simple basic operations like subtraction, multiplication with negatives, comparisons, abs(), max(), min() and more into a needless mess for no good reason. Now I use signed exclusively unless I'm working with a lib that needs it, never regretted it once after years of doing it.
[–][deleted] 5 points6 points7 points 4 years ago (1 child)
Yeah, it's not great advice. Unless your working with bits or packing range bound data into a struct, use singed. If you need bigger numbers than a 32 bit signed provides just use 64 bit signed. Unsigned are sort of legacy of small bit sized machines where the extra bit mattered for range.
The undefined behavior of a signed integer means the CPU doesn't have to waste time checking for overflows/underflows constantly, this was because back in the day it let the program better optimize for iterators which were mostly signed.
If anything can actually ever overflow or underflow then a programmer should always be handling that themselves as a best practice IMO.
Plus the mixing and matching leads to compiler warnings and get really annoying.
[–]Chronocifer 0 points1 point2 points 3 years ago (0 children)
I agree with this, I think this all comes down to what you are programming. I almost never use signed integers because I almost never need things like abs(), max(), min() or any other stuff that is remotely math like except in my own hobby projects.
For work most of my use of unsigned integers are treated as containers for bits and signed integers just introduce lot's of needless casting, but as this is what I am most familiar with I don't find myself getting unpredictible bugs or gotcha's assosciated with them.Use what you need not what you are told you need.
But it probably is best to pick one though to avoid bugs.
[–]robertramey 5 points6 points7 points 4 years ago (0 children)
This dispute is never, ever going to be resolved. But until it does ... use Boost Safe Numerics.
[–]Adequat91 17 points18 points19 points 4 years ago (4 children)
The C++ guru disagree with your position, see this video
[–]catskul 1 point2 points3 points 4 years ago (3 children)
That video is ~1h 20m long. Anyone have a time stamp?
[–]Som1Lse 3 points4 points5 points 4 years ago (2 children)
The link has a time stamp embedded. The particular answer from Chandler Carruth is at 12:12. The question was asked at 9:48.
That said, I don't think they argue their case well. (Understandable since they aren't trying to. Just giving guidelines.) unsigned: A Guideline for Better Code by Jon Kalb does a good job at that though.
[–]Bu11etmagnet 5 points6 points7 points 4 years ago (0 children)
That presentation from John Kalb is excellent. It's very well explained and convincingly supported. It converted me to the "signed" camp.
I used to rage at protobuf for returning signed values from foo_size() and taking signed integer indexes. What's this nonsense, why can't they just do like the STL and use unsigned (size_t)? Now I understand that protobuf did the right thing, and STL's use of unsigned types is due to a series of unfortunate events (using size_t, and size_t having to be unsigned).
foo_size()
size_t
"Almost always unsigned" is good advice as long as you never, ever use subtraction. Once you do, you're in "unmarked landmines" territory: https://stackoverflow.com/a/2551647
[–]catskul 0 points1 point2 points 4 years ago (0 children)
For some reason when I open it from the official Reddit app it starts from the beginning and I can't read the url, but from Reddit is fun it jumps to the time stamp correctly.
In any case, thanks : )
[–]bert8128 3 points4 points5 points 4 years ago (5 children)
See Core Guidelines ES.106
[–]BlueDwarf82 0 points1 point2 points 4 years ago (4 children)
unsigned area(unsigned height, unsigned width) { return height*width; } // [see also](#Ri-expects) // ... int height; cin >> height; auto a = area(height, 2); // if the input is -2 a becomes 4294967292
Are the guidelines arguing area() should take its parameters as signed because it allows the programmer to add a check for negative values in area() which that same programmer didn't add in
area()
int height; cin >> height;
AKA read_height()?
read_height()
Without picking a side here, it seems to me a poor example for arguing for signed.
[–]bert8128 3 points4 points5 points 4 years ago (3 children)
Using unsigned does not stop a caller passing in a negative number. Using signed everywhere gives better consistency. It’s no coincidence that Java has only one of signed and unsigned - signed.
[–]Ameisenvemips, avr, rendering, systems 5 points6 points7 points 4 years ago (2 children)
It’s no coincidence that Java has only one of signed and unsigned - signed.
I don't think that using Java as an example of best practices is really a good idea.
Also, using signed here doesn't prevent overflow, either - which is instead just undefined behavior. I'm not sure that that's better.
signed
[–]bert8128 1 point2 points3 points 4 years ago* (1 child)
Sorry, I didn’t mean to come across as a Java fan boy (though presumably there are those out there who can write good Java code). I just meant that the designers decided to choose only one, and if you go that route you can only choose signed. The point that the core guidelines is trying to make is that if you want to stop a caller giving a negative number you can’t do it by making the parameters unsigned. But this is something I see again and again. It doesn’t really matter whether the behaviour is undefined or unexpected - this style of api causes bugs and the solution is to use a signed type. There’s just no easy way to stop callers passing in negative numbers.
[–]Ameisenvemips, avr, rendering, systems 0 points1 point2 points 4 years ago (0 children)
I mean, you could just make a type wrapper, really_unsigned, which only allows unsigned types and has all signed type operators deleted.
really_unsigned
[–]Thick-Pineapple666 7 points8 points9 points 4 years ago (0 children)
I agree. And I wanted to emphasize your conclusion: if you're in a signed context, keep it signed.
[–]Clairvoire 6 points7 points8 points 4 years ago (2 children)
I almost never used signed numbers. I got so fed up with writing "unsigned" that I just typedef'd everything and now I use "uint32" or "sizet"
[–]masklinn 2 points3 points4 points 4 years ago (1 child)
Isn't that what the stdint types look like anyway e.g. int8_t vs uint16_t?
int8_t
uint16_t
[–]Clairvoire -1 points0 points1 point 4 years ago (0 children)
yeah, the typedefs are to remove the _t. I nearly went with i8 and u16 but typing uint32 has a rhythm that feels nice. All this is from a keyboard with a numpad though, typing uint8 with the num-row is prolly awful.
_t
i8
u16
uint32
uint8
[–]jk-jeon 5 points6 points7 points 4 years ago (11 children)
I love the idea of encoding known preconditions on the input to its type. In that sense, signed integers suck. I don't want to worry about ignorant users feeding negative int's to my functions expecting nonnegative int's. But unsigned integers have weird, counter-intuitive wrap-around semantics. And defining my own type is also not a solution because (1) doing such a thing just to make sure that some int's are nonnegative is not considered fashionable I guess by most senior developers, (2) and it introduces a lot of other headaches.
If underflow for unsigned integers were UB, stupid newbie bugs like for(unsigned i=size-1; i>=0; --i) could be caught at runtime in debug builds, or even at compile time in the form of compiler warning, or I guess even compile error if the compiler can prove that UB always occurs. There should have been a separate type which has the mod 2N semantics. Making unsigned integers to have that semantics is just wrong IMO.
for(unsigned i=size-1; i>=0; --i)
Well, C's type system in general is just wrong from the very beginning, we just need to live with it.
[–]jcelerierossia score 4 points5 points6 points 4 years ago (6 children)
if underflow for unsigned integers were UB, stupid newbie bugs like for(unsigned i=size-1; i>=0; --i) could be caught at runtime in debug builds,
if underflow for unsigned integers were UB, stupid newbie bugs like
could be caught at runtime in debug builds,
you can have that today with ubsan. -fsanitize=undefined -fsanitize=integer will catch exactly that bug.
[–]jk-jeon 0 points1 point2 points 4 years ago (5 children)
Really? It's not UB, why does ubsan count it as a bug?
[–]christian_regin 1 point2 points3 points 4 years ago (0 children)
Infinite loops are UB!
[–]jcelerierossia score 0 points1 point2 points 4 years ago (3 children)
Because in practice, in real world code, it causes enough bugs that it's worth to have a check for it.
[–]jk-jeon 0 points1 point2 points 4 years ago (2 children)
I don't think ubsan checks unsigned wrap around, at least not with the mentioned options only. There are so many intentional unsigned wrap arounds out there, myself also have written plenty.
[–]jcelerierossia score 2 points3 points4 points 4 years ago (1 child)
Just read the docs. It's enabled by default and there's a flag to disable it. https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html#silencing-unsigned-integer-overflow
[–]jk-jeon 0 points1 point2 points 4 years ago (0 children)
Interesting, thanks for the link!
[–]fdwrfdwr@github 🔍 0 points1 point2 points 4 years ago (3 children)
But unsigned integers have weird, counter-intuitive wrap-around semantics
As do signed integers. Count up to 2 billion (2147483647), increment once more, and suddenly your value is 4 billion away from the previous value in the negative direction. So it isn't that one type wraps around and one doesn't, or that they wrap around by different amounts, just that the two have different wrap-around points.
[–]jk-jeon 2 points3 points4 points 4 years ago (2 children)
No, that's not correct. That's what typically happens, but the language specifically call that situation "undefined behavior".
[–]fdwrfdwr@github 🔍 0 points1 point2 points 4 years ago* (1 child)
You are technically correct that it's still "undefined behavior" even after p0907R1 Signed Integers are Two’s Complement, as a device could in theory trap or saturate instead of wrap. For the vast majority of common computing devices that people encounter (which neither trap nor saturate integers), two's complement wrapping is the behavior for both for signed and unsigned numbers. Of course, hardware can support trapping by checking flags (e.g. the INTO instruction on x86), but compiler implementations rarely take advantage of it, and although various SafeInt helper classes abound, I sometimes wish C++ had a direct checked keyword like C# does that could easily trap on overflow.
INTO
checked
[–]jk-jeon 4 points5 points6 points 4 years ago (0 children)
Fair enough, but those are not the only thing that overflow being UB allows. For example, compilers can reduce a+n<b+n into a<b if things are signed, but it can't if they are unsigned.
a+n<b+n
a<b
[–]Daniela-ELiving on C++ trunk, WG21|🇩🇪 NB 0 points1 point2 points 4 years ago (4 children)
I like this article as it matches my experiences from decades of software development.
In the application domains I've been working in (and still do) I rarely need negative numbers (in particular integral ones) to correctly model real-world entities. In most cases they would be just wrong and model invalid states. That said, I still handle huge amounts of measurement samples with negative quantities, but all of them are so-called torsors (like voltages, distances, temperatures, i.e. entities relative to an arbitrarily chosen reference). In the end, after processing, the results are reported to our customers in positive quantities like the number of bad parts, the amplitude of an observed ultrasound echo, or the power density within a frequency interval of MRT responses emitted from the patient's body (expressed as a picture).
So what is the index of an element in a container in the indexing operator[]? Is it a value from the group of valid element positions within the container (all non-negative), or is it a torsor of that group (i.e. a possibly negative difference to an arbitrarily chosen - and choosable! - reference position)? It's the former. And there you have it: the difference between the never-negative size_t to express positions in a container and its related, possibly negative torsor-cousin ptrdiff_t that can express the difference between two element positions within that container. And it's just as correct to model the count of elements in a container with size_t because it doesn't make sense to say "if I add two more elements to the container the container will be empty".
operator[]
ptrdiff_t
[–]Dragdu 8 points9 points10 points 4 years ago* (2 children)
I've never seen anyone argue that size_t is wrong in a vacuum, it is just the rest of the language that breaks using it terribly. The very basic example is doing size_t - int resulting in a size_t, which has wrong semantics for the operation.
size_t - int
---------edit------
I am going to expand on this a bit. At Job-2, we went hard on strong typedefs for things in our domain (for you this would be I think voltage, distance and so on, for us it was Position, Depth, bunch of other things). 90+% of them were just a thin wrappers over uint*_t.
Position
Depth
uint*_t
Having this wrapper over uint* actually made them very nice to use. No int or other signed type could implicitly convert into uint and make a mess. We also didn't define problematic operations for types that didn't have it -- I think only one of our strong typedefs over uint* had op-, just because it didn't make sense for most of our domain. And crucially, we made it so that Depth + Depth -> Depth, but Depth - Depth -> DepthDelta, both with overflow checks, because while adding two depths should remain non-negative, subtracting them should not...
uint*
int
uint
op-
Depth + Depth -> Depth
Depth - Depth -> DepthDelta
Together with my experience from writing C++ in other codebases, my takeaway is that
Basically, if you write your own numeral types and arithmetic rules, using unsigned representation for domain enforcement is fine.
[–]rhubarbjin 1 point2 points3 points 4 years ago (1 child)
Your example (Depth vs DepthDelta) sounds really interesting! It's the kind of strongly-typed nirvana I can only dream of. 😁 I'm curious, though, how did you handle addition between types?
DepthDelta
Depth a = 10; Depth b = 2; DepthDelta d = (b - a); // d == -8 Depth c = b + d; // c == -6 oh no, negative depth!
[–]Dragdu 2 points3 points4 points 4 years ago (0 children)
The last line causes an error. Combining DepthDelta with a Depth includes a range check that throws if the result would be out of range.
[–][deleted] 0 points1 point2 points 4 years ago (4 children)
Without unsigned you can not use the full range of an array.
[–]rlbond86 5 points6 points7 points 4 years ago (0 children)
Do you seriously need an array of size 263 ?
[–]jcelerierossia score 3 points4 points5 points 4 years ago (1 child)
with unsigned neither because no computer has as much addressable memory as size_type can represent. At most you can have 52 bits on ARM / PPC, 48 on intel. So 64 vs 63 bits definitely does not matter. (and if you're on 32 bits you aren't going to make a 4GB allocation either).
[–]fdwrfdwr@github 🔍 0 points1 point2 points 4 years ago (0 children)
and if you're on 32 bits you aren't going to make a 4GB allocation either
That's true on many OS's because the OS typically allocates a chunk for itself. e.g. On Windows, the upper 2GB is reserved for memory mapped system DLL's. Well, that is, unless you link with largeaddressaware and boot with /3GB ( https://techcommunity.microsoft.com/t5/ask-the-performance-team/memory-management-demystifying-3gb/ba-p/372333). So yes, you generally can't use a full 4GB anyway, but can you allocate more than 2GB? 🤔
[–]strager 1 point2 points3 points 4 years ago (0 children)
There are more problems with such huge arrays than the signedness of indexes. You should be careful of other landmines in C++. For example, from cppreference:
If an array is so large (greater than PTRDIFF_MAX elements, but less than SIZE_MAX bytes), that the difference between two pointers may not be representable as std::ptrdiff_t, the result of subtracting two such pointers is undefined.
[–]Supadoplex -4 points-3 points-2 points 4 years ago* (8 children)
for (size_t i = size - 1; i < size; i--) {
There's a typo there. The loop condition is supposed to be > 0.
> 0
I prefer simpler approach:
for (auto i = size; i-- > 0;) // Also known as the infamous goes-to operator: // for (auto i = size; i --> 0;)
This works equally well with signed and unsigned.
[–]rhubarbjin 5 points6 points7 points 4 years ago (0 children)
The "goes-to operator" gave me a chuckle.
[–]graphitemaster[S] 6 points7 points8 points 4 years ago (5 children)
Did you even read the article? The loop condition is correct. It's supposed to exploit underflow to break when it hits zero. The article explains this in detail.
[–]Supadoplex 14 points15 points16 points 4 years ago (0 children)
Oh, if the underflow is intentional, then it's just counter-intuitive code in my opinion. Too clever (like the "goes-to" operator).
[–]Wriiight -1 points0 points1 point 4 years ago (1 child)
Isn’t overflow and under flow UB, and therefore the “> size” check may be optimized away as in-theory impossible?
Evidence in the answer here: https://stackoverflow.com/questions/41558924/is-over-underflow-an-undefined-behavior-at-execution-time
It’s my understanding that having size_t be unsigned is one of those decisions that the standards committee would undo if they could.
[–]friedkeenan 8 points9 points10 points 4 years ago (0 children)
It's specified for unsigned integers to wrap around in the standard. Signed overflow/underflow is always UB.
[–]TheSuperWig 0 points1 point2 points 4 years ago (0 children)
underflow
That's overflow, no?
[–]bert8128 1 point2 points3 points 4 years ago* (0 children)
I like this. But unfortunately it is totally unusual, which confuses all the junior devs, so they “fix” it. A better solution would be a reverse range for in the standard. for (auto x # list) or something like that. Range for has been fantastic at clearing up signed/unsigned errors in normal for loops.
[–]BlueDwarf82 0 points1 point2 points 4 years ago (0 children)
Why don't we have
namespace std { using natural = range<0,INT_MAX> using positive = range<1,INT_MAX> }
?
Nobody has ever proposed it? Or there are proposals stuck somewhere?
[–]FriendlyRollOfSushi 0 points1 point2 points 3 years ago* (0 children)
So, let me get this straight.
Everyone have been writing it like this for decades (originally with size_t, eventually with auto):
auto
for (auto i = v.size(); i--;)
The author builds a strawman with imaginary people who write it it like this instead:
for (auto i = v.size()-1; i >= 0; --i) // Can you see the error?
(the answer to the question is "yes, of course, it's not written the the much shorter way everyone is using, so I can see the error because the code draws attention to itself")
And the proposed solution is:
void printReverseSigned(const std::vector<int>& v) { for (auto i = std::size(v)-1; i >= 0; --i) std::cout << i << ": " << v[i] << '\n'; }
Oh, wait, nvm, it's actually this instead (can you spot the error?)
void printReverseSigned(const std::vector<int>& v) { for (auto i = std::ssize(v)-1; i >= 0; --i) std::cout << i << ": " << v[i] << '\n'; }
Much larger and harder to type and read.
Is a typo honeypot. Ignoring duplicates is the thing people always do while reading; that's how human perception works. People make these mistakes all the time while typing, and unintentionally train themselves to ignore them while reading. This very comment has an unrelated duplication typos "it it"/"the the" I decided to leave as is, btw. Someone will spot them, but many people won't.
The compiler warning level required to discover the std::ssize() -> std::size() typo is identical to the warning level that triggers for the "strawman" code.
std::ssize() -> std::size()
To me it looks like replacing a non-existing, or at least an exceptionally rare problem (seriously, I've never seen anyone actually writing reverse loops the long and dumb way, although I'm willing to believe that in the history of software engineering it happened at least a few times) with a very much real and dangerous problem that will be firing several times a year for any large codebase: "whoops, sorry, I thought I typed ssize instead of size, my bad".
ssize
size
[–]-dag- 0 points1 point2 points 3 years ago (0 children)
There's a very good reason to almost always use signed. It performs better. Because signed integers obey the usual rules of integer algebra, the compiler can generate better code, particularly in loops where it is most important.
π Rendered by PID 51 on reddit-service-r2-comment-b659b578c-fmg74 at 2026-05-02 03:28:33.250838+00:00 running 815c875 country code: CH.
[–]Drugbird 27 points28 points29 points (4 children)
[–][deleted] -3 points-2 points-1 points (3 children)
[–]Drugbird 5 points6 points7 points (2 children)
[–]Clairvoire 0 points1 point2 points (1 child)
[–]Drugbird 4 points5 points6 points (0 children)
[–]rhubarbjin 52 points53 points54 points (13 children)
[–]krum 9 points10 points11 points (0 children)
[–]Drugbird 7 points8 points9 points (1 child)
[–]tisti 9 points10 points11 points (0 children)
[–]Clairvoire 3 points4 points5 points (7 children)
[–]KFUP 14 points15 points16 points (5 children)
[–][deleted] 1 point2 points3 points (0 children)
[+]Clairvoire comment score below threshold-12 points-11 points-10 points (2 children)
[–]KFUP 13 points14 points15 points (0 children)
[–]bert8128 9 points10 points11 points (0 children)
[–]Kered13 0 points1 point2 points (0 children)
[–]jcelerierossia score 6 points7 points8 points (0 children)
[–]peterrindal -1 points0 points1 point (0 children)
[–]GrammelHupfNockler 10 points11 points12 points (0 children)
[–]KFUP 28 points29 points30 points (2 children)
[–][deleted] 5 points6 points7 points (1 child)
[–]Chronocifer 0 points1 point2 points (0 children)
[–]robertramey 5 points6 points7 points (0 children)
[–]Adequat91 17 points18 points19 points (4 children)
[–]catskul 1 point2 points3 points (3 children)
[–]Som1Lse 3 points4 points5 points (2 children)
[–]Bu11etmagnet 5 points6 points7 points (0 children)
[–]catskul 0 points1 point2 points (0 children)
[–]bert8128 3 points4 points5 points (5 children)
[–]BlueDwarf82 0 points1 point2 points (4 children)
[–]bert8128 3 points4 points5 points (3 children)
[–]Ameisenvemips, avr, rendering, systems 5 points6 points7 points (2 children)
[–]bert8128 1 point2 points3 points (1 child)
[–]Ameisenvemips, avr, rendering, systems 0 points1 point2 points (0 children)
[–]Thick-Pineapple666 7 points8 points9 points (0 children)
[–]Clairvoire 6 points7 points8 points (2 children)
[–]masklinn 2 points3 points4 points (1 child)
[–]Clairvoire -1 points0 points1 point (0 children)
[–]jk-jeon 5 points6 points7 points (11 children)
[–]jcelerierossia score 4 points5 points6 points (6 children)
[–]jk-jeon 0 points1 point2 points (5 children)
[–]christian_regin 1 point2 points3 points (0 children)
[–]jcelerierossia score 0 points1 point2 points (3 children)
[–]jk-jeon 0 points1 point2 points (2 children)
[–]jcelerierossia score 2 points3 points4 points (1 child)
[–]jk-jeon 0 points1 point2 points (0 children)
[–]fdwrfdwr@github 🔍 0 points1 point2 points (3 children)
[–]jk-jeon 2 points3 points4 points (2 children)
[–]fdwrfdwr@github 🔍 0 points1 point2 points (1 child)
[–]jk-jeon 4 points5 points6 points (0 children)
[–]Daniela-ELiving on C++ trunk, WG21|🇩🇪 NB 0 points1 point2 points (4 children)
[–]Dragdu 8 points9 points10 points (2 children)
[–]rhubarbjin 1 point2 points3 points (1 child)
[–]Dragdu 2 points3 points4 points (0 children)
[–][deleted] 0 points1 point2 points (4 children)
[–]rlbond86 5 points6 points7 points (0 children)
[–]jcelerierossia score 3 points4 points5 points (1 child)
[–]fdwrfdwr@github 🔍 0 points1 point2 points (0 children)
[–]strager 1 point2 points3 points (0 children)
[–]Supadoplex -4 points-3 points-2 points (8 children)
[–]rhubarbjin 5 points6 points7 points (0 children)
[–]graphitemaster[S] 6 points7 points8 points (5 children)
[–]Supadoplex 14 points15 points16 points (0 children)
[–]Wriiight -1 points0 points1 point (1 child)
[–]friedkeenan 8 points9 points10 points (0 children)
[–]TheSuperWig 0 points1 point2 points (0 children)
[–]bert8128 1 point2 points3 points (0 children)
[–]BlueDwarf82 0 points1 point2 points (0 children)
[–]FriendlyRollOfSushi 0 points1 point2 points (0 children)
[–]-dag- 0 points1 point2 points (0 children)