loading...
Cover image for Pointer in C/C++

Pointer in C/C++

mh_shifat profile image 5hfT Updated on ・7 min read

Pointer in C/C++

Let's consider a int type variable var.

int var = 10;

So if we print var it will be like :

printf("%d",var);

Output : 10
There is a location in our system memory where every variables are stored when we declare them.
So where is the variable var ?
To print the momory address or location of a variable we have to use unary & operator

print("The memory address of a is %p: ",&a); //%p is used to print a pointer

Output :The memory address of a is : 0x7fffbabdb6f0
The memory address can be different as it takes a random memory address everytime you build your program, but looks like same(0x..........).
Let's declare a pointer :

int *pa; // or int * pa

Note : If you are pointing the pointer at a data type you have to declare it at the declaration of the pointer
Now point Pointer pa at var;

pa = &var;

Now the pointer is pointed at the var variable and it takes the location or memory address of var.
Let's print pa :

printf("%p",pa);

Output : 0x7fffbabdb6f0
Which is same as the memory address of var variable.
We can also print the value of a memory address using unary * operator.This is also called dereferencing

printf("%d",*pa);

Output : 10
Now we can say a Pointer is an object in many programming languages that stores a memory address.
Let's see what happens if we change the value of var.

var = 10001;
printf("Value of var : %d\n",var);
printf("Value of memory  address %p is : %d",pa,*pa);

Output :

Value of var : 10001
Value of Memory address 0x7fffbabdb6f0 is : 10001

We can see changing the value of var changes the value of pa memory address.
What if we increase the value of the Pointer pa ? Let's see:

pa++;
printf("Pointer (pa) is pointed to %p and value becomes : %d\n", pa, *pa);

Output :

Pointer (pa) is pointed to 0x7fffbabdb6f8 and value becomes : -1161971980

Here -1161971980 is a gurbage value as nothing is initialized in memory address 0x7fffbabdb6f8

Pointer Expressions and Pointer Arithmetic:

A limited set of arithmetic operations can be performed on pointers. A pointer may be:

  • incremented ( ++ )
  • decremented ( -- )
  • an integer may be added to a pointer ( + or += )
  • an integer may be subtracted from a pointer ( – or -= )

Pointer arithmetic is meaningless unless performed on an array.
Note : Pointers contain addresses. Adding two addresses makes no sense, because there is no idea what it would point to. Subtracting two addresses lets you compute the offset between these two addresses.

Pointer in Arrays

Declare an array:

int ar[] = {1, 2, 3, 4, 5};

Declare pointer variable:

int *ptr;

Assign the address of v[0] to ptr

ptr = ar; //We can use ptr=&ar[0];(both are same)

Let's print array elements with thieir memory address :

for (int i = 0; i < 5; i++)
{
    printf("Value of *ptr = %d\n", *ptr);
    printf("Value of ptr = %p\n", ptr);

    // Increment pointer ptr by 1
    ptr++;
}

Output :

Value of *ptr = 1
Value of ptr = 0x7fffe4378a30
Value of *ptr = 2
Value of ptr = 0x7fffe4378a34
Value of *ptr = 3
Value of ptr = 0x7fffe4378a38
Value of *ptr = 4
Value of ptr = 0x7fffe4378a3c
Value of *ptr = 5
Value of ptr = 0x7fffe4378a40

We can also use :

for (int i = 0; i < 5; i++)
{
    cout<<ptr[i]<<" ";
}
cout<<endl;

Output : 1 2 3 4 5
As an array name acts like a pointer constant. The value of this pointer constant is the address of the first element.

Pointer to pointer
Have you even think that if a variable has a memory address in the memory then what about the pointer we are using to point on the variable?
Yes,a pointer also has a memory address.

int sum =10;
int * psum = &a;
printf("Memory address of variable(sum) : %p\n",psum);
printf("Memory Address of pointer(psum) :  %p\n",&psum);

Output :

Memory address of variable(sum) : 0x7ffc3b46638c
Memory Address of pointer(psum) :  0x7ffc3b466398

Let's declare a pointer for a int pointer :3

int **ppsum = &psum; //as ppsum is a pointer of a int type variable's pointer

print the memory address of the pointer :

printf("Memory Address of pointer(psum) :  %p\n",ppsum);

Output :

Memory Address of pointer(psum) :  0x7ffc712e6780

Pointer in Structure

Structures are user defined data types.

Let' see a structure

struct Person
{            //structure
    int age; //members
    float salary;
};

In the Person structure there are two member int type and float type.Person is also a data type here.

So the Person data type also invoke memory and has a memory address.

struct Person p1;

printf("%p",&p1);

Output:

0x7ffebf9ffab0

Let's declare a pointer and point to the structure.

struct Person *p;
p=&p1;
printf("%p",p);

Output:

0x7ffebf9ffab0

We can also use pointer to change values:

p->age = 25; //or *(p).age = 25
p->salary = 2121324.50

Pass by Value

void passByValue(int x){
    x = 20;
    print("In passByValue function, x = %d\n",x);
}

int main(){
    int x = 10;
    passByValue(x);
    print("In main Function, x = : %d\n",x);
    return 0;
}

Output :

In passByValue function, x = 20
In main Function, x = : 10

In this case, x in passByValue() function and x in main() function are not same, just the variable names are same but they indicate two different memory locations.
Let's go deeper :3

void passByValue(int x){
    cout<<"In passByValue Function : "<<endl;
    x = 20;
    printf("x = %d\n",x);
    cout<<"Memory address of x = "<<&x<<endl;
}

int main(){
    int x = 10;
    passByValue(x);
    cout<<"In main Function : "<<endl;
    printf("x = %d\n",x);
    cout<<"Memory address of x = "<<&x<<endl;
    return 0;
}

Output :

In passByValue Function :
x = 20
Memory address of x = 0x7ffc6c07397c
In main Function :
x = 10
Memory address of x = 0x7ffc6c0739a0

So looking at the outputs we can find that the memory addresses are not same ! The two x are not same.

Pass by Reference

void passByReference(int *px){
    *px = 100;
    printf("In passByReference() function, Memory address =  %p\n",px);
}

int main(){
    int x = 10;
    cout<<"(Before) Value of x : "<<x<<endl;
    passByReference(&x);
    cout<<"(After) Value of x : "<<x<<endl;
    cout<<"Memory address of x = "<<&x<<endl;
    return 0;
}

Output :

(Before) Value of x : 10
Memory address =  0x7ffe2bd44670
(After) Value of x : 100
Memory address of x = 0x7ffe2bd44670

In pass by reference we are sending the memory address of x and then in the function we are chnaging the value which is stored in the memory address.
So chaging value of memory address will also chnage the value of a as it is stored in the memory address.

Dangling Pointers

int* getValue(){
    int x;
    x=2;
    return &x;
}

int main(){
    int *px = getValue();
    print("Value in memory address(px) = %d\n",*px);
}

Output : Value in memory address(px) = 2

In this case getValue() returns the memory address where x were stored. After returning memory address x vanishes but the value in that address remains and after that if we take a variable like

int var3 = 120

there is a huge possibility that var3 can be stored in that memory address which can make problem for you and that is called Dangling Pointer

Dangling pointers arise during object destruction, when an object that has an incoming reference is deleted or deallocated, without modifying the value of the pointer, so that the pointer still points to the memory location of the deallocated memory.

So hare comes the term Dynamic Memory Allocation

Dynamic Memory Allocation

Sections in Memory management:

  1. Stack
  2. Heap
  3. BSS
  4. Data
  5. Code

C Dynamic Memory Allocation can be defined as a procedure in which the size of a data structure (like Array) is changed during the runtime.

There are 4 library functions provided by C defined under header file to facilitate dynamic memory allocation in C programming. They are:

  • malloc()
  • calloc()
  • free()
  • realloc()

C malloc() method

“malloc” or “memory allocation” method in C is used to dynamically allocate a single large block of memory with the specified size. It returns a pointer of type void which can be cast into a pointer of any form. It initializes each block with default garbage value.

Syntax:
ptr = (cast-type*) malloc(byte-size)

For Example:

int* ptr = (int*) malloc(100 * sizeof(int));

Since the size of int is 4 bytes, this statement will allocate 400 bytes of memory. And, the pointer ptr holds the address of the first byte in the allocated memory.

C free() method

“free” method in C is used to dynamically de-allocate the memory. The memory allocated using functions malloc() and calloc() is not de-allocated on their own. Hence the free() method is used, whenever the dynamic memory allocation takes place. It helps to reduce wastage of memory by freeing it.

free(ptr);

Note:When we declare a pointer it pointed to a random memory address of the memory so better practice is to set them null first

int* px = NULL;

C calloc() method

“calloc” or “contiguous allocation” method in C is used to dynamically allocate the specified number of blocks of memory of the specified type. It initializes each block with a default value ‘0’.

Syntax:

ptr = (cast-type*)calloc(n, element-size);

For Example:

ptr = (float*) calloc(25, sizeof(float));

This statement allocates contiguous space in memory for 25 elements each with the size of the float.

Note:alloc() function allocate n bytes in memory where calloc() function allocate x blocks of y bytes in memory

C realloc() method

“realloc” or “re-allocation” method in C is used to dynamically change the memory allocation of a previously allocated memory.

Syntax:

ptr = realloc(ptr, newSize);

where ptr is reallocated with new size 'newSize'.

int* p = (int*)malloc(n*sizeof(int));
p = realloc(p,n*sizeof(int));

for more Dynamic Memory Allocation and Fragmentation in C and C++

Posted on by:

mh_shifat profile

5hfT

@mh_shifat

I'm a Quick Learner | Love to Write Code | Learn new Tech stuffs | Find Peace in Solving or Fixing an Error. ~Every code matters !~

Discussion

markdown guide
 

Do you remember that a int data type takes 2 or 4 bytes Storage in memory ?

This is incorrect.

sizeof (int) is >= 1.

It is not required to be 2 or 4.

(Note that byte in C/C++ is defined to be char, and char may have more than 8 bits).

WTF??
We can say pa is now pointed to x variable or pointed to the memory address 0x7fffbabdb6f4 which is same as the pointer px
And the is something more to notice that the memory location of var was 0x7fffbabdb6f0 and memory location of x was 0x7fffbabdb6f4.
Look closely find something amazing ? The last two digits f0 and f4 ! coincidence ?? Noh!
Do you remember that a int data type takes 2 or 4 bytes Storage in memory ?
Yeah you get it right! As var was taking memory address from 0x7fffbabdb6f0 to 0x7fffbabdb6f3, using ++ operator in pointer it jummped to the next memory address and it was 0x7fffbabdb6f4 and as there was another variable in our code , it was the memory location for x variable whichpointer pa pointed.

Unfortunately your understanding of what has happened here is incorrect, and the program only appears to work by accident.

C does not have a flat memory model, and you cannot derive a pointer from a pointer that does not point into or one past the end of the same array, and it is undefined behavior to dereference a pointer one past the end of array.

When you wrote pa++ you derived a pointer value from pa and assigned it back to pa, meaning that it is still indexing the same array, but is now pointing one past the end of the array.

Dereferencing pa at this point has undefined behavior.

By accident, on your implementation, this undefined behavior happened to access another variable, but that's purely accidental.

 

Good points, but I'd go even further.

When you wrote pa++ you derived a pointer value from pa and assigned it back to pa, meaning that it is still indexing the same array, but is now pointing one past the end of the array.

It doesn't really index an array, it doesn't know if it points to an array and which array. It just points to a memory address and interprets what is there as an integer - without even having any notion of the array the programmer wanted to point at.

This example starts to get funnier when you declare something right after this array. While that's unspecified where the variable will be stored, you can guess for the sake of the game.

int main() {


long ar[] = {1, 2, 3, 4, 5};
double d[]={4.2, 0.42};
long *ptr;
ptr = ar;
for (int i = 0; i < 7; i++)
{
    printf("Value of *ptr = %ld\n", *ptr);
    printf("Value of ptr = %p\n", ptr);

    // Increment pointer ptr by 1
    ptr++;
}

With gcc, it'll read in ds values and even though they are doubles, as the pointer is pointing at long values, it will interpret the doubles as longs. After all, in the memory they are just 0s and 1s... (Note that on clang, based on what I saw, d will have a lower memory address, so the loop will not find it)

 

I think you are inferring an incorrect model of how C works by observing how some popular implementations of C work.

Let's take a look at a freely available draft standard for C.

open-std.org/jtc1/sc22/wg14/www/do...

For the purposes of these operators, a pointer to an object that is not an element of an array behaves the same as a pointer to the first element of an array of length one with the type of the object as its element type.

and

If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined. If the result points one past the last element of the array object, it shall not be used as the operand of a unary * operator that is evaluated.

What this means is that:

  1. All C objects are effectively stored in arrays.
  2. You cannot legally get from one array to another array using pointer arithmetic.

What you're observing above is undefined behavior -- you may be able to get away with it with particular compilers and architectures, but it isn't how C works. :)

That's perfectly right, we are speaking about two different things. You say that getting from one array to another via pointer arithmetics is undefined behaviour. It is! In fact, that's pretty much I talked about at C++ On Sea earlier this week. Except that, I didn't speak about arrays, but STL containers and not about pointer arithmetics, but iterators.

What I tried to point at here and I see I was not clear enough, is that a pointer or an iterator doesn't know about the container it points at. It only knows the memory address and the type that it is looking for.

Let's take a practical example. We have two containers of the same type, a and b and we have a pointer to each pa and pb. Can you write a function taking the pointers and decide whether the pointers belong to the same array without having any more info available about the arrays?

#include <iostream>
#include <array>

bool f(int* pa, int* pb) {
   // TODO
   // return true of pa and pb point at the same array, false otherwise
   return true;
}

int main() {
    int a[] = {1,2,3};
    int b[] = {4,5,6};
    int* pa = a;
    int* pb = b;
    if (f(pa,pb)) {
        std::cout << "pa and pb belong to the same array" << std::endl;
    } else {
        std::cout << "pa and pb belong to the same array" << std::endl;
    }
}

As far as I understand, this is not possible, because the pointers they don't belong to a container. And this doesn't change the fact, that it's the callers responsibility to use the pointer in a sane way not trying to depend on UB. In fact, I find it quite disturbing that the article mentions C++ whereas there is no reason in 2020 to use c-style arrays and pointer arithmetics to iterate over an array...

Can you write a function taking the pointers and decide whether the pointers belong to the same array without having any more info available about the arrays?

You cannot introspectively determine this.

Just like you cannot introspectively determine that a pointer has a well defined value, or that a pointer points at allocated memory.

As far as I understand, this is not possible, because the pointers they don't belong to a container.

It is not possible because C and C++ do not provide introspective mechanisms to determine this.

However, they do assert that you cannot legitimately derive a pointer to one array from a pointer to another array.

(And as all objects are effectively stored in arrays in C and C++, this is a universal property and applies likewise to the substructure of STL containers, and so on.)

Which means that the association exists, regardless of if it is encoded into the program in a detectable fashion or not.

Just as memory being allocated or not may not be encoded into the program in a detectable fashion.

(For example, consider a compiler which has been able to statically determine all memory allocation your program will perform and so has hard coded every allocation at compile-time.)

The fact that you can't check it doesn't mean that it isn't significant. :)

 

Thanks for your response and explanation.

 

When i ran the same code in different online compilers I found the same result in case of increment of pointer.

 

While I usually use coliru for small sketches, if I want to try many compilers with different versions I go to wandbox.

 

That just means you haven't tried hard enough. :)

Tuming on asan in gcc will do the trick.

You could also try using cint, an interpreter.

Or ...