What can this be? C command - Page 1
Forum section: electronics / general

author: PerranOak
author_rank: Frequent Contributor
timestamp: December 11, 2019, 04:35:08 pm
content: These lines were in a programme I am studying:#define STROBE PORTBbits.RB5=1,PORTBbits.RB5=0;#define RS PORTBbits.RB4I 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.

author: Andy Watson
author_rank: Super Contributor
timestamp: December 11, 2019, 05:08:17 pm
content: "#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.

author: AG6QR
author_rank: Frequent Contributor
timestamp: December 11, 2019, 05:16:32 pm
content: 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.

author: SiliconWizard
author_rank: Super Contributor
timestamp: December 12, 2019, 01:18:00 am
content: 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.

author: brucehoult
author_rank: Super Contributor
timestamp: December 12, 2019, 02:52:07 am
content: Quote from: PerranOak on December 11, 2019, 04:35:08 pmI 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.htmlUnfortunately it doesn't explain why this isn't quite enough to be sure:Code:#define STROBE() {PORTBbits.RB5=1; PORTBbits.RB5=0}The issue is that if someone writes...Code: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.

author: Nominal Animal
author_rank: Super Contributor
timestamp: December 12, 2019, 06:49:55 am
content: Quote from: PerranOak on December 11, 2019, 04:35:08 pmThat 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,#definedefines 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#defineNAMEstatement(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 toPORTBbits.RB5=1,PORTBbits.RB5=0;;or, using the preferred definition, todo { PORTBbits.RB5=1; PORTBits.RB5=0; } while (0);which can safely used inifclauses andforloops 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.

author: PerranOak
author_rank: Frequent Contributor
timestamp: December 13, 2019, 12:54:08 pm
content: 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.

author: jfiresto
author_rank: Frequent Contributor
timestamp: December 13, 2019, 04:06:14 pm
content: Quote from: PerranOak on December 13, 2019, 12:54:08 pmYou 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.QuoteBecause, as some used to say, syntactic sugar causes cancer of the semicolons. I will let someone else answer.

author: PerranOak
author_rank: Frequent Contributor
timestamp: December 13, 2019, 04:28:01 pm
content: Darn, you're right! I have that book and it is as you say.

author: westfw
author_rank: Super Contributor
timestamp: December 14, 2019, 09:38:14 am
content: 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.RB4You could put together a more complex statements like:Code:RS = digitalRead(switch);RS = !RS; // toggleOUT = 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;;"

author: Jan Audio
author_rank: Frequent Contributor
timestamp: December 14, 2019, 02:54:08 pm
content: Always define your own stuff.Dont use other persons defines.If you need them you use them.

author: PerranOak
author_rank: Frequent Contributor
timestamp: December 14, 2019, 08:05:20 pm
content: 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".

author: Nerull
author_rank: Frequent Contributor
timestamp: December 14, 2019, 09:08:24 pm
content: Quote from: PerranOak on December 13, 2019, 12:54:08 pmYou 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.

author: westfw
author_rank: Super Contributor
timestamp: December 15, 2019, 03:05:18 am
content: QuoteUm. 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:if (x)STROBE; elsex = 1;

author: HwAoRrDk
author_rank: Super Contributor
timestamp: December 15, 2019, 08:45:25 am
content: As an aside, one common place you will see the comma operator used in everyday C code is inforloops that have multiple iteration variables. For example, if you have a loop like the following:Code:for(int a = 0, b = 0; a < 5; a++, b++) {/* ... */}The third semicolon-separated part within the parenthesis ("a++, b++"), theiterationexpression, 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. anifstatement):Code:int temp;if(some_var == foo && (do_something(some_var, &temp), temp == CONST_XYZ)) {/* ... */}Here, the right-hand side of the 'and' expression: calls thedo_something()function, which takes a pointer totempas an argument and changes its value (this is commonly referred to as anoutputargument); ignores any returned value from the function; evaluates the equality expression againsttemp'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:int temp;if(some_var == foo) {do_something(some_var, &temp);if(temp == CONST_XYZ) {/* ... */}}

author: gmb42
author_rank: Frequent Contributor
timestamp: December 15, 2019, 09:47:24 am
content: Quote from: HwAoRrDk on December 15, 2019, 08:45:25 amThe 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.

author: HwAoRrDk
author_rank: Super Contributor
timestamp: December 15, 2019, 10:20:30 am
content: Quote from: gmb42 on December 15, 2019, 09:47:24 amWell, it was a contrived example, and not necessarily ideally what oneshoulddo. 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.

author: Jan Audio
author_rank: Frequent Contributor
timestamp: December 15, 2019, 02:24:02 pm
content: error detected :PORTBbits.RB5=1,PORTBbits.RB5=0;should bePORTBbits.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.RB4Now you find out you need a RS define because it is more readable :#define REGISTER_SELECT /*here the pin you want*/ PORTBbits.RB4See 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;

author: langwadt
author_rank: Super Contributor
timestamp: December 15, 2019, 02:58:26 pm
content: Quote from: Jan Audio on December 15, 2019, 02:24:02 pmcomma also works and I'd say in this case it is the better choice, because it makes it a work like a "single" statement

author: Nominal Animal
author_rank: Super Contributor
timestamp: December 16, 2019, 09:02:26 am
content: 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 haveCode:#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 doa = 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:#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. */}

author: djnz
author_rank: Regular Contributor
timestamp: January 30, 2020, 10:57:34 am
content: 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:gcc -g -W -Wall -Wextra -O3 -std=gnu11 -fverbose-asm -save-temps=obj -masm=intel foo.c -o fooor, if you are interested in only the preprocessor and only your file, something likeCode:gcc -E -nostdinc foo.cBooks are good, but even gcc itself has taught me many things...