11 ms·
When ‘int’ is the new ‘short’
- albinofrenchy 11y agoI don't entirely understand why 'int' is the new 'short' here; int hasn't been a particularly good way to store sizes since C99. Good spot though; I kind of doubt that this was a conscience design decision and probably just a slip up.
- dottrap 11y agoAgreed on both points. Plain int hasn't been very good for a long time. C99 <stdint.h> is really the way to go, but since this is C++ we're talking about, both the C++ committee and Microsoft Visual Studio deserve most of the blame for why people weren't using them since neither recognized/supported it for the longest time. (Visual Studio just finally got stdint and stdbool, about 15 years late.) And agreed, good catch.
- chetanahuja 11y agoIt's not been a problem (for a long time now) to find appropriate headers that typedef'd fixed size types (int32_t etc.) for almost any platform you would care about. For any reasonably complicated cross-platform codebase, it's not so much effort to include such a header in your code for the sake of improving the readability and debuggability of your code by a lot.
- dottrap 11y agoA lot of people know to do this, but I have met a shocking amount of C++ developers that know nothing of <stdint.h>. And here we have a prime example of Chrome, a major cross-platform project with high visibility that is not using these types nor didn't define their own in this case. These types were intended to help reduce mistakes. But compiler fragmentation basically resulted in organizations avoiding it, leading to these types of mistakes that could have been avoided in the first place.
- cpeterso 11y agoGoogle's C++ style guide recommends using int unless a fixed size is needed, such as binary compatibility in network code or file formats.
- albinofrenchy 11y agohttps://google-styleguide.googlecode.com/svn/trunk/cppguide.html#Integer_Types https://google-styleguide.googlecode.com/svn/trunk/cppguide.... Maybe I'm reading this wrong, but to me it seems like this is saying go ahead and use the fixed size variants whenever, but it is still OK to use int when you need <=32 bits.
- chetanahuja 11y agoYeah it's al lukewarm endorsement of int, quite possibly there only to accommodate legacy code. "<stdint.h> defines types like int16_t, uint32_t, int64_t, etc. You should always use those in preference to short, unsigned long long and the like, when you need a guarantee on the size of an integer. Of the C integer types, only int should be used."
- albinofrenchy 11y agoI work with a MSVC guy who not only didn't know about stdint, but also thought/thinks that 'word' means 16 bits, and insists that the WORD typedef is more portable than uint16_t. Still, at least nowadays there is no excuse -- everything from TI to VC++ supports stdint variations.
- pjmlp 11y agoMicrosoft only cares about C++, and C++11 was the revision that updated the C headers to C99. In any case, it isn't as if the language doesn't allow for type alias.
- pbsd 11y agoVisual Studio has had stdint.h since its 2010 edition. Before that there were readily-available substitutes (like https://code.google.com/p/msinttypes/ https://code.google.com/p/msinttypes/), or you could do it yourself by typedef'ing [unsigned] __intNN as [u]intNN_t.
- KayEss 11y agoSomebody elsewhere pointed out to me that these will give types that are not aliases of the common ones. I.e. __int8 isn't the same as either unsigned char or char. Probably won't make a difference most places, but what does?
- pbsd 11y agoThat is also the case in usual implementations of stdint.h, where int8_t is defined to be `signed char`. In C and C++, `char`, `signed char`, and `unsigned char` are different types, and `char` is not guaranteed to be signed or unsigned---that's up to the implementation. EDIT: looking at the documentation, it appears that __int8 is supposed to always be an alias for `char`, even as far back as 2003: https://msdn.microsoft.com/en-us/library/29dh1w7z(v=vs.71).aspx https://msdn.microsoft.com/en-us/library/29dh1w7z(v=vs.71).a.... However, the workaround found in msinttypes suggests that Visual Studio 6 does have this problem. I weep for those still using it.
- dottrap 11y agoBut not stdbool, nor a lot of other C99-ish features until Visual Studio 2013 Update 4 which shipped this past November. 10 or 15 years too long makes little difference. Way too long over due.
- mohawk 11y agosize_t has been the right type for buffer/array sizes since ANSI C.
- dottrap 11y agoNow; on x86_64, with gcc and clang an int is still a 32-bit integer type; Minor nit. The size of int is typically defined by the platform ; the compiler follows along. All the major/popular ones happen to define int as 32-bit, so that's what you are seeing with gcc/clang. Maybe on Solaris you might see it as 64-bit.
- chetanahuja 11y agoMany many C/C++ programmers, even many of those employed by google, have a natural tendency to use a old C types like short/int/long etc. for integral types without thinking through X-platform issues or api interactions with other code. Also, size_t is a frustrating beast. It's meaning is dependent on the platform. The Single Unix spec only calls for size_t to be an unsigned integer type. Now imagine you're writing code to compile over multiple mobile platforms as well as on x86_64 on the server side. Can you tell me what is the largest number you can address with that type -- without getting into a long google/stackoverflow session or hitting the compiler manuals for each of those platforms? If you absolutely want to make sure that your type can handle the values you expect it to handle, better give it well defined types provided by stdint.h (uint32_t is sooo much better than just int or unsigned int or even size_t for this purpose). Now granted, you'd need to interact with external libraries (including libc/libc++) that'll want to use size_t etc. Not much you can do here but be very careful when passing data back and forth between your code and the library code. But that's been the lot of C coders since time began.
- unwind 11y agoI disagree. All you need to care about for cases like these, when you're talking about the size of something, is that both malloc() and new[] handle allocation size using size_t. That, to me, says pretty clearly that "the proper type to express the size, in bytes, of something you're going to store in memory is size_t". It can't be too small, since that would break the core allocation interfaces which really doesn't seem likely. You don't need to know how many bits are in size_t all that often, and certainly not for the quoted code.
- ww520 11y agoFor cross platform interoperability, API with the exact size type helps remove any ambiguity. Using size_t might be fine for intra-process usage, but as soon as we are dealing with data across platforms, exact size type definition is a must.
- raverbashing 11y ago
- deleted 11y ago[deleted]
- stinos 11y agoCan anyone shed some light on the design rationale for using integer types (most commonly int it seems, as here) followed by a check if the number is not negative, whereas one could just use an unsigned type right away?
- hthh 11y agoI disagree with it, but the Google C++ Style Guide offers the rationale you're looking for (under "On Unsigned Integers"): https://google-styleguide.googlecode.com/svn/trunk/cppguide.html#Integer_Types https://google-styleguide.googlecode.com/svn/trunk/cppguide....
- raverbashing 11y agoYeah this doesn't convince me The bug example there, sure, needs a signed type, so you can't blame the type for its wrong usage I've had more bugs coming from using signed types that I'll not be bothered by writing 'unsigned' ever again
- detrino 11y agoI disagree that a signed type is the correct solution to that, rather you need a while loop: std::size_t i = foo.size(); while (i != 0) { --i; ... }
- gohrt 11y agoThe point is that C int types are an unsafe mess, so it's better to have one simple rule than memorize all the corner cases and address them all the time.
- deleted 11y ago[deleted]
- detrino 11y agoI am addressing the specific example given by the Google style guide. The code for counting down using a signed integer as they do has more corner cases than the while loop I have shown. Using their way, you have to remember to subtract 1 from the size at the start and then use >= in the loop conditional. My way is just the inverse of what you do while counting up. It's also worth pointing out the style of loop they give can't be used at all if you are counting down iterators or pointers instead of numbers.
- yason 11y agoI've never really liked the short/int/long definitions. They're just out there to confuse you, especially if you've written your share of assembly. For as long as I can remember I've just used size_t, uintptr_t, uint32_t/int32_t (or any of the 16/32/64 variants), exactly because I want to be explicit about the machine word sizes I'll be dealing with. Before that, I always used similar (u32/i32, LONG/ULONG, ...) platform-specific typedefs on proprietary systems too. For all practical purposes, int/unsigned int has been at least 32 bits since the early 90's (well, on modern platforms) but why use those if you can explicitly declare how many bits you actually need. (I've bumped into a few archaic platforms where stdint headers weren't present but it's easy to just add a few build-specific typedefs somewhere in that case.)
- antimagic 11y agoWell I know historically it was because there were potentially performance issues with using explicit sizes - int was basically the guarantee that you would be working in the CPU's native size, and hence at it's most efficient, no need to shift and mask bits to get the value that you wanted. Obviously this leads straight to a bunch of relatively subtle bugs, but I guess for some applications the tradeoff of speed vs safety was worth it.
- ordinary 11y agoC++11 has improved the situation somewhat by making it possible to explicit state what you want from your numeric types: http://en.cppreference.com/w/cpp/types/integer http://en.cppreference.com/w/cpp/types/integer
- mbel 11y ago<stdint.h> was actually introduced by C99 and included in C++11 with other C99 changes.
- Gibbon1 11y agoOn 8 bit machines it's the reverse, an int being 16 bits minimum requires more operations to handle than an 8 bit number. Pass an 'int' push two things on the stack. Etc. Causes your code size to balloon noticeably.
- alex95 11y agoIs this really a blog post about someone complaining that another used an int rather than a short? Is this genuine content that you guys care about?
- mohawk 11y agoA signed type for a buffer size in Chrome. Sigh, C/C++ just wasn't made for humans.
- zvrba 11y agoSigned types are the sane ones and give you error checking possibilities (negative size doesn't make sense; huge positive size may be right or an error). Unsigned types break trivial math like x < y => x-1 < y-1.
- mohawk 11y agoOn the contrary! Unsigned types are the only ones in C were everything is sanely defined, they are the integers mod 2^n. Signed types have undefined behaviour on overflow in C. In-band signalling of error states is error-prone. It is unfortunately commonly done in C because you can't return multiple values and passing in pointers is uglier.
- TillE 11y ago> and passing in pointers is uglier It's not so bad. It means you can be really consistent about returning error codes from every function in the exact same way. This is one of the very few things I actually like about the Win32 API. If only they used the same type of error codes in every section of the API.
- mattdw 11y agoDo signed types not also "break trivial math" like that, just at a different boundary? Genuine question. (The 0 boundary is obviously going to be more commonly hit than the 2^32 boundary, but nonetheless.)
- mohawk 11y agoSigned types have the same "wraparound" problem, i think the OP meant that they don't have this problem at the zero boundary.
- ctz 11y agoIt's kind of bizarre that chrome goes to exceptional lengths to sandbox things into lots of mutually untrusting processes, then parses network input outside of that protection.
- nikic 11y agoI think the most interesting bit here is this: > Now, the more astute reader will point out that I just sent over 4 gigabytes of data over the internet; and that this can’t really be all that interesting - but that argument is readily countered with gzip encoding, reducing the required data to a 4 megabyte payload. This was pretty much my first thought on seeing the IOBuffer signature - "That exploit payload is going to be huge". But things are not always as they seem and using gzip to generate a large string on the client is something I had not previously considered.
- ceequof 11y agoYou can blow up all sorts of things with gzip: https://en.wikipedia.org/wiki/Zip_bomb https://en.wikipedia.org/wiki/Zip_bomb
- pixelbeat 11y agoIn my experience we can't rely on manual handling of these integer overflow issues, especially with changing compiler behavior over time. I've noted some compile time and run time checking options at: http://www.pixelbeat.org/programming/gcc/integer_overflow.html http://www.pixelbeat.org/programming/gcc/integer_overflow.ht...
- imaginenore 11y agoHow exactly are you going to serve a 4GB certificate? Who in the world is going to wait for that to load?
- umanwizard 11y agoThe article mentions that it can be compressed to 4 MB...
- deleted 11y ago[deleted]
- pornel 11y agoConversion to and from C's `int` is one place where I think Rust is failing too :( Rust allows silent truncation of values in numeric conversions and considers it "safe", because other features for memory safety will catch buffer overflows — but it doesn't care about cases where the program will do a memory-safe logically-invalid thing (e.g. write to a wrong location within a buffer). That's because Rust has no integer size promotion at all, which means `len as usize` and `len as c_int` are required all over the place when interfacing with C (and the `as` operator has no overflow checking by design).
- aidenn0 11y agoI think integer size promotion would only make it worse. I do agree that "as" should have range checking. Some of it can be done statically (e.g. converting to a strictly larger type, and when the range of possible values are known) and the rest can be done with minimal overhead if they emit the code right.
- pcwalton 11y agoIt's not minimal overhead in many cases. Think about vectorization, for example.
- exDM69 11y agoRather than "type casts", implicit or explicit, I would like to see something with unambiguous, explicit semantics. Something like LLVM IR's operations, e.g. zero_extend, sign_extend, bitcast, floattoint, pointer_cast, etc. This may lead to some unnecessarily verbose code in some places but it leaves no surprises. It also gets rid of the need to do dirty tricks like union casts or pointer tricks when you want a bitcast from float to int or vice versa which is common when you write SIMD code, for example, using bitwise ops to extract the sign bit of a float or something like that.
- pcwalton 11y agoHow does the lack of integer size promotion make it easier to overflow? I don't follow.
- jhallenworld 11y agoI've recently tried to fix JOE (a UNIX portable program) so that it will compile without warnings and with minimum casting with -Wconversion. This is what I've found: Chars: I hate it that they are signed because I like the convention of promoting them to int, and then using -1 as an error. It's easy to forget to convert to unsigned first, and the compiler will not complain. In the past I've used 'unsigned char' everywhere, but it's a mess because strings are chars and all library functions expect chars. My new strategy is to use 256 as the error code instead of -1. The only problem is that getchar() uses -1, so it's weird. IMHO, it's a C-standard mistake that char is signed. I used to use int for indexes and long for file offsets. But these days, int is too short on 64-bit systems and long is not large enough on 32-bit systems. ptrdiff_t is the new int. I've switched to ptrdiff_t in place of int and off_t in place of long. Ptrdiff_t is correct on every system except maybe 16-bit MS-DOS (where it's 32-bits, but I think it should be 16-bits). Off_t is a long long if you have '#define _FILE_OFFSET_BITS 64'. Ptrdiff_t is ugly and is defined in an odd include file: stddef.h. It's not used much by the C library. The C library likes to use size_t and ssize_t. The definition of ssize_t is just crazy (it should just be the signed version of size_t, but it isn't). I understand why size_t is unsigned, but I kind of wish it was signed. It's rare that you have items larger than 2^(word size - 1), so signed is OK. You are guaranteed to have -Wconversion warnings if you use size_t, because ptrdiff_t is signed (even if you don't use ptrdiff_t, you still get a signed result to pointer differences so you will have warnings). Anyway, to limit the damage I make versions of malloc, strlen and sizeof which return or take ptrdiff_t. They complain if the result is ever negative. Yes this is weird, but I think it's better than having many explicit casts to fix warnings. Casts are always dangerous.
- david-given 11y agochars are only signed on some platforms (x86, for one). On others they're unsigned (ARM, for one). One knockon effect of this is that strcmp() will return different values on the two different platforms for UTF-8 strings (because 0xff > 32, but -1 < 32)... Incidentally, I don't know if you know about intptr_t; it's an int large enough to put a pointer in losslessly. It's dead handy. (My current project involves a system with 16-bit ints, 32-bit long longs, and 20-bit pointers...)
- fugyk 11y agoHow can they hide the bug in an open source project after fixing it. Isn't commit public?