Author Topic: What can this be? C command  (Read 3106 times)

0 Members and 1 Guest are viewing this topic.

Offline PerranOakTopic starter

  • Frequent Contributor
  • **
  • Posts: 548
  • Country: gb
What can this be? C command
« on: December 11, 2019, 04:35:08 pm »
These lines were in a programme I am studying:

       #define STROBE PORTBbits.RB5=1,PORTBbits.RB5=0;
       #define RS PORTBbits.RB4

I get that the second line assigns the name "RS" to that bit but shouldn't it end with a semicolon?

The first line I don't get at all - what can it be doing?

Cheers.
You can release yourself but the only way to go is down!
RJD
 

Offline Andy Watson

  • Super Contributor
  • ***
  • Posts: 2085
Re: What can this be? C command
« Reply #1 on: December 11, 2019, 05:08:17 pm »
"#define" is not compiled. Think of it as a pre-process directive that is the equivalent of find-and-replace. "#define this_string that_string" causes all occurances of this_string to be replaced with that_string. If, when the compiler encounters that_string, it makes sense within the language syntax then it's fine. For example, STROBE on its own would work - it willl be replaced by legitimate statement(s), however, if you tried something like "A_variable = STROBE + Another_variable;" the complier would throw a wobbly because it would find a semi-colon mid-statement.
 

Offline AG6QR

  • Frequent Contributor
  • **
  • Posts: 857
  • Country: us
    • AG6QR Blog
Re: What can this be? C command
« Reply #2 on: December 11, 2019, 05:16:32 pm »
Those lines begin with the pound sign, so they are instructions to the C preprocessor.  They #define macros for use elsewhere in your code.

#define foo some other stuff!

The above line will define the macro named "foo" to have the contents "some other stuff!".  That definition is syntactically legal. It means that subsequent instances of the text "foo" will be interpreted as "some other stuff!" by the compiler.  That's not a particularly useful example, because use of that macro would most likely result in a syntax error.
 

Offline SiliconWizard

  • Super Contributor
  • ***
  • Posts: 14472
  • Country: fr
Re: What can this be? C command
« Reply #3 on: December 12, 2019, 01:18:00 am »
As said above, those are preprocessor macros. You should definitely learn about that if you're using C.

They are basically text replacement definitions. The first one here will make the preprocessor (which runs before actual compilation) simply replace every occurence of the "STROBE" token in the source file with "PORTBbits.RB5=1,PORTBbits.RB5=0;". What it's meant to do is rather straightforward: "pulse" RB5. The end ';' will just be added verbatim during the text substitution. It has no meaning to the macro declaration itself.

Similarly, the second one will just make the preprocessor replace "RS" with "PORTBbits.RB4". Simple as that. The use? Everytime you would need to write "PORTBbits.RB4", you will just need to write "RS" instead. It's shorter, supposed to be explanatory (RS is probably a signal's name in the schematic that's connected to RB4 of the MCU), and can be easily modified so that all your code that relies on this IO will automatically get updated just by modifying the macro itself if you ever need to change it to something else (another IO).

Macros just yield verbatim text replacement (and can have parameters, here there isn't any).

Although the first one is correct per se, I would call it bad style: to avoid confusion, a macro acting like a function should preferably be declared as a function-like macro IMO:
#define STROBE() PORTBbits.RB5=1,PORTBbits.RB5=0
(As you can see, I wouldn't put a final ';' to this either.)

As it was initially, it was meant to be used like so in the code: "STROBE", whereas my prefered version would be used like so: "STROBE();"

To make it less error-prone, the even prefered way of declaring it would be:
#define STROBE() do { PORTBbits.RB5=1; PORTBbits.RB5=0; } while (0)

The reason to use the above construct is explained in another recent thread.


 

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 4036
  • Country: nz
Re: What can this be? C command
« Reply #4 on: December 12, 2019, 02:52:07 am »
These lines were in a programme I am studying:

       #define STROBE PORTBbits.RB5=1,PORTBbits.RB5=0;
       #define RS PORTBbits.RB4

I get that the second line assigns the name "RS" to that bit but shouldn't it end with a semicolon?

The first line I don't get at all - what can it be doing?

I hope you got the idea that the idea of the first line is to turn an output signal ON and then as quickly as possible turn it OFF again. This is likely to be less than a microsecond even on a slow CPU such as an AVR or PIC, and it might be just a few nano-seconds on a fast CPU (which could be a problem, in fact). This will be used as some kind of timing mark by whatever is connected to that output.

The other key thing is this doesn't happen where the #define is written, but where STROBE is used later in the program.

I second what SiliconWizard says about "do { } while(0)". The following page explains *why*

http://www.bruceblinn.com/linuxinfo/DoWhile.html

Unfortunately it doesn't explain why this isn't quite enough to be sure:

Code: [Select]
#define STROBE() {PORTBbits.RB5=1; PORTBbits.RB5=0}

The issue is that if someone writes...

Code: [Select]
if (something) STROBE(); else something_else;

... as you would expect to do if STROBE() was a normal statement or function call, the compiler will give a syntax error because there shouldn't be semicolon between } and else.

As pointed out, ending a macro with a semicolon is bad practice.
 

Offline Nominal Animal

  • Super Contributor
  • ***
  • Posts: 6260
  • Country: fi
    • My home page and email address
Re: What can this be? C command
« Reply #5 on: December 12, 2019, 06:49:55 am »
    #define STROBE PORTBbits.RB5=1,PORTBbits.RB5=0;
That should really be something like
    #define  STROBE  do { PORTBbits.RB5=1; PORTBits.RB5=0; } while (0)
as this is the standard idiom to use when you need a macro that consists of more than one statement.

Like others have explained, #define defines a preprocessor macro.  When you use the defined name (which is by convention written in upper case, but even lower case name), the preprocessor replaces it with the definition.  Macros can also take arguments, but that's not important here.

The syntax for argumentless macros is
    #define  NAME  statement(s)...
Note that there is no semicolon; none of the preprocessor directives (beginning with # ) end with a semicolon, as they are not C, but just preprocessor directives.

So, if you have code that does e.g.
    STROBE;
the preprocessor converts it to
    PORTBbits.RB5=1,PORTBbits.RB5=0;;
or, using the preferred definition, to
    do { PORTBbits.RB5=1; PORTBits.RB5=0; } while (0);
which can safely used in if clauses and for loops without braces.

It essentially assumes that port B pin 5 is an output.  The macro turns it on/high, and immediately off/low.  In other words, "strobes" the pin.
 

Offline PerranOakTopic starter

  • Frequent Contributor
  • **
  • Posts: 548
  • Country: gb
Re: What can this be? C command
« Reply #6 on: December 13, 2019, 12:54:08 pm »
Brilliant, thank you all.

The bit I REALLY didn't get is the comma between the two statements in STROBE. It is simply that when STROBE (or rather the commands that replace it) is encountered when running it executes the first one then immediately executes the second one. (This command is part of the initialisation of an LCD display.)

The problem I have is that you can't look-up this sort of thing in a C book or in searching what would I search for - "comma between statements in C language #define"?

I still don't get why some commands end in a semicolon and others don't.
You can release yourself but the only way to go is down!
RJD
 

Online jfiresto

  • Frequent Contributor
  • **
  • Posts: 818
  • Country: de
Re: What can this be? C command
« Reply #7 on: December 13, 2019, 04:06:14 pm »
... The bit I REALLY didn't get is the comma between the two statements in STROBE....The problem I have is that you can't look-up this sort of thing in a C book....

You can if it is a competent one. If you search for "comma" in the index of Kernighan and Ritchie, The C Programming Language, the (Old Testament) C Language bible everyone used years ago, you will find two pages about the "comma operator", that tersely explain its operation and use.

Quote
I still don't get why some commands end in a semicolon and others don't.

Because, as some used to say, syntactic sugar causes cancer of the semicolons. I will let someone else answer.
-John
 

Offline PerranOakTopic starter

  • Frequent Contributor
  • **
  • Posts: 548
  • Country: gb
Re: What can this be? C command
« Reply #8 on: December 13, 2019, 04:28:01 pm »
Darn, you're right! I have that book and it is as you say.  :-[
You can release yourself but the only way to go is down!
RJD
 

Offline westfw

  • Super Contributor
  • ***
  • Posts: 4199
  • Country: us
Re: What can this be? C command
« Reply #9 on: December 14, 2019, 09:38:14 am »
Putting a semicolon at the end of some text in a line of C terminates the "C statement."Most #define statements (which are pre-processor statements rather than C statements) are designed to be used INSIDE of C statements without terminating the statement.

For example, with #define RS PORTBbits.RB4
You could put together a more complex statements like:
Code: [Select]
RS = digitalRead(switch);
RS = !RS;  // toggle
OUT = RS | CS | SWITCH;
Since the preprocessor just does text substitution, if it has the semicolon each of those would result in illegal statements (like "PORTBbits.RB4; = !PORTBbits.RB4;;"
 

Offline Jan Audio

  • Frequent Contributor
  • **
  • Posts: 820
  • Country: nl
Re: What can this be? C command
« Reply #10 on: December 14, 2019, 02:54:08 pm »
Always define your own stuff.
Dont use other persons defines.
If you need them you use them.
 

Offline PerranOakTopic starter

  • Frequent Contributor
  • **
  • Posts: 548
  • Country: gb
Re: What can this be? C command
« Reply #11 on: December 14, 2019, 08:05:20 pm »
Cheers.

I'm still confused about semicolons but I guess it will come with experience.

I am going through a course and really have to follow the book and therefore can't readily "go my own way".
« Last Edit: December 14, 2019, 08:17:44 pm by PerranOak »
You can release yourself but the only way to go is down!
RJD
 

Offline Nerull

  • Frequent Contributor
  • **
  • Posts: 694
Re: What can this be? C command
« Reply #12 on: December 14, 2019, 09:08:24 pm »
Brilliant, thank you all.

The bit I REALLY didn't get is the comma between the two statements in STROBE. It is simply that when STROBE (or rather the commands that replace it) is encountered when running it executes the first one then immediately executes the second one. (This command is part of the initialisation of an LCD display.)

The problem I have is that you can't look-up this sort of thing in a C book or in searching what would I search for - "comma between statements in C language #define"?

I still don't get why some commands end in a semicolon and others don't.

You can think of the C preprocessor as an almost completely separate system and  language to C. It's a text-replacement engine that runs on your source file before it ever gets to the compiler. C commands end in semicolons, but preprocessor commands don't, because its a different language.

The line "#define STROBE PORTBbits.RB5=1,PORTBbits.RB5=0;" tells the preprocessor to search through the rest of your source file after this point and literally just replace every instance of the text "STROBE" with the text "PORTBbits.RB5=1,PORTBbits.RB5=0;". The preprocessor doesn't care what this text is, it's just doing search and replace.

In this  instance, one line ends in a semicolon, while the other does not because of where they are meant to be used. STROBE is being used as a command. You type STROBE somewhere, and the search-and-replace engine replaces it with the code above, which is a C statement and must end in a semicolon.

RS, on the other hand, is meant to be used as a variable.

If you typed, for example "RB = 1;" somewhere in your code, the preprocessor's search and replace engine would replace this with "PORTBbits.RB4 = 1;". If you changed the macro to "#define RS PORTBbits.RB4;" than the preprocessor would instead blindly change your code to this: "PORTBbits.RB4; = 1;" which obviously isn't going to make the compiler very happy.
« Last Edit: December 14, 2019, 09:15:07 pm by Nerull »
 
The following users thanked this post: PerranOak

Offline westfw

  • Super Contributor
  • ***
  • Posts: 4199
  • Country: us
Re: What can this be? C command
« Reply #13 on: December 15, 2019, 03:05:18 am »
Quote
Always define your own stuff.
Dont use other persons defines.
Um.   Always use the vendor's defines for the onchip peripherals...

Replacing someone else's defines in a "almost right" re-use situation would ... somewhere between a useless act of NIH Syndrome, and an positive step in improvement and understanding the code.

Depending on how bad the existing code is.  (I really don't like that STROBE definition.  It'll break editor code shaping, and correct-looking statements like:
Code: [Select]
    if (x)
      STROBE;    else
      x = 1;
 

Offline HwAoRrDk

  • Super Contributor
  • ***
  • Posts: 1477
  • Country: gb
Re: What can this be? C command
« Reply #14 on: December 15, 2019, 08:45:25 am »
As an aside, one common place you will see the comma operator used in everyday C code is in for loops that have multiple iteration variables. For example, if you have a loop like the following:

Code: [Select]
for(int a = 0, b = 0; a < 5; a++, b++) {
    /* ... */
}

The third semicolon-separated part within the parenthesis ("a++, b++"), the iteration expression, is actually using the comma operator.

Note that the comma operator is not to be confused with declaring multiple variables of the same type at once in a single statement (e.g. "unsigned int x = 0, y = 0;"). The comma in that case is not the comma operator, but simply a delimiter.

An example of how the comma operator can come in handy is when you want to use and then evaluate a temporary variable within an expression (e.g. an if statement):

Code: [Select]
int temp;
if(some_var == foo && (do_something(some_var, &temp), temp == CONST_XYZ)) {
    /* ... */
}

Here, the right-hand side of the 'and' expression: calls the do_something() function, which takes a pointer to temp as an argument and changes its value (this is commonly referred to as an output argument); ignores any returned value from the function; evaluates the equality expression against temp's new value; and, finally, uses the result of that in the 'and' operation.

You could write the above without, but it would be more verbose:

Code: [Select]
int temp;
if(some_var == foo) {
    do_something(some_var, &temp);
    if(temp == CONST_XYZ) {
        /* ... */
    }
}
 

Offline gmb42

  • Frequent Contributor
  • **
  • Posts: 294
  • Country: gb
Re: What can this be? C command
« Reply #15 on: December 15, 2019, 09:47:24 am »
Code: [Select]
int temp;
if(some_var == foo && (do_something(some_var, &temp), temp == CONST_XYZ)) {
    /* ... */
}

...

Code: [Select]
int temp;
if(some_var == foo) {
    do_something(some_var, &temp);
    if(temp == CONST_XYZ) {
        /* ... */
    }
}

The former piece of code would fail any code review and coding standards I've ever used.  The latter would be acceptable if the declaration of temp was moved inside the scope of the if block.

Being clever with the comma operator just makes unmaintainable code.
 

Offline HwAoRrDk

  • Super Contributor
  • ***
  • Posts: 1477
  • Country: gb
Re: What can this be? C command
« Reply #16 on: December 15, 2019, 10:20:30 am »
The former piece of code would fail any code review and coding standards I've ever used.  The latter would be acceptable if the declaration of temp was moved inside the scope of the if block.

Being clever with the comma operator just makes unmaintainable code.

Well, it was a contrived example, and not necessarily ideally what one should do. But the example usage is one I have found to be not uncommon, and have used myself in the past where I wanted the flow of code to be 'uninterrupted' by excessively nested statements.

I suppose if you have coding standards to adhere to that target the lowest common denominator, then usage of language features that are not well understood by many (e.g. in OP's case) are indeed to be avoided.
 

Offline Jan Audio

  • Frequent Contributor
  • **
  • Posts: 820
  • Country: nl
Re: What can this be? C command
« Reply #17 on: December 15, 2019, 02:24:02 pm »
error detected :
PORTBbits.RB5=1,PORTBbits.RB5=0;
should be
PORTBbits.RB5=1; PORTBbits.RB5=0;

What i mean is you take the code from the defines and place them in your code below.

Where they type STROBE you copy this :

PORTBbits.RB5=1;
PORTBbits.RB5=0;

For RS you copy :
PORTBbits.RB4

Now you find out you need a RS define because it is more readable :
#define REGISTER_SELECT /*here the pin you want*/ PORTBbits.RB4

See it as a memory support for you to know what pin is what for easy coding.

For a define with more lines you use the \ character like this :

#define REGISTER_SELECT PORTBbits.RB5=1; \
PORTBbits.RB5=0;
« Last Edit: December 15, 2019, 02:27:59 pm by Jan Audio »
 

Offline langwadt

  • Super Contributor
  • ***
  • Posts: 4427
  • Country: dk
Re: What can this be? C command
« Reply #18 on: December 15, 2019, 02:58:26 pm »
error detected :
PORTBbits.RB5=1,PORTBbits.RB5=0;
should be
PORTBbits.RB5=1; PORTBbits.RB5=0;


comma also works and I'd say in this case it is the better choice, because it makes it a work like a "single" statement

 

Offline Nominal Animal

  • Super Contributor
  • ***
  • Posts: 6260
  • Country: fi
    • My home page and email address
Re: What can this be? C command
« Reply #19 on: December 16, 2019, 09:02:26 am »
The reason the "do { stuff1; stuff2; } while (0)" idiom is preferred over "(stuff1,stuff2)" is that the former is a statement, and the latter is an expression: the former does not yield any value, whereas the latter yields the value of the final expression.

That is, if you have
Code: [Select]
#define  STROBE1  do { PORTBbits.RB5=1; PORTBbits.RB5=0; } while (0)
#define  STROBE2  (PORTBbits.RB5=1,PORTBbits.RB5=0)
you can do e.g.
    a = STROBE2;
but you cannot do
    a = STROBE1;

Only when the macro should evaluate to some (useful) value, the comma-based one is preferred.

Some compilers, like GCC, clang, and ICC, support an extension to C, where a code scope can be treated as an expression:
Code: [Select]
#define  TOGGLE ({ int _bit = PORTBbits.RB5; PORTBbits.RB5 = !_bit; _bit; })
This is much like a normal code block, except that because it is in parenthesis, it behaves like a single expression, yielding the value of the final expression in the scope.  Above, it yields the value of _bit, i.e. the state of the port B pin 5 before the toggle.  Do remember that this is not standard C, and is not portable across all C compilers, though.  It is just common enough so it makes sense to recognize it and its uses.  For example,
    if (TOGGLE) {
        /* Port B pin 5 was HIGH, now toggled to LOW. */
    } else {
        /* Port B pin 5 was LOW, now toggled to HIGH. */
    }
 

Offline djnz

  • Regular Contributor
  • *
  • Posts: 179
  • Country: 00
Re: What can this be? C command
« Reply #20 on: January 30, 2020, 10:57:34 am »
Perhaps it could be useful to look at the preprocessor output, or like I sometimes like to do, save all intermediate temporary files ( .c, .i, .s)

Something like:

Code: [Select]
gcc -g -W -Wall -Wextra  -O3 -std=gnu11 -fverbose-asm -save-temps=obj -masm=intel   foo.c  -o foo

or, if you are interested in only the preprocessor and only your file, something like

Code: [Select]
gcc -E -nostdinc foo.c

Books are good, but even gcc itself has taught me many things...
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf