4 ms·
If 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". W
by TheNewAndy 10y ago
If 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?
- lake99 10y agoI did not expect you'd say "char", because it's a fixed size numeric type. Isn't that what you have been arguing against so far? Anyway, I'd get yelled at if I checked in code where I do math on char. It has to be on uint8 or int8 (or other widths). Not that I'd ever check in such code, after all, I agree with the company policy there. But "char" is just a part of the problem. We have the same issue with all kinds of bid-widths. When I read PC code with such names, I don't worry about their sizes, and just assume i386 conventions. When I read embedded C code with datatypes of undeclared widths, I get quite annoyed. Thankfully, I can't recall the last time I read such code. > then uint_least32_t should make you happy The thing is, I assume all uintX types to really mean at least X bits wide. I am aware that the compiler is free to use its discretion for allocating memory. I worry about finer details only when I need to.