简体   繁体   中英

Why do we need the address-of operator (ampersand, &) when passing a variable to scanf, but not when assigning to it?

When using the scanf function, we cannot simply specify the variable name. Rather, we have to prefix the variable name with an ampersand, eg int a; printf("enter your value"); scanf("%d", &a); int a; printf("enter your value"); scanf("%d", &a); .

I checked online sources searching for the reason. Most of them say something like:

  1. We need to modify the value in that variable. We add & to denote a memory location, so that the value at that location can be changed.

  2. The arguments to the scanf() function have pointer type: it needs to be given a memory address, so that it can change the variable's value.

I am new to coding, so I am little bit confused about this. If we need the address to modify what the memory contains, why do we not use & when assigning to a variable, eg int a; a = 5; int a; a = 5; ? In this example, we also we modify the value. Why do we not write it like this: int a; &a = 5; int a; &a = 5; ?

… why do we not use & when assigning to a variable, eg int a; a = 5; int a; a = 5; ? In this example, we also we modify the value. Why do we not write it like this: int a; &a = 5; int a; &a = 5; ?

C has automatic conversion from lvalues to values. In x + y * z , each of x , y , and z is not a value but rather designates an object ; it is some memory reserved for x , y , or z along with an associated type that tells us how to interpret the memory. But what we actually want this expression to do is multiply the value of y by the value of z and add the value of x . To make the code easier to write, C automatically converts any designation of an object into its value, in places where we want its value. It automatically gets the value from memory and uses it in the expression.

The rule for this is in C 2018 (the 2018 version of the C standard) clause 6.3.2.1 paragraph 2. This rule says that an lvalue is automatically converted to the object's value in many places. “lvalue” is the term the standard uses for an expression that designates an object. I will explain the term in a moment.

The rule says:

Except when it is the operand of the sizeof operator, the unary & operator, the ++ operator, the -- operator, or the left operand of the . operator or an assignment operator, an lvalue that does not have array type is converted to the value stored in the designated object (and is no longer an lvalue); this is called lvalue conversion

For the moment, ignore all of those except the assignment operator. This rule says that in:

a = x + y * z;

x , y , and z are converted to their values, but a is not converted because it is the left operand of an assignment operator. That means on the left side of an assignment, we do not have a value but have the actual object. That is what allows an assignment to put a value into the object.

This is where the term “lvalue” originates—from the l eft side of an assignment, because that is where lvalues are most common or prominent. Going back to those other exceptions for the conversion rule (the operand of sizeof and so on), those are other places where we want to operate on the object itself, not its value. The sizeof operator gives the size of an object. The & takes the address of an object. ++ and -- increment or decrement the value stored in an object. . references a member in a structure or union object.

Now we can see why we must pass the address to scanf . There are actually two reasons. One is that this lvalue conversion rule will convert any object in the arguments to a function call to its value. If we write scanf("%d", a); , a would be automatically converted to its value. And, in C as it originally developed, there was no good way to modify the automatic conversion rule to make it not convert some function arguments and not others, because the compiler generally did not know what parameters a function expected. If the code contained the call foo(a) , the compiler would not know whether foo expected to receive the value of a or a designation of the object a . So we had no way of writing the conversion rule to sometimes convert a function argument and sometimes not. The second reason is that C has no way to pass an object designation in a function call. (We can pass a pointer, and that passes the address of the object, and then function must then convert the pointer to an object designation by using * to dereference the pointer.)

(Once function prototype declarations were added to C, it would have been possible to solve both of those issues and pass objects to functions. C++ did that with its reference type.)

In the BLISS programming language, there is no automatic conversion of lvalues to values. Whenever you want to use the value of an object, you have to explicitly indicate that by putting a . before the object designator. For example, in BLISS, you would write:

a = .x + .y * .z;

to say to multiply the value of y by the value of z , add the value of x , and store the result in a .

Because you want scanf() to take in input, and write that data to the memory location of the variable you need the data to be in.

& means you are passing the memory address of that variable.

So:

int x = 0; 
// int is usually 4 bytes, so x is 4 bytes.
// And it has a memory address, for example: 0x12345678

scanf("%d", &x); // basically passes 0x12345678 to scanf

// scanf assumes the argument to be a pointer, so it writes 
// to memory address 0x12345678.
// You have specified %d, so scanf assumes that the first argument
// next to the format-string("%d") is an int * (int pointer)

Why do you have to pass the address? Because you want scanf to write at the memory address of (x), not at the memory address (x).

Doing this will result in undefined behaviour:

int x = 0;
scanf("%d", (int *)x);
// scanf will write to address 0x0, since x is 0

Fun fact: NULL is C is usually defined as (void *)0 . So the above is equivalent to scanf("%d", NULL);

= is an operator, not a function call, so it already knows the memory address of the variable. So it doesn't need the & .

Why we not write like this: int a; &a = 5;?

  1. Because we can't write to an address (technically, &a is not an l-value ).
  2. Why the clutter? No sane language makes assignment harder than it should be.
  3. Some languages (often interpreted ones) allow something you suggest (eg the shell's getopt abc varibablename ) but the designers of C decided to not go that way.

Buckle in, this is going to take a few minutes.

When you call a function with arguments (parameters), there are several different evaluation strategies for how the function handles those arguments. The one used by C is known as "call by value". When you call a function like

foo( x, y, z );

each of the expressions x , y , and z is fully evaluated and the results of those evaluations are what get passed to the function. In the function definition

void foo( int a, int b, int c ) 
{
  a = b + c;
} 

each of the formal parameters a , b , and c are different objects in memory from x , y , and z - they receive the values of x , y , and z , but the update to a does not affect x in any way. Here's an example that illustrates this directly:

#include <stdio.h>

void foo( int a, int b, int c )
{
  a = b + c;
  printf( "a = %d\n", a );
}

int main( void )
{
  int x = 1;
  int y = 2; 

  printf( "before foo: x = %d, y = %d\n" );
  foo( x, y, x + y );
  printf( "after foo:  x = %d, y = %d\n" );

  return 0;
}

When run, this code gives the output:

before foo: x = 1, y = 2
a = 5
after foo:  x = 1, y = 2

As you can see, updating a has no effect on the value of x .

In order for a function to change the value of a parameter, we must pass a pointer to that parameter. If we want changes in a to be reflected in x , we must pass the address of x in the function call using the unary & (address-of) operator and dereference it in the function definition using the unary * (dereference) operator:

#include <stdio.h>

void foo( int *a, int b, int c )
{
  *a = b + c;
  printf( "*a = %d\n", *a );
}

int main( void )
{
  int x = 1;
  int y = 2;

  printf( "before foo: x = %d, y = %d\n", x, y );
  foo( &x, y, x + y );
  printf( "after foo:  x = %d, y = %d\n", x, y );

  return 0;
}

In this case, a doesn't receive the value of x , but rather its address , giving us this situation:

 a == &x // int * == int *
*a ==  x // int == int

The expression *a in foo acts as kind of a synonym for x in main , so writing a new value to *a is equivalent to writing a new value to x .

And this, ultimately, is why you need to pass pointers to the things you want to update when you call scanf (or any other function that needs to update a parameter).

NOTE - arrays are weird. Unless it is the operand of the sizeof or unary & operators, or is a string literal used to initialize a character array in a declaration, an expression of type "N-element array of T " will be converted, or "decay", to an expression of type "pointer to T " and the value of the expression will be the address of the first element of the array. In other words, if you have code like this:

int arr[10];
...
bar( arr );

the compiler replaces the expression arr in the function call with an expression equivalent to &arr[0] , so instead of bar getting a copy of the entire array, it gets the address of the first element:

void bar( int *a )
{
  ...
}

This is why you don't use the & operator when reading strings with scanf - you're passing an array expression as an argument, which is implicitly converted to a pointer before the call:

 int val;
 char name[10];

 scanf( "%d %s", &val, name );

There is a reason for this behavior, but that's a bit beyond the scope of this discussion. Just remember that arrays are weird.

#include <stdio.h>
int main()
{
int x;
scanf("%d",&x);
printf("the value is stored in x is :",x);
return 0;
}

Because a value passed to Function is just a copy of original value. if function operation is over then the copy value is deleted. that's why we pass ('&'address) for change value from its own location. if we not pass address its change only in function only, if we exit the function the value is stored in variable is changed to its previous value.

code ex.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM