5 ms·
> How do you port it to a machine without a 16-bit type? The problem is made more severe when you use "int" and let the compiler decide the size of the variabl
by lake99 10y ago
> How do you port it to a machine without a 16-bit type?
The problem is made more severe when you use "int" and let the compiler decide the size of the variable. I guess I don't know what you're advocating.
> The stuff about communicating with the outside world is quite different.
I meant communicating as a throwaway example. It's a valid example, but really, everything gets affected. For example, a CAN identifier is 29 bits. What generic "int" would you use when you want to fill in this identifier? And no, I work at a level that's lower than an ABI. So, no OS, no file IO, etc.
- adrianN 10y agoHow does it make portability worse if you write your code in a way that doesn't assume anything about the size of your ints? If you don't know that your ints are 16 or 32 or 27 bits long you have to be more careful with checking for overflows, but if the code does that I don't see what problems you'll have when you port it to a platform with a different int size.
- lake99 10y agoYou can only check for overflows if you err in favour of larger architectures and declare all integers as "long int", and then check for overflows. Even the 8-bit compilers I have seen will allocate 32-bits for long int. And doing that will let the code remain compatible on smaller architectures, but it will hammer your CPU and memory usage. It's not a pleasant situation when you're struggling to save bytes.
- TheNewAndy 10y agoYou can't ever check for (signed) overflows in C - you can only prevent them. I'm not sure if I'm drawing a distinction you weren't intending to make (if so, then my apologies). When you are adding signed integers in C, you need to prove that doing so can't overflow - knowing the exact width doesn't really help you here - just prove it for the narrowest possible width and you have proved it for all of them. In practice this isn't hard because either it is trivial to prove (e.g. incrementing a variable which is known to be small, or just plain modifying the code to put in explicit saturation/other logic for the cases where things get hairy) Of course an 8-bit compiler is going to allocate 32-bits for a long int - the language requires that a long is at least 32-bits wide. If you had a compiler that didn't do this, then it isn't a C compiler.
- TheNewAndy 10y agoIf you use "int", you know you have at least 16-bits to play with. Take some correct code that works with int16_t, and replace the "int16_t" with plain "int". What breaks? 1. If you are dumping it out to file or similar (i.e. assuming the layout in memory) then your code is no longer portable between different endian machines. 2. If you are relying on overflow behaviour, then your code is broken already (this doesn't apply to unsigned types, but using minimum width types and explicit mods/masks is likely going to be clearer code anyway). 3. The one place where you can't replace int16_t with int is in the case where you make use of the assumption that an int16_t can represent -0x8000 and you then port to a machine that doesn't use two's complement and int is 16-bits... I'm not aware of any such machine, but that is about the extent of it. What type to use for a 29-bit CAN identifier? A "long" will do just fine. It is guaranteed to be at least 32-bits wide, and also to exist. Would you suggest a uint29_t?
- lake99 10y ago> Take some correct code that works with int16_t, and replace the "int16_t" with plain "int". What breaks? Memory. Defaulting to 16-bits when an 8-bit variable will do can be incredibly wasteful on an 8-bit µC. Keep in mind that we are not just talking about one variable in isolation. We are talking about all the integers we pass between functions. We are talking about code space, data space, and stack space. There are compilers that can optimize their arithmetic operations to 8-bit registers when they can be sure that that's all their operands need. > Would you suggest a uint29_t? If you are aware of a machine that provides that, yes! Otherwise, I'd suggest uint32_t. Yes, "long" is guranteed to be 32-bits wide, but it can also be 64-bits wide. I would not recommend defaulting to "long", as that could be wasteful. Here's an interesting discussion about the meaning of "long" and "long long" for their compiler: https://www.dsprelated.com/showthread/comp.dsp/42108-1.php https://www.dsprelated.com/showthread/comp.dsp/42108-1.php. I see this discussion as a failure of the C standard. I would much rather the compiler provided int64_t or int40_t or whatever else they can, that is not inefficient.
- TheNewAndy 10y ago> Memory. Defaulting to 16-bits when an 8-bit variable will do can be incredibly wasteful on an 8-bit µC. I hope it doesn't seem like I'm just moving the goalposts, but the obvious answer here is to use a type which is at least 8-bits wide if you only need 8-bits. Such a type exists, and is called "char" (with the appropriate signedness modifiers). This whole discussion has been about writing portable code. Using a "char" here is going to work perfectly on your 8-bit uC. It is also going to work perfectly on your SHARC chip with 32-bit chars. > If you are aware of a machine that provides that, yes! Otherwise, I'd suggest uint32_t. I think we are talking about different things. I am talking about writing portable code. You are talking about writing code that only works on a particular machine (or the class of machines that have a 29-bit int). I know there is a place for that code, and once you are writing code where you actually need to make use of knowledge about the machine, then I'm all for using types that make this clear. However, typically this code only lives at the edges of the system, and the actual "computation" can be written in portable, efficient, readable code without a great deal of trouble. I'm completely aware that long can be 64-bits on some machines. If you care so much about the wasted memory, then uint_least32_t should make you happy - if you are happy with the C99 dependency (which can limit portability, though things are getting better), then I don't see how you can see this as being worse than the fixed width uint32_t. Personally, I have found that while it sounds nice in theory, the domains I've been working in have meant that the memory doesn't make much difference (if it is just sitting on the stack or being passed between functions, then on 64-bit machines, there is typically no differences, as calling conventions tend to pad things out). It is only when you have an array of these in memory that it might start to matter, and here it tends not to matter a great deal - you are typically now optimizing an algorithm for an amd64 machine (read - it has plenty of memory) and the algorithm typically doesn't actually use a lot of it (since it needs to run on tiny micros too). Anyway, I think we probably agree for code which isn't supposed to be portable. Potentially just that I tend to work more on the code that is supposed to be portable, and you work on the code at the edges?