2

I looked at this and this and this and this and more.

Question is:

A basic C Programming MOOC on EdX is showing how to access a member of a struct within a function, when the struct was passed by pointer. Why in the world are they using & next to *???

They show: scanf("%lf", &(*studptr).aveGrade);

Why not just use studptr.aveGrade in scanf?

(Leaving aside the separate question, "why use scanf at all")

(Was asked for complete example)

void readStudent(struct student *studptr) {
    print("\nEnter a new student record: \n");
    printf("First name: ");
    scanf("%s", (*studptr).firstName);
    printf("Last name: ");
    scanf("%s", (*studptr).lastName);
    printf("Birth year: ");
    scanf("%d", &(*studptr).birthYear);
    printf("Average grade: ");
    scanf("%lf", &(*studptr).aveGrade);
}
JimLohse
  • 1,209
  • 4
  • 19
  • 44
  • Is it intented, that the first two `scanf()` do not have the `&`? – RobertS supports Monica Cellio Dec 18 '19 at 17:37
  • 3
    @RobertS-ReinstateMonica — yes, the inputs are strings so the `&` is not required. (More precisely, though less concisely, the members `firstname` and `lastname` are almost certainly arrays of `char` and therefore passing them as written passes a `char *` which is what `%s` expects. Adding the `&` would pass a `char (*)[n]` where `n` is the size of the array — a very different type.) – Jonathan Leffler Dec 18 '19 at 17:41
  • @JonathanLeffler Thank you very much, Jonathan. I did not knew the case when you have the input assignment of `scanf`()` to a stucture´s member. So, this coding technique is equivalent to `scanf("%s",array);` when `a` would be an array of `char`, and `&` is redundant, if not invalid though, because `array` already points to the first element of the array, which is required for the operation. – RobertS supports Monica Cellio Dec 18 '19 at 17:56
  • 2
    @RobertS-ReinstateMonica — yes, the situations are essentially the same. Using the `&` is technically incorrect (as I said) because the type of pointer passed in is incorrect. In practice, you normally get away with it, but it is officially UB (undefined behaviour) and UB should be avoided at all costs (and typing one less character is a very small cost). – Jonathan Leffler Dec 18 '19 at 18:01
  • it is not next, as there's a parenthesis in between. Check operator precedence and you'll see that `.` has higher precedence than `&`. – Luis Colorado Dec 20 '19 at 05:26

6 Answers6

8

In the fragment:

scanf("%lf", &(*studptr).aveGrade);

the & operator applies as if there were parentheses:

scanf("%lf", &((*studptr).aveGrade));

There's no good reason to use (*ptr).member in preference to ptr->member; indeed, explicit indirection notation is more verbose, and doubly so when there are chains of operations:

(*(*(*ptr).ptrmember).altptr).member

vs:

ptr->ptrmember->altptr->member

So, the code should be:

scanf("%lf", &studptr->aveGrade);

Yes, the two are 'equivalent', but the arrow notation is idiomatic C and the other is not.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
5

Because & doesn't refer to (*stupdtr), it refers to (*studptr).aveGrade. The . operator has higher precedence.

Federico klez Culloca
  • 26,308
  • 17
  • 56
  • 95
3

The two symbols are referring to different things.

The & is taking the address of the aveGrade member.

The * is deferencing the studptr to get a structure.


If it were up to me, I'd write it using the Arrow operator for pointers-to-structures:

scanf("%lf", &studptr->aveGrade);

If you want parens to clarify:

scanf("%lf", &(studptr->aveGrade) );
abelenky
  • 63,815
  • 23
  • 109
  • 159
  • 2
    The 'parentheses to clarify' might be of use to a novice programmer, but using them emphasises "novice". I couldn't recommend them. YMMV — and apparently does. – Jonathan Leffler Dec 18 '19 at 16:30
3

Why not just use studptr.aveGrade in scanf?

As you wrote studptr is a pointer, So this expression studptr.aveGrade is invalid.

It is correctly to write either studptr->aveGrade or ( *studptr ).aveGrade

So the call of scanf can be written either like

scanf("%lf", &studptr->aveGrade);

or like

scanf("%lf", &(*studptr).aveGrade);

Take into account that

&(*studptr).aveGrade

and

( &*studptr).aveGrade

are different expressions.

The first expression can be rewritten using more parentheses like

& ( ( *studptr ).aveGrade )
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
1

It's true that when & immediately precedes * that the two cancel each other out. However, that's not what's happening here:

&(*studptr).aveGrade

Looking at each operator:

  • The * operator is applied to studptr in the parenthesized expression
  • Given (*studptr), the . operator has higher precedence than & so it is applied next
  • & is then applied to (*studptr).aveGrade)

So & is giving you the address of the aveGrade member of what studptr points to. Without parenthesis, the & and * would cancel and you'd get a syntax error because studptr is a pointer to a struct, not a struct.

dbush
  • 205,898
  • 23
  • 218
  • 273
0

In structure, aveGrade is double so if you want to access that you need to dereference studptr to aveGrade by using ampersand(&). So it's absurd to use (*studptr).aveGrade in scanf.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • Thanks for the answer but I think if you read the other answers you'll see why the construct is not absurd -- just outdated. @Johnathon Leffler's answer https://stackoverflow.com/a/59396315/3255525 explains why -> is better, more accepted practice. – JimLohse Dec 18 '19 at 16:52
  • 1
    @JimLohse yes I know about arrow operator being preferred over this way. They about arrow operator should be preferred in mooc too. – Avneesh Sirohi Dec 18 '19 at 16:59