Author Topic: Is there a difference between "unsigned long" and "uint32_t" for 32-bits MCU?  (Read 20486 times)

0 Members and 2 Guests are viewing this topic.

Online peter-h

  • Super Contributor
  • ***
  • Posts: 6014
  • Country: gb
  • Doing electronics since the 1960s...
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.
Z80 Z180 Z280 Z8 S8 8031 8051 H8/300 H8/500 80x86 90S1200 32F417
 

Offline paulca

  • Super Contributor
  • ***
  • Posts: 6425
  • Country: gb
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.
"What could possibly go wrong?"
Current Open Projects:  68000 Self Build computer + OS.
 

Online NorthGuy

  • Super Contributor
  • ***
  • Posts: 3526
  • Country: ca
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.

You 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.
 

Online Siwastaja

  • Super Contributor
  • ***
  • Posts: 11218
  • Country: fi
You write code for the compiler. You write comments for humans. As simple as that.

I 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 are designed to 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" and assuming it 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 of at least 0..255 and the fact larger range can be used if it 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.
 

Offline Kalvin

  • Super Contributor
  • ***
  • Posts: 2175
  • Country: fi
  • Embedded SW/HW.
You write code for the compiler. You write comments for humans. As simple as that.

If you say so.

Below is code written for the compiler, with comments for humans:

Code: [Select]
#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: [Select]
#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 key
struct node* find(int key) {

   //start from the first link
   struct node* current = head;

   //if list is empty
   if(head == NULL) {
      return NULL;
   }

   //navigate through list
   while(current->key != key) {

      //if it is last node
      if(current->next == NULL) {
         return NULL;
      } else {
         //go to next link
         current = current->next;
      }
   }     

   //if data found, return the current Link
   return current;
}

Same code. Spot any difference? Which one would you like to maintain?
 

Online Siwastaja

  • Super Contributor
  • ***
  • Posts: 11218
  • Country: fi
Try to remove comments from the Kalvin's second piece of code. I think it actually gets more readable, 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 like
Code: [Select]
// NULL pointer is used to signify the last element of the listwhich documents a convention which the code relies on. This particular code works without only because it's so obvious that anyone can guess this 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.
 

Offline Kalvin

  • Super Contributor
  • ***
  • Posts: 2175
  • Country: fi
  • Embedded SW/HW.
I just took the first code snippet that I found when googling "C source code linked list", and run it through C/C++ Obfuscator.
 

Offline JPortici

  • Super Contributor
  • ***
  • Posts: 3913
  • Country: it
You write code for the compiler. You write comments for humans.

Sorry, 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.
 

Online NorthGuy

  • Super Contributor
  • ***
  • Posts: 3526
  • Country: ca
Same code written for humans and the compiler:

Code: [Select]
#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 key
struct node* find(int key) {

   //start from the first link
   struct node* current = head;

   //if list is empty
   if(head == NULL) {
      return NULL;
   }

   //navigate through list
   while(current->key != key) {

      //if it is last node
      if(current->next == NULL) {
         return NULL;
      } else {
         //go to next link
         current = current->next;
      }
   }     

   //if data found, return the current Link
   return current;
}

Same code. Spot any difference? Which one would you like to maintain?

For me, that is the code written for the compiler with comments written for humans:

Code: [Select]
// ------------------------------------------- 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 found

struct 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?
 
The following users thanked this post: Siwastaja

Offline paulca

  • Super Contributor
  • ***
  • Posts: 6425
  • Country: gb
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* node

What 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?
« Last Edit: December 29, 2022, 01:38:13 pm by paulca »
"What could possibly go wrong?"
Current Open Projects:  68000 Self Build computer + OS.
 

Online NorthGuy

  • Super Contributor
  • ***
  • Posts: 3526
  • Country: ca
Naming variables to shadow the type is leaving the interruption entirely in the readers understanding of token precedence.

node* node

What could possibly go wrong?

Code: [Select]
struct node* node
Struct 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. 
 

Online SiliconWizard

  • Super Contributor
  • ***
  • Posts: 17793
  • Country: fr
Naming variables to shadow the type is leaving the interruption entirely in the readers understanding of token precedence.

node* node

What could possibly go wrong?

Code: [Select]
struct node* node
Struct 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.

Oh 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.
« Last Edit: December 29, 2022, 06:54:36 pm by SiliconWizard »
 

Online NorthGuy

  • Super Contributor
  • ***
  • Posts: 3526
  • Country: ca
Oh yeah, which doesn't mean the above is a good idea though.

This is not an idea. This is a coincidence.

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.

Sure. 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.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf

 

-->