3

We use ampersand(&) operator during user input. It means we are assigning the particular value to the memory address of the particular variable. But during assignment of a value, we don't need ampersand. How is the value being passed without any memory address specification? Sample code is given below.

#include<stdio.h>

int main()
{
    int a, b, total;
    scanf("%d%d", &a, &b); //& for memory address
    total = a+b;           //how the value is being tranfarred to total without any memory address?   
    printf("%d", total);

    return 0;
}
Sohid Ullah
  • 75
  • 1
  • 1
  • 7
  • 3
    I guess the concepts you need to look up first are "pass by value" and "pass by reference". And which of these is used in C. – Eugene Sh. May 31 '18 at 18:07
  • `scanf` needs to know where to put the value, and so it is passed the address of the variable needing the value. If you just passed `a` and `b` then `scanf` receives those variable's current values (if any, since `a` and `b` have not been initialised), which are of no use to it. – Weather Vane May 31 '18 at 18:11
  • Something like: `a` on the right-hand-side is the _value_ of `a`. `total` on the left-hand-side is a _reference_ of `total`. With `scanf("%d%d", &a, &b);`, `&a` is a RHS. – chux - Reinstate Monica May 31 '18 at 18:19
  • 2
    I've always thought `scanf` was evil, a cruel hoax played on new learners, but here's yet another example of its perfidy, making the suggestion that somehow `&` is somehow necessary for assignment in general. – Steve Summit May 31 '18 at 18:39
  • 3
    @PeterJ_01: How is it not a programming question? The OP is trying to understand how C works, and this is a fairly subtle point, having to do with lvalues and how they're handled in different contexts. – Keith Thompson May 31 '18 at 18:39
  • It definitely isn't good question, which is why it doesn't get any upvotes, but I don't think it deserves to be closed. For beginners, this exact question really causes confusions and crashes and all kinds of problems. – Edenia May 31 '18 at 19:06
  • @SteveSummit 100%. And the C books. Many hundreds of pages. Who is insane enough to read them? – 0___________ May 31 '18 at 19:26
  • He won't learn C asking this question anyway. – Edenia May 31 '18 at 19:29
  • `a = 5;` tells the compiler "put the number 5 into the memory allocated for the variable `a`". It knows where that is, and generates the code. `scanf("%d", &a)` tells the compiler to put the address of `a` on the stack and compile a call to `scanf`, which it does. But then `scanf` runs *at runtime*, not compile time, so it knows nothing about variables, just what's on the stack. If we had done `scanf("%d", a)`, the compiler would have put the current value of `a` on the stack, and scanf would find that useless at runtime (or it would interpret that as an address and stomp on random memory). – Lee Daniel Crocker May 31 '18 at 20:07
  • @Edenia: It's gotten 3 upvotes so far, including one from me. My answer addresses a fairly subtle point that a beginner might not pick up easily, that an expression on the LHS of an assignment is handled differently than an expression in other contexts. *lvalues* are not an easy concept; it took the C committee 22 years and three editions of the standard to come up with a consistent and correct definition of the term. – Keith Thompson May 31 '18 at 20:10
  • @KeithThompson I don't see the upvotes :) probably I don't have the right to. – Edenia May 31 '18 at 20:12
  • @Edenia: As I type this, the question as 4 upvotes and 3 downvotes. With enough rep, you can click the "1" and see it split into upvotes and downvotes. – Keith Thompson May 31 '18 at 21:06
  • @KeithThompson Nope, this advantage requires higher level of reputation. I cannot see them split, but I can see the 1 upvote now. This is totally expected. – Edenia May 31 '18 at 21:50

3 Answers3

5

The scanf function has to access the object into which a value is being read. All C function arguments are passed by value, so if you wrote

int a, b;
scanf("%d%d", a, b); // THIS IS WRONG

the scanf function would only be able to see the values of a and b as they were when the function was called. It would have no way to modify a.

Assignment requires an lvalue as its left operand. An lvalue (to simplify slightly) is an expression that designates an object; the name of a variable is the most common kind of lvalue. Since the assignment operator = is built into the language, it can have special rules that apply to it. In particular, the lvalue on the LHS of the = identifies the object being assigned to; it doesn't yield the (previous) value of that object. That's how assignment is defined by the language.

For example, if you write:

a = b;

both a and b are lvalues, but they're treated differently because of their context. a refers to the object whose name is a. b also refers to an object, but in that context it yields the current value of that object.

If assignment were implemented as a function call, it would have to take the address of the object being assigned to:

int assign(int *lhs, int rhs);
assign(&a, b);

The language could have required an address for the LHS of an assignment, but allowing just the name of the target object is much more convenient.

(To explain the simplification above, the current standard's definition of lvalue is "an expression (with an object type other than void) that potentially designates an object". The word "potentially" is there because, for example, *ptr where ptr is a pointer is an lvalue even if the current value of ptr is NULL. Being an lvalue is a compile-time concept. Attempting to assign a value to *ptr is legal, but has undefined behavior.)

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
  • To add something `scanf("%d%d", a, b); // THIS IS WRONG` It will assume that `a` and `b` are address to variables (the va_arg will try to cast them as pointers) and either try to de-reference them or overflow them (if a string was to be expected with `%s`. Which all leads to undefined behavior. But if `a` and `b` are both initialized I think standard functions guarantee to have checks against `NULL` and then nothing will happen. – Edenia May 31 '18 at 18:42
  • @Edenia: No casts occur. `scanf` will *assume* that the arguments are of type `int*`. Passing an argument of the wrong type has undefined behavior. Passing a null pointer of the correct type has undefined behavior. Initializing `a` and `b` won't change that. – Keith Thompson May 31 '18 at 20:07
  • variadic functions use va_arg(list, type) (since the type isn't known) where the type MUST be correct dereferancable type. – Edenia May 31 '18 at 20:15
  • printf-alike functions tend to detect NULL pointers provided (for instance, `printf("%s\n", NULL);` may be kind and print "(null)", You are right scanf doesn't though. It's been a while since I last passed NULL to it. Or to other standard function for that matter. – Edenia May 31 '18 at 20:17
  • @Edenia what has `printf` to do with it. You have copied my comment from another recent question. This is about a destination, not a source, as the other question. – Weather Vane May 31 '18 at 20:20
  • @WeatherVane Yes I did, because I was switching from the one to the other, and I am tired of typing. I'd like to reuse at least a little. Anyway I just said that it was my bad not recalling how scanf behaves when dealing with null pointers passed to it. I never said I am certain so I always had one in mind. – Edenia May 31 '18 at 20:24
  • @WeatherVane Also, AWKWARD – Edenia May 31 '18 at 20:27
  • @Edenia please don't post comments about `scanf` and then say "my bad not recalling how scanf behaves". Be helpful. – Weather Vane May 31 '18 at 20:29
  • @WeatherVane I am sorry for that (although not comments, not even a comment just a portion of one comment, to be exact). I thought using famous people's comments and quotes from answers is not a bad idea. Though I did that for first time. So don't worry. – Edenia May 31 '18 at 20:30
  • 2
    @Edenia I suppose `scanf` family too could be kind when given a `NULL` pointer and return `0` but C isn't a very kind language. – Weather Vane May 31 '18 at 20:36
  • @WeatherVane: I'm not sure that would be kind. There's no portable way in general to check for invalid pointers. An implementation of `scanf` could check for null pointers (and behave how?), but it can't detect a pointer to an object that no longer exists, or a garbage pointer from an uninitialized object -- or `&a` when you meant `&b`. Ultimately it's up to the programmer to pass correct arguments. – Keith Thompson May 31 '18 at 21:10
  • It could be mentioned that "pass by value" implies *lvalue conversion* when the arguments are lvalues, i.e. the function does not receive an lvalue. There are other languages that have syntax to suppress that conversion and the function does receive an lvalue. – M.M Jun 01 '18 at 00:53
1

Assignment is a C language construct.

C knows that an assignment doesn't work with the value of its left hand side operand but rather with its address*. You don't need to say & explicitly.

With user-defined functions, and especially prototypeless old-style functions (unspecified argument types) and variadic functions like scanf, C can't know what they want -- they might want the address or they might equally well want the value and so you have to specify that by prepending or not prepending the ampersand.

Now with a prototyped function like:

void take_address(int *x);

you could theoretically extend the language so that

void use_take_address(void){ int x=42;  take_address(x); }

would implicitly take the address of the argument (C++ does something like that with references, after all), but it would, arguably, harm readability.

With &x you can be sure that the function can at least theoretically modify x; without & you can be more or less sure it can't (unless x is an array).

It's a language design choice.


*This is actually not 100% accurate. An assignment might as well be working with a register-based variable that does not have an address and using & on a variable in C actually disqualifies it from being explicitly markable with register, but those are details that a good optimizer can get past.

Petr Skocik
  • 58,047
  • 6
  • 95
  • 142
1

Variables are an abstract unit that has addresses associated with it. Variables declared with syntaxes such as int var represent a value located on the particular address &var.

scanf expects POINTERS to variables, i.e address, which is why you use the ampersand. This is needed because when you pass variables to functions (without the ampersand), you only pass their value, not the address information of the actual variable. Some people like to say that when you pass a variable to a function, the function takes "a copy" of the value of the variable.

If you want to decalre pointers (variables that represent addresses of their particular type, instead of values) you do it as such: int* var; This variable will be used to try to access another variable of type int, by assigning its address to it. int* var = &some_int

Take for example the following scenario. You want to make a function that stores some value in a variable passed to it. To do that you must define your function like that:

void foo (int* var)
{
*var = 1; // * is the de-reference operator which shows the value of the address `var` and we assign the value to 1.
}
Edenia
  • 2,312
  • 1
  • 16
  • 33
  • what about the `register` variables. Are they addressable? What are your thoughts about them. – 0___________ May 31 '18 at 18:32
  • 2
    Well I think In C, you cannot take the address of a variable with register storage. As the C standard claims at Cf. C11 6.7.1/6: But register is storage type modifier and I don't know if it is subject of the matter. – Edenia May 31 '18 at 18:33