Is there a difference between "unsigned long" and "uint32_t" for 32-bits MCU? - Page 1
Forum section: electronics / general

author: unscripted
author_rank: Contributor
timestamp: December 22, 2022, 01:40:52 pm
content: I'm going through other people code, just studying it and found these lines a bit confusing:uint8_t *destPtr;destPtr = (uint8_t *)((unsigned long)(uint32_t)&modInfo.Data);Does this make sense in a 32-bit MCU? Aren'tunsigned longanduint32_tboth 32 bits in size? Is there another difference than can make the application behave differently if replacing unsigned long with uint32_t?

author: Kalvin
author_rank: Super Contributor
timestamp: December 22, 2022, 01:53:31 pm
content: Quote from: unscripted on December 22, 2022, 01:40:52 pmI guess this might be a bit simpler solution without unnecessary trickery:Code:destPtr = (uint8_t *) &modInfo.Data;In order to be 100% sure whether your assumption is correct, you can use a static assert in your code:Code:static_assert(sizeof(unsigned long) == 4, "Unsigned long not 4 bytes");static_assert(sizeof(uint32_t) == 4, "uint32_t not 4 bytes");

author: Sacodepatatas
author_rank: Regular Contributor
timestamp: December 22, 2022, 02:44:12 pm
content: Quote from: Kalvin on December 22, 2022, 01:53:31 pmI see still some redundancy within that code because of type conversion of a pointer to a variable and then getting the address of such var for casting to a uint8_t pointer type. I guess that the compiler takes care of this redundancy, but i think the code gets more simple by doing like this:Code:destPtr = (uint8_t *) (modInfo.Data+1);

author: Siwastaja
author_rank: Super Contributor
timestamp: December 22, 2022, 03:07:24 pm
content: unsigned long is of architecture-specific size. You need to read the documentation to find out, or write a simple test program which prints the result of sizeof (unsigned long).This is EXACTLY why uint32_t and friends were introduced in C99. Just use them when it matters.If you have to interface with existing code you don't want to modify, and you still need to assume the size, add a static assert like Kalvin showed.

author: NorthGuy
author_rank: Super Contributor
timestamp: December 22, 2022, 06:32:40 pm
content: Quote from: unscripted on December 22, 2022, 01:40:52 pmNo. This just shows that a person who wrote the code had no idea what he's doing. uint32_t and Co are used when you want a variable of the specified length (32-bit) regardless of anything. Casting pointers to uint32_t will work if the pointer fits into 32 bits, and will crash otherwise (for example on a 64-bit CPU). Doing this where there's absolutely no need for such casting is plain stupid.

author: SiliconWizard
author_rank: Super Contributor
timestamp: December 22, 2022, 07:03:09 pm
content: Quote from: unscripted on December 22, 2022, 01:40:52 pmThe above line of code shows, at best, a poor command of C.- The double cast doesn't add any value here.- Casting a pointer to an integer doesn't make any sense in this context. It's absolutely useless, and could be wrong if pointers don't fit in a (whatever is shorter on the given platform) uint32_t or unsigned long. The right C type to use for casting pointers to integers is uintptr_t. But again in this context, it's absolutely useless even if using the right type. You don't need to cast to anything, the '&' operator already gives you a pointer, that can be cast to another pointer type directly, so all you need is 'destPtr = (uint8_t *) &modInfo.Data;'.(Whether this cast would then make sense is another matter, depends on what you are accessing.)

author: wek
author_rank: Frequent Contributor
timestamp: December 23, 2022, 12:15:35 am
content: Quote from: SiliconWizard on December 22, 2022, 07:03:09 pmIt can be used to strip qualifiers thus avoid warnings.JW

author: SiliconWizard
author_rank: Super Contributor
timestamp: December 23, 2022, 12:54:27 am
content: Quote from: wek on December 23, 2022, 12:15:35 amNo. You use cast to pointers directly for that. No need to cast to integers and back.

author: wek
author_rank: Frequent Contributor
timestamp: December 23, 2022, 01:29:51 am
content: >>> But again in this context, it's absolutely useless even if using the right type.>> It can be used to strip qualifiers thus avoid warnings.> No. You use cast to pointers directly for that. No need to cast to integers and back.Not if you use gcc with -Wcast-qual.That may sound strange, but I want to catch *inadvertently* stripped qualifiers, so the cast through integer indicates intention. Disabling the warning through pragmas or function attributes is not always practical.JW

author: JPortici
author_rank: Super Contributor
timestamp: December 23, 2022, 06:04:09 am
content: Quote from: Sacodepatatas on December 22, 2022, 02:44:12 pmwhy would you do that? pointer arithmetic always gives me headaches and leaves room for nasty bugs. if the aim is to get the address of element one in the array, use the address of element one in the array. That is the most clear for humans

author: NorthGuy
author_rank: Super Contributor
timestamp: December 23, 2022, 03:21:49 pm
content: Quote from: JPortici on December 23, 2022, 06:04:09 amIt's the same. The C-99 says:Quotehence &x is the same as &*(x+y) same as (x+y)

author: Siwastaja
author_rank: Super Contributor
timestamp: December 23, 2022, 04:05:56 pm
content: It's technically the same, but always consider what meaning you convey to the human reader of the code.I prefer arithmetic using + when for example summing a peripheral register memory address from multiple offsets (like AHB_BASE + PERIPHERAL_BASE + 42) and then cast that to the final type.When accessing something that's either anarray, or usually is originally an array but lost its array-ness by being passed to a function as a pointer, it's quite obvious to use []:Code:void func(uint8_t *buf, size_t len){buf // looks better...}// ... because it's usually used like this:uint8_t buf;func(buf, sizeof buf);

author: NorthGuy
author_rank: Super Contributor
timestamp: December 23, 2022, 04:25:50 pm
content: Quote from: Siwastaja on December 23, 2022, 04:05:56 pmWhy is that?Consider, for example, this:Code:ptr++;vs this:Code:ptr = &ptr;

author: Siwastaja
author_rank: Super Contributor
timestamp: December 23, 2022, 05:32:49 pm
content: Yeah, ptr++ surely looks understandable, corresponding to natural language "take the next element as our baseline".But, if you want to access the 42th element, then ptr sounds more natural than *(ptr+42).Good style depends on case.

author: JPortici
author_rank: Super Contributor
timestamp: December 23, 2022, 06:06:48 pm
content: Quote from: NorthGuy on December 23, 2022, 03:21:49 pmI know it's technically the same. That's not the point.using the address of the nth array element is a far more readable way. And less error prone (did you mean to get the address of the nth entry? or of the nth byte after the entry?)

author: SiliconWizard
author_rank: Super Contributor
timestamp: December 23, 2022, 06:59:31 pm
content: Quote from: wek on December 23, 2022, 01:29:51 amI see. Never enabled this warning but, why not. Although your approach indeed circumvents the warning and "kinda" indicates intention, a cast to integer and back may not be very obvious to the reader either here, so dunno. Obviously your coding style choice and why not. If you do that, just use (u)intptr_t though. That's the only portable way of doing it.

author: SiliconWizard
author_rank: Super Contributor
timestamp: December 23, 2022, 07:01:28 pm
content: Quote from: JPortici on December 23, 2022, 06:06:48 pmIt's really down to coding style, I have no strong feeling either way. I personally have zero problem with pointer arthimetic that I find pretty straightforward once you stop thinking about C as assembly and stop thinking about C pointers as low-level addresses.But to each their style, both can be defended.

author: NorthGuy
author_rank: Super Contributor
timestamp: December 23, 2022, 08:12:39 pm
content: Quote from: JPortici on December 23, 2022, 06:06:48 pmYou always get the n-th element, not n-th byte.To get a pointer to the n-th byte you need to cast to a type which is 1-byte long, sayCode:uint8_t *a;a = &((uint8_t*)x);a = (uint8_t*)x+2;

author: NorthGuy
author_rank: Super Contributor
timestamp: December 23, 2022, 08:28:04 pm
content: Quote from: Siwastaja on December 23, 2022, 05:32:49 pmSure, accessing elements is more natural with [], getting pointers is another story.I usually think of other things when I type, so I can type &ptr or (ptr+42). Doesn't matter much to me. Right now (ptr+42) is somewhat more appealing to me, but I am sure there were times in my life where I had different preferences, and I sure can feel different about this in the future. Within the same program, I always try to use the same method, but I have no logical explanation for that.

author: peter-h
author_rank: Super Contributor
timestamp: December 26, 2022, 09:32:04 pm
content: Surely the answer to the question is a simple "no" - for all normal CPUs.And similarly an "int" is same as "long" and same as int32_t.Are there any exceptions?I am a mere novice here...I never use a "long", but I do use "int" for trivia like for loop variables which only run from 0 to 1000Plus there is the silent internal promotion of all integers to "int" in C.My 32F4 code will never be ported down to a Z80, and I will be long dead before it gets ported to a 64 bit CPU, but if it was, it should still run the same, except any "int" will take up 8 bytes instead of 4.

author: Kalvin
author_rank: Super Contributor
timestamp: December 27, 2022, 07:36:04 am
content: Quote from: peter-h on December 26, 2022, 09:32:04 pmCheck this Wikipedia page:https://en.wikipedia.org/wiki/C_data_typesC data typeintis at least 16-bits in size. Typically the size of theintdata type is identical to the native data size of the target system. For example, in systems with 16-bit architecture the size of theintis 16 bits, and in 32-bit architecturesintis 32 bits. In 8-bit systems, the size of theintwill be 16 bits by definition.C data typelongis at least 32-bits in size.C data typeint32_tis exactly 32-bits in size by definition.As you can see, the data sizes ofint,longandint32_twill probably be the same only in 32-bit architectures.As a rule of a thumb it is recommended to useintin loops and for temporary variables (unless you know better, or the numerical range ofintis not sufficient), as this will typically produce the best performance and best code size. Usinglongin the loops and for temporary variables where the numerical range ofintwould suffice is just a bad habit, which may lead poor performance and larger code size.

author: BBBbbb
author_rank: Unknown
timestamp: December 27, 2022, 08:18:48 am
content: Regarding OP's Q at the start - I'm pretty sure this was done to eliminate some linter warnings, possibly on a legacy code later converted to use stdint.I've seen this type of messy casting only when people set their brains on autopilot and start cleaning linter report after they've done the code. Many don't try to understand those warnings and see them as a nuisance, thus such castings occur.

author: mikerj
author_rank: Super Contributor
timestamp: December 27, 2022, 09:12:46 am
content: Quote from: peter-h on December 26, 2022, 09:32:04 pmIt's not unusual for an int to be 16 bits on small (usually 8 bit) micros, the Microchip PIC C compiler for the 12F/16F/18F parts (formerly HTSoft) and most 8051 compilers use this. There are also some oddball things like a "short long" being 24 bits on the PIC compiler, pretty handy for saving space.

author: Siwastaja
author_rank: Super Contributor
timestamp: December 27, 2022, 12:54:48 pm
content: Quote from: Kalvin on December 27, 2022, 07:36:04 amIt's a crappy rule of thumb, but I admit following it too; out of habit, and laziness to change.The actually good and portable solution to achieve the optimal performance is to pick the smallest (u)int_fastXX_t. For example, if the loop variable runs from 0 to 100, use int_fast8_t, and it will providebetterperformance on 8-bit architectures, because int would be 16-bits. Yet, compiler would use 32-bitinton a 32-bit architecture, fitting one native register and not having to perform bit masking operations.But writing int_fast16_t is more work than just writingint, I admit.

author: Kalvin
author_rank: Super Contributor
timestamp: December 27, 2022, 02:17:46 pm
content: Quote from: Siwastaja on December 27, 2022, 12:54:48 pmI do know that the given rule of thumb is not optimal like you pointed out, but personally I consider that pretty good rule of thumb and compromise for everyday C programming, even in the embedded world.“Premature optimization is the root of all evil”- Donald Knuth

author: peter-h
author_rank: Super Contributor
timestamp: December 27, 2022, 05:21:04 pm
content: The OP talks about a 32 bit CPU.If you are writing C code for the old 8/16 bit CPUs then everything changes and much more thought needs to be given to performance. The difference could be literally 100x to 1000x. I worked on such things in the 1980s. One guy was writing in C and I was writing the high perf bits in assembler.

author: paulca
author_rank: Super Contributor
timestamp: December 28, 2022, 11:14:59 am
content: You can see the two types of C programmer in this thread.* The ones who think 1Mb of HD space still costs £10,000 and write C like they wish they were writing assembler and that they don't even have a compiler.* The ones who realise code is for HUMANS.The later cost the industry less money and time these days so I'm on their side.ANYONE who starts using single character variables and irritating void pointer mathematics gets their pull require rejected. If it's not readable by every member in the team it's worthless, even if it works perfectly.

author: NorthGuy
author_rank: Super Contributor
timestamp: December 28, 2022, 03:32:16 pm
content: Quote from: paulca on December 28, 2022, 11:14:59 amYou write code for the compiler. You write comments for humans. As simple as that.Of course, big companies who want to amass big pools of cheap deplorable codes will have to restrict what the coders can or cannot do with strict rules. But there's no reason to insist that such rules should be applied to every free-thinking human being.

author: Siwastaja
author_rank: Super Contributor
timestamp: December 28, 2022, 05:09:48 pm
content: Quote from: NorthGuy on December 28, 2022, 03:32:16 pmI rarely disagree with you, but this time I do.You write code for both humans, and compiler; I'd even say human first. Good compilers aredesignedto consume code written for humans, so there usually is no conflict here at all; good code works for both.If one finds oneself "outsmarting" the human reader to "satisfy" the compiler all the time, then this is probably due to wrong assumptions (i.e., trying to write "portable assembler C" or "obfuscated C" andassumingit compiles into more efficient binary, without ever checking). Some ideas of "efficient C" can be also decades old, and worked with compilers of 1980-1990's.Only rarely you have to prioritize the compiler over human reader. In such cases, comments are of course of utmost importance. But generally, I would recommend using comments to document the ideas (why do something the way it's done, how it was done before, why it was changed, what to consider in the future...), and let the code itself document the trivial "what it does" part.Many features serve both human readers and compilers well. For example, uint_fast8_t communicates the required range ofat least 0..255and the fact larger range can be usedifit provides performance gains. Both compiler and human reader consumes this information, and because it's standardized since C99, human reader does not need to remember project-specific conventions.

author: Kalvin
author_rank: Super Contributor
timestamp: December 28, 2022, 05:23:07 pm
content: Quote from: NorthGuy on December 28, 2022, 03:32:16 pmIf you say so.Below is code written for the compiler, with comments for humans:Code:#include <stdio.h>#include <string.h>#include <stdlib.h>#include <stdbool.h>struct node{int data;int key;struct node *next;};struct node* o_e21552a9af5ed08c016c839d7c3d1904=NULL;/* find a link with given key */ struct node * o_a02219af0d4b30036859b1c4e02a6f81(int o_b676f94be60655dc700fbdb14dcf0b67){/* start from the first link */ struct node* o_88cf849b1aa547af4983bf1dab9e06b1=o_e21552a9af5ed08c016c839d7c3d1904;/* if list is empty */ if (o_e21552a9af5ed08c016c839d7c3d1904 == NULL){return NULL;};/* navigate through list */ while (o_88cf849b1aa547af4983bf1dab9e06b1->key != o_b676f94be60655dc700fbdb14dcf0b67){/* if it is last node */ if (o_88cf849b1aa547af4983bf1dab9e06b1->next == NULL){return NULL;}else {/* go to next link */ o_88cf849b1aa547af4983bf1dab9e06b1 = o_88cf849b1aa547af4983bf1dab9e06b1->next;};};/* if data found, return the current Link */ return o_88cf849b1aa547af4983bf1dab9e06b1;};Same code written for humans and the compiler:Code:#include <stdio.h>#include <string.h>#include <stdlib.h>#include <stdbool.h>struct node {int data;int key;struct node *next;};struct node *head = NULL;//find a link with given keystruct node* find(int key) {//start from the first linkstruct node* current = head;//if list is emptyif(head == NULL) {return NULL;}//navigate through listwhile(current->key != key) {//if it is last nodeif(current->next == NULL) {return NULL;} else {//go to next linkcurrent = current->next;}}//if data found, return the current Linkreturn current;}Same code. Spot any difference? Which one would you like to maintain?

author: Siwastaja
author_rank: Super Contributor
timestamp: December 28, 2022, 05:29:50 pm
content: Try to remove comments from the Kalvin's second piece of code. I think it actually getsmorereadable, because comments do not add any value as the code itself says exactly the same, so the comments only increase the mental load, having to read everything twice (and waste time in useless comments). You might want to add a comment likeCode:// NULL pointer is used to signify the last element of the listwhich documents aconventionwhich the code relies on. This particular code works without only because it's so obvious that anyone canguessthis intent by reading the code.Such useless comments are actually a good indicator that the code itself is OK. Sometimes we are taught at school to write comments like that, and like training wheels, they are useful when one still struggles with basic concepts of the language (like what -> means, or what ++ does). But sticking to such comments for too long prevents one from starting to write good kind of comments.

author: Kalvin
author_rank: Super Contributor
timestamp: December 28, 2022, 05:33:13 pm
content: I just took the first code snippet that I found when googling "C source code linked list", and run it through C/C++ Obfuscator.

author: JPortici
author_rank: Super Contributor
timestamp: December 28, 2022, 05:54:46 pm
content: Quote from: NorthGuy on December 28, 2022, 03:32:16 pmSorry, but i disagree. Source code is for humans as well.Simple, clear statements you don't need to comment, there's a reason humans developed coding standards.And compilers have become really smart over the years. Once you throw optimizations into the mix, it's not unlikely that compound statements will compile to the same binary as a sequence of smaller, simpler statements describing the same operation (unless of course something in there is volatile)and if performance is so critical that you need to write "clever code" please just write an assembly module and call the function from C.

author: NorthGuy
author_rank: Super Contributor
timestamp: December 28, 2022, 06:51:29 pm
content: Quote from: Kalvin on December 28, 2022, 05:23:07 pmFor me, that is the code written for the compiler with comments written for humans:Code:// ------------------------------------------- find_key()// searches for a given key starting from a given node (may be NULL)// returns the first occurence of a node with the matching key or NULL if nothing is foundstruct node* find_key(struct node* node, int key) {while (node) {if (node->key == key) return node;node = node->next;}return NULL;}Which one would you like to maintain?

author: paulca
author_rank: Super Contributor
timestamp: December 29, 2022, 01:17:15 pm
content: The only code review comment I would have to that is...Naming variables to shadow the type is leaving the interruption entirely in the readers understanding of token precedence.node* nodeWhat could possibly go wrong?Two very common occurrences of this I see daily are... "date" and "str". At least make it "myDate" or "myStr"! I have even seen database columns called "date" when "date" is a keyword and causes everyone to escape stuff to use the column. How did that get through a review?

author: NorthGuy
author_rank: Super Contributor
timestamp: December 29, 2022, 06:12:33 pm
content: Quote from: paulca on December 29, 2022, 01:17:15 pmCode:struct node* nodeStruct tags have their own name space. Nothing is wrong if the same name is used elsewhere.These are very basics of C. If you hired people who cannot understand C syntax, they will get you in troubles no matter how hard you try to restrict them. You get what you paid for.

author: SiliconWizard
author_rank: Super Contributor
timestamp: December 29, 2022, 06:50:17 pm
content: Quote from: NorthGuy on December 29, 2022, 06:12:33 pmOh yeah, which doesn't mean the above is a good idea though.But in this example, the wrong part is not particularly that the same identifier (albeit in a different namespace) is used for the struct type and the variable. It's rather that the struct identifier is much too generic to be meaningful. The variable identifier itself, especially if it has a very limited scope, is probably alright.If you make your identifiers a bit more descriptive (don't go overboard either), this kind of stuff will almost never happen.

author: NorthGuy
author_rank: Super Contributor
timestamp: December 29, 2022, 10:03:14 pm
content: Quote from: SiliconWizard on December 29, 2022, 06:50:17 pmThis is not an idea. This is a coincidence.Quote from: SiliconWizard on December 29, 2022, 06:50:17 pmSure. With C, you need to name things so that there's no conflict between different compilation units. For example, I add prefixes. Local variables will never have such prefixes, so such chance is nearly zero.