C: Why do array elements need to be scanned by memory address?

(warning C beginner)

I want to read some integers from the user and store them in an array. So:

int main (void)
{

int i, num, cont = 0;
int arre[10];

for (int i=0;i<5;i++)
{
    scanf("%d", arre[i]);

etc.

      

When I run this, I get segmentation error 11 on OSX. If I run it with Valgrind, the problem occurs when I enter the first integer and it tells me:

==1610== Command: ./ArraysAndPointers
==1610== 
2
==1610== Use of uninitialised value of size 8
==1610==    at 0x18F0BA: __svfscanf_l (in /usr/lib/system/libsystem_c.dylib)
==1610==    by 0x18718A: scanf (in /usr/lib/system/libsystem_c.dylib)
==1610==    by 0x100000F2D: main (ArraysAndPointers.c:11)
==1610== 
==1610== Invalid write of size 4
==1610==    at 0x18F0BA: __svfscanf_l (in /usr/lib/system/libsystem_c.dylib)
==1610==    by 0x18718A: scanf (in /usr/lib/system/libsystem_c.dylib)
==1610==    by 0x100000F2D: main (ArraysAndPointers.c:11)
==1610==  Address 0x0 is not stack'd, malloc'd or (recently) free'd

      

If I add & before arre [i] if the problem is solved. But I do not know why. I am afraid that I am reading an integer but storing (apparently) its memory address in an array. However, when I check its value as it appears in the resulting array, it is the int itself, not the memory address. Why is this so?

Note. I am fundamentally struggling to understand pointer / memory addresses and their relationship to arrays, char *, etc. (see my other questions ) and despite having taken several C training modules with various vendors and watching various explanations on the internet, I have yet to meet someone who can definitively cover up the concept for me. In particular, I'm interested in knowing when and why pointers are needed . If anyone can suggest a good link / video / tutorial / article for me, I would be very grateful.

+3


source to share


5 answers


Let's replace i

with 0. here for an explanation.

scanf("%d", arre[0]);

      

This code goes to the array, looks at the first element and finds it 17 (or whatever), so it passes 17 as the second argument to scanf()

. But it scanf()

expects a pointer, so it gets terribly confusing when it sees 17 and ends up crashing your application.



scanf("%d", &arre[0]);

      

This code calculates the location of the first element in the array and passes that pointer to scanf()

. scanf()

happily writes the value to the memory addressed by that pointer.

+5


source


scanf()

with a format specifier %d

expects int *

as the second parameter.

So, by standards, you need to specify the address of the variable where you are going to store the scanned value.

The man says:



d

Matches an optionally signed decimal integer; the next pointer must be a pointer to int.

Answering your extended question on pointers:

Any good C book will explain in detail why pointers are needed. Just enter it to save the scanned value. In this case it is an array and you need to scan the value into an array giving the address where you need to store the value that is given&arr[i]

+1


source


scanf () wants 2 parameters:

  • format of expected inputs
  • where to store scanned values

The C language can only execute pass-by-value parameters . You cannot tell a function to store a value in a parameter and that the parameter will store that value (technically, the parameters are copied to the stack

- the LIFO queue - and popped off the stack when the function ends). Therefore, if you store a value in a parameter (like int), the value will be lost at the end of the function .

If you want to store the value, since you cannot pass in the variable itself , you are passing the memory address (i.e. & var) to the variable in main memory ( the heap

), not the stack. It will be received by the function in a parameter of type ( int * ), which means what is designed by this memory address is an int variable

.

So, with this address (passed on the stack), you can change what is in main memory (heap), and the value written at that address on the heap will be retained even after the end of the function, because emptying the stack will not empty the heap.

You save the value in a variable at a (for example, int * a: a is a pointer to int), writing: *a = <my int>

.

And for information, the C array variable is actually a pointer to the first element of the array (the address of the first element): arre is the same value as &arre[0]

And arre[n]

there is *(arre + n)

: what is stored in (the address of the array plus the size offset of n elements).

+1


source


C passes all arguments to a function by value, which means that the formal parameter in the function definition is a different object in memory from the actual parameter in the function call. Take a look at the following example:

void swap( int a, int b ) { int tmp = a; a = b; b = tmp; }

void foo( void )
{
  int x = 1, y = 2;
  swap( x, y );
  printf( "x = %d, y = %d\n", x, y );
}

      

The formal parameter a

in swap

is a different object in memory than x

in foo

, so any change we make to a

does not affect x

; after the call, the swap

values x

and y

remain unchanged.

For the function to swap

change the values x

and y

, we must pass pointers to these variables:

void swap( int *a, int *b ) { int tmp = *a; *a = *b; *b = tmp; }

void foo( void )
{
  int x = 1, y = 2;
  swap( &x, &y );
  printf( "x = %d, y = %d\n", x, y );
}

      

This time, instead of passing values ​​to x

and y

to, swap

we pass addresses x

and y

. The variables a

and b

are pointers to x

and, y

respectively, so writing to an expression is the *a

same as writing to x

(similarly, writing to an expression *b

is the same as writing to y

).

An abbreviated way to describe it

a == &x  --> *a == x
b == &y  --> *b == y

      

When you call scanf

with an argument arre[i]

, you are passing the value of that array element to the function. Unfortunately, it scanf

wants the address of this element so that it can write a new value to it. scanf

tried to interpret the value you sent as the address of an object in memory hence segfault.

This is why you need to use an operator &

in an expression arre[i]

when you pass it as an argument scanf

.

Note that you do not need to use the operator &

if the argument is already a pointer type. Remember that when you read strings using a conversion specifier %s

, you usually pass an array argument, for example:

char input[81];
scanf( "%s", input );

      

In this case, the argument is input

implicitly converted from an array from char

expression to a pointer to char

expression, and the expression value is the address of the first element in the array. In most cases, an array expression will "decay" into a pointer expression. As a beginner, this bites you several times.

+1


source


The assertion scanf("%d", arre[i]);

will bit-cast arre [i] into the address. This address is not allocated, therefore it is the so-called "wild pointer". Accessing an address that doesn't belong to you is undefined behavior. If it is a system-protected address, the system will kill the process and give a segmentation fault.

I am curious to know when and why pointers are needed.

C functions are always called by value , which means that the parameters in the calling function are a copy of the arguments in the caller. Changing parameters will not affect the arguments. A common example is

void swap(int a, int b)
{
    int tmp = a;
    a = b;
    b = tmp;
}
int main(void)
{
     int x = 1, y = 10;
     swap(x, y);
     printf("x = %d, y = %d\n", x, y); // still origin value
     return 0;
}

      

One of the reasons for using pointers is to change the arguments. You can declare (or define) your function like

void swap(int* a, int *b);

      

and name

swap(&x, &y);

      

Another example of using pointers is a large structure parameter that takes too much time and space to copy. Then its pointer is used. Sometimes a qualifier is used to protect it from modification (but not guaranteed) const

.

Compare the following foo

and goo

.

struct Big { char dummy[1024]};
void foo(struct Big b);
void goo(const struct Big* b);

      

+1


source







All Articles