#include<stdio.h>

int main() {

  /* variables have rvalues and lvalues!    
   *
   *  "rvalue": value for right hand side of 
   *            side of an assignment       
   *  "lvalue": value for left hand side of 
   *            side of an assignemnt       
   *
   *  lvalue: memory address of the variable
   *  rvalue: contents of that address      
   */

  int x;
  x = 6;
  
  printf("lvalue of x:\t %d\n",&x);
  printf("rvalue of x:\t %d\n",x);
  
}




#include<stdio.h>

int main() {
  
  /*  rvalues and lvalues are of different types:    
   *    type of rvalue of x: T                     
   *    type of lvalue of x: *T                    
   *       (read: pointer to T) 
   */

  int  x;
  int* ptr;    /* variable to hold lvalue of x */
  
  ptr = &x;
  x = 6;

  printf("rvalue of x:\t %d\n",x);
  printf("lvalue of x:\t %d\n",&x);
  printf("rvalue of ptr:\t %d\n",ptr);  

  /* question: what's the type of lvalue of ptr? */

  printf("lvalue of ptr:\t %d\n",&ptr);  
}





#include<stdio.h>

int main() {
  
  /*  given an lvalue, we want to retrieve
   *  the corresponding rvalue.
   *  The process is called  dereferencing
   */

  int  x;
  int* ptr;
  
  ptr = &x;
  x = 6;

  /*  `*' operator returns rvalue corresponding
   *  to an lvalue, i.e. the contents of that 
   *  memory address ptr
   */

  printf("x:\t%d\n", x);
  printf("*ptr:\t%d\n", *ptr);
  printf("ptr:\t%d\n", ptr);

}





#include<stdio.h>

int main() {
  
  int  x;
  int* ptr;
  
  ptr = &x;
  
  /*  We've two ways of accessing the rvalue of x:
   *    via  x    or
   *    via  ptr
   */

  x = 6;
  (*ptr)++;

  printf("x:\t%d\n", x);
  printf("ptr:\t%d\n", *ptr);

}






#include<stdio.h>

int main() {
  
  int  x;
  int  y;
  int* ptr;
  
  /*  we can point the pointer to any
   *  memory location. That's potentially
   *  dangerous... why?
   */

  x = 6;
  y = 8;

  ptr = &x;
  printf("ptr:\t%d\n", *ptr);

  ptr = &y;
  printf("ptr:\t%d\n", *ptr);



}






#include<stdio.h>

void sum(int x, int y, int* sum) {

  /*  returns x + y in variable pointed to
   *  by sum. ``call by reference''
   */
  
  *sum = x + y;
}

int main() {
  int res = 0;
  sum(4,5,&res); 

  printf("res:\t%d\n",res);
  
}






#include<stdio.h>

int a;

int* ptrToA() {

  /*  returns the pointer to 
   *  global a as function value
   */

  return &a;
}

int main() {
  a = 6;
  
  printf("a:\t%d\n", a);
  printf("&a:\t%d\n",&a);
  printf("prtToA:\t%d\n", ptrToA());
}






#include<stdio.h>

int a;

void ptrToA(int** res) {

  /*  returns the pointer to
   *  a by reference
   */

  (*res) = &a;

}


int main() {
  int* ptr;

  a = 6;
  
  printf("a:\t%d\n", a);
  printf("&a:\t%d\n",&a);
  
  ptrToA(&ptr);

  printf("ptr:\t%d\n", ptr);
}






#include<stdio.h>

int a;

void valueOfA(int* res) {
  /* returns value of A by reference
   */
  
  (*res) = a;
}

int main() {
  int b = 0;
  printf("a:\t%d\n", a);
  printf("b:\t%d\n", b);
  valueOfA(&b);
  printf("b:\t%d\n", b);
}






#include<stdio.h>

int* ptrToB() {
  int b = 5;
  int* ptr;
  
  /* return a pointer to b
   */
  
  ptr = &b;

  printf("inside ptrToB:\n");
  printf("ptr:\t%d\n", ptr);
  printf("(*ptr):\t%d\n\n", *ptr);
  
  return ptr;
}


int main() {
  int* ptr;
    
  ptr = ptrToB();
  printf("inside main:\n");
  printf("ptr:\t%d\n", ptr);
  printf("(*ptr):\t%d\n", *ptr);
}






#include<stdio.h>
#include <stdlib.h>

/* So far, all we've used pointers only as references
 * to other variables (aliasing). However, we can have
 * pointers pointing to user allocated memory locations.
 */

int main() {
  int* ptr;  

  ptr = (int*) malloc(sizeof(int));
  (*ptr) = 10;
  printf("ptr:\t%d\n",ptr);
  printf("(*ptr):\t%d\n",*ptr);
}





#include<stdio.h>
#include <stdlib.h>


int* ptrToInt() {
  int* ptr;
  
  /* return a pointer to b
   */

  ptr = (int*) malloc(sizeof(int));
  (*ptr) = 5;
  
  printf("inside ptrToInt:\n");
  printf("ptr:\t%d\n", ptr);
  printf("(*ptr):\t%d\n\n", *ptr);
  
  return ptr;
}

int main() {
  int* ptr;  

  ptr = ptrToInt();
  printf("inside main:\n");
  printf("ptr:\t%d\n", ptr);
  printf("(*ptr):\t%d\n", *ptr);
}






#include<stdio.h>
#include<stdlib.h>

/* Pointers can point to various types... 
 */

int main() {
  int* ptrInt;
  float* ptrFloat;
  char* ptrChar;

  ptrInt = (int*) malloc(sizeof(int));
  ptrFloat = (float*) malloc(sizeof(float));
  ptrChar = (char*) malloc(sizeof(char));

  (*ptrInt) = 10;
  (*ptrFloat) = 12.3;
  (*ptrChar) = 'A';

  printf("ptrInt:\t%d\n",*ptrInt);
  printf("ptrFloat:\t%f\n",*ptrFloat);
  printf("ptrChar:\t%c\n",*ptrChar);
}





#include<stdio.h>
#include<stdlib.h>

/* Pointers can be used to implement data structures of
 * dynamic size
 */

int main() {
  int length = 10;

  char* A = (char*) calloc(length,sizeof(char));
  char* iter;
  int i;

  iter = A;
  for (i = 0; i < length; ++i) {
    (*iter) = 'A'+i;
    /* points iter to next memory location */
    ++iter;
  }

  /* use pointer notation */
  for (i = 0; i < length; ++i)
    printf("char %i:\t%c\n",i,*(A+i)); 

  printf("\n");

  /* use array notation */
  for (i = 0; i < length; ++i)
    printf("char %i:\t%c\n",i,A[i]); 

}