the asterisc in C - Page 1
Forum section: electronics / general

author: Simon
author_rank: Unknown
timestamp: September 28, 2019, 11:27:44 am
content: I am now aware that * can mean multiplication or pointer. So how are the two functions distiguised? is it the space in the multiplication?A+B works but would A*B work or would that be interpreted as "B" being a pointer? So it should be written as A * B?

author: T3sl4co1l
author_rank: Super Contributor
timestamp: September 28, 2019, 11:47:02 am
content: Whitespace is stripped so "A*B" is all that matters.Dereference is a unary operation, so "A dereference B" is meaningless. Therefore multiplication is unambiguously chosen.You could however have "A**B" which would parse as "A*(*B)" or "A times (dereference B)".Dereference can also be an LVALUE, i.e.,*B = 1;is a valid statement, butA*B = 1;is an error: the result of multiplication can only be an RVALUE (the expression returns a number).As for the other form, direct pointer arithmetic is discouraged -- though perfectly syntactically valid. The preferred alternative is array access, "B", which is equivalent to *(B+A*sizeof(B)).Likewise, unary "&" is the reference-of operator, while binary "&" is bitwise-and (and "&&" is logical-and).Tim

author: NivagSwerdna
author_rank: Super Contributor
timestamp: September 28, 2019, 11:52:38 am
content: de-reference has higher operator precedence than multiply in C.https://en.cppreference.com/w/c/language/operator_precedence

author: Simon
author_rank: Unknown
timestamp: September 28, 2019, 12:12:30 pm
content: So:#define SL_PORT_REGISTER(port, sl_register) *(uint32_t *)(SL_PORTS + (port*SL_PORT) + sl_register)The port*SL_PORT multiplication would not become misinterpreted?

author: T3sl4co1l
author_rank: Super Contributor
timestamp: September 28, 2019, 12:22:44 pm
content: Right, though the leading asterisk could be misinterpreted:A SL_PORT_REGISTER(port, sl_register)would be parsed as A times the expression the macro expands to. I guess the first * should be in parentheses? Or is it really needed because it's already cast to a pointer type? (Is it a double pointer type? I've always been easily confused by pointer types...)Tim

author: Nusa
author_rank: Super Contributor
timestamp: September 28, 2019, 12:26:34 pm
content: Quote from: Simon on September 28, 2019, 12:12:30 pmRemember that #define is preprocessor TEXT replacement, and the input values of "port" and "sl_register" can be nearly anything, including complicated or partial expressions. So the answer to that will depend on what it looks like after replacement in each instance it's used in.You can never use too many parens in #defines. I'd probably rewrite it like so for both clarity and safety:#define SL_PORT_REGISTER(port, sl_register) *(uint32_t *)(SL_PORTS + ((port)*SL_PORT) + (sl_register))A more interesting thing is something like *index++; Precedence will tell you whether that evaluates as (*index)++ or *(index++), but I'll leave that answer as an exercise for you. I'd suggest using parens as a rule in either case to avoid confusion.

author: Simon
author_rank: Unknown
timestamp: September 28, 2019, 12:34:39 pm
content: Quote from: T3sl4co1l on September 28, 2019, 12:22:44 pmMy understanding is that *(uint32_t *) will point to a register of the number that follows. The following math was to save time creating every single register setup manually.

author: RoGeorge
author_rank: Super Contributor
timestamp: September 28, 2019, 12:58:04 pm
content: Quote from: Simon on September 28, 2019, 12:34:39 pmAfter the C preprocessor will end its job, your source code will become*(uint32_t *)a_constant_address_at_runtime*(uint32_t *) is explained inhttps://stackoverflow.com/questions/48833976/what-is-uint32-tDoes this answers your question?

author: Simon
author_rank: Unknown
timestamp: September 28, 2019, 03:02:47 pm
content: Quote from: RoGeorge on September 28, 2019, 12:58:04 pmThe link was interesting but i was more interested how the "*" is interpreted.

author: Jeroen3
author_rank: Super Contributor
timestamp: September 28, 2019, 03:48:09 pm
content: This is the kind of stuff C does to make you lose your mind.Standard says*: The operand of the unary * operator shall have pointer type.Which I think means that, for rvalues, as long as whatever you put after the * isn't a pointer type, it will be multiplication.*6.5.3.2 Address and indirection operators

author: magic
author_rank: Super Contributor
timestamp: September 28, 2019, 05:04:24 pm
content: Quote from: Simon on September 28, 2019, 12:12:30 pmI would hope so, becauseport somethingsomethingdoesn't make any sense in C so the * has to be multiplication.I have never heard of compilers confusing the two as long as the code is correct and doesn't try to multiply without anything on the left side of * or things like that.Quote from: Jeroen3 on September 28, 2019, 03:48:09 pmNope, here is an rvalue interpreted as a dereference of a float variable.Code:$ cat asterisk.cint main() {float p=3.14;return *p;}$ clang asterisk.c -o /dev/nullasterisk.c:3:9: error: indirection requires pointer operand ('float' invalid)return *p;^~1 error generated.GCC would do the same but the error message doesn't spell it out as explicitly so I used clang for this example.This distinction is made syntactically, based on whether the code matches pattern A*B or only *B.

author: T3sl4co1l
author_rank: Super Contributor
timestamp: September 28, 2019, 06:17:43 pm
content: This, and as I noted above, "A**B" is also unambiguous, as *B can be collapsed to a numerical value, and A*(*B) can then be made from it.(I don't know that it parses it precisely that way. It's perfectly equivalent to read A first, and look for a binary operator following it, and then look for another value following that. Which means *B must be the unary operator, and which converts B from an incompatible type into its dereferenced type, which presumably is compatible and therefore compiles without error.)Tim

author: SiliconWizard
author_rank: Super Contributor
timestamp: September 28, 2019, 06:32:10 pm
content: Quote from: Jeroen3 on September 28, 2019, 03:48:09 pmNot quite. That's when ALL terms have their importance in a technical document, and especially in standards.Before the fact it's followed or not by a pointer type, here they talk about the UNARY * operator.By definition, a unary operator CANNOT have an operand on both of its sides. Otherwise, it's treated as a binary operator. The additional rule is just that as a unary operator, it's only defined as a dereference operator, and thus its operand can only have pointer type.Take this expression: 'A * B', here the '*' operator can't be the unary *. There are two operands.Now take this one: A * *B. The second '*' can only be a unary operator: on its left, there is NO operand, just another operator, and it follows that the first "*" can only be a binary operator. Thus this is non-ambiguously "A times dereferenced B", and by the rule above, B must have pointer type. "A * *B" (which of course can be written "A**B", whitespaces don't matter here, it's just less readable for humans if you omit them), if B doesn't have pointer type, is an error and will not compile.Any expression A * B, whatever the types of A and B, can only be treated as a multiplication in C.Now consider this:Code:int A, *B, C;C = A * B;There is no ambiguity. GCC will, for instance, tell you: "error: invalid operands to binary * (have 'int' and 'int *')". It will never even consider the "*" here as a unary operator, since it's not used as one.It may tickle the eye, but it's not ambiguous. It works as any unary operator. You can consider the "-" operator, which also has both forms (unary and binary), to see this.