#include<iostream.h>
#include<conio.h>
/*

*****
Please do not read the following examples if you don't know what pointers are at all. These examples
are written supposing the reader has got basic understaning of pointers concept, trying to make
the reader understand the deep concepts easier.
*****

- every variable or array or struct which has a name when defined, needs 
    a place to store its address(which is sometimes called symbol table).
	But this place is managed by the compiler at compile time ,
	and is freed up when compile is finished. This is beacause during compile,
	all occurences of that name is replaced with the corresponding memory address.
	And that's why we do not have access to that place.

- Pointer Size in Most Dos C/C++ Compilers = 2
- Pointer Size in Most Windows C/C++ Compilers = 4

- sizeof(int)= 2 in Most Dos C/C++ Compilers ( like Turbo C++ 3.0 )
			2 in Some Windows C/C++ Compilers ( like Turbo C++ 4.5 )
			4 in Other Windows C/C++ Compilers ( like Visual C++ )

- Here we suppose that the Pointer Size is 4 Bytes and the int Size is also 4 Bytes.


- When you define 'int a[3][4][5][6]' the type of 'a' will be:
	(int[6][5][4]*) in DOS C/C++ 
	(int(*)[4][5][6]) in Windows/Visual C++
	(int[3][4][5][6]) in Visual C++ .Net

- sizeof(int*)==sizeof(char*)==sizeof(float*)==sizeof(int**)==sizeof(char****)
			==sizeof(int(*)[4])==sizeof(int(*)[4][5][6])== Pointer Size

- if you define an array then the size of that array will be equal to the
	number of bytes that array will need in memory
	int a[4][5] -> sizeof(a)==sizeof(int[4][5])==80

- 'lvalue' means any C/C++ expression which can be put on the left side of an
	assignment statement. This means that 'lvalue' is something whose value
	can be changed.
	So for example if we have ++(X) then (X) should also be an 'lvalue'.

- An expression in C is a part of you program which has got a total value. So a (if a is an int variable)
	, a*2, a*pow(2,a) or *a[5] all are expressions. Each expressions (E) in C has got some specifications:
		1-E has got a type such as (int) or (float*)
		2-E has got a size which is almost always defined by its type
		3-E has got a value such as 20, 3.45, etc.
		4-E may be an 'lvalue' or may NOT be an 'lvalue' which then we call 'rvalue'
	
- Note: the simbol <-> shows the equality of its left and right statements in all cases
	A <-> B  : A and B are always the same
- Note: the simbol <-X-> shows the unequality of its left and right statements in all cases
	A <-X-> B : A and B are not always the same. They are different at least in some cases. 


- a[i] <-> *(a+i)    ( i is an integer number)
- a[0] <-> *a
- a[]  <-> *a (only in function arguments definition)
- a[i][j] <-> (*(a+i))[j] <-> *((*(a+i))+j)
- a[i][j] <-X-> *(a+i)[j] <-> **(a+i+j)     (this is beacause [j] acts before *)
- a[i][j][k] <-> *(**a+(i*sizeof(*a)+j*sizeof(**a)+k*sizeof(***a))/sizeof(***a))

- When you add an integer to a pointer (a+i) the address which pointer points
    to ( the pointer value in memory ) is increased by i*sizeof(*a).
    For example if a is (int *) which means it points to an integer then
    (a+i) means i*4 bytes after the place (a) points to.The result is the i'th
    integer after (a).
- You can only add an integer type value to a pointer type value. So adding
  one pointer to another one is not legal even if they are of the same type.
  But adding an integer(char,int,unsigned long int,...) to any kind of pointer
  (like (int **) or (char(*)p[4]) ) is allowed. The result is allways of the 
  same type as the pointer. Most of other types of mathematical and logical 
  operands such as *,/,&,... are not applicable to a pointer.
- Subtraction of two pointers of the same type is also legal. For example
  if we have the definition (int *a,**p) then (a-*p) is a valid expression.
  The result would be and (int) value showing the difference between the
  place a points to and the place *p points to int the units of (*a). 
  This means that *p+(a-*p) is an (int *) value pointing to the same place 
  that (a) points to. But trying to use (*p+a)-*p leads to a compile error, 
  because adding two pointers is not legal.

- int a ->
		a is (int)
		a is 'lvalue'
		*a is NOT legal
		&a is NOT 'lvalue'
		sizeof(a)==4
- int *a -> 
		a is (int *)
		*a is (int)
		a[i] is (int)
		a is 'lvalue'
		*a is 'lvalue'
		&a is NOT 'lvalue'
		sizeof(a)==4
- int **a -> 
		a is (int **)
		*a is (int *)
		**a is (int)
		a[i] is (int*)
		a[i][j] is (int)
		sizeof(a)==4   (a is only a pointer)
		sizeof(*a)==4  ( *a is also a simple pointer)
		sizeof(**a)==4 ( **a is an integer)
		a[i][j] <-X-> *(*a+i+j)
		*a[i] is (int)
		*a[i] <-> **(a+i) <-X-> *(*a+i)
		*a[i] <-X-> **a+i
		(*a)[i] <-X-> *a[i]
- int a[4] -> 
		  a is (int *)
		  *a is (int)
		  a[i] is (int)
		  &a is (int(*)[4])
		  a is NOT 'lvalue'
		  *a is 'lvalue'
		  a[i] is 'lvalue'
- int a[5][6] ->
		a is (int(*)[6])
		*a[i] is (int)
		(*a)[i] is (int)
		*(*a+i) is (int)
		**a+i is (int)
		*a[i] is the integer i*6*4 bytes after **a
		(*a)[i] is the integer i*4 bytes after **a
		*(*a+i) is the integer i*4 bytes after **a
		**a+i is the value of **a plus i
		*a[i] is 'lvalue'
		(*a)[i] is 'lvalue'
		*(*a+i) is 'lvalue'
		**a+i is NOT 'lvalue'
- int a[4][5][6] -> 
			&a is NOT 'lvalue' & it is (int(*)[4][5][6])
			a is NOT 'lvalue' & it is (int(*)[5][6])
			*a is NOT 'lvalue' & it is (int(*)[6])
			**a is NOT 'lvalue' & it is (int *)
			***a is 'lvalue' & it is (int)
			the value of all above pointers are equal to the address of the first byte of the array in the 
				memory except (***a) which is not a pointer and is the value of the first integer of the array
				(int)(&a)==(int)(a)==(int)(*a)==(int)(**a)!=(int)(***a)
			a[i] is NOT 'lvalue' & it is (int((*)[6]))
			a[i][j] is NOT 'lvalue' & it is (int *)
			a[i][j][k] is 'lvalue' & it is (int)
			*a[i] is NOT 'lvalue' & it is (int *)
			*a[i][j] is 'lvalue' & it is (int)
			**a[i] is 'lvalue' & it is (int)
			sizeof(a)==480 (in Visual C++)
			sizeof(*a)==sizeof(a[i])==120
			sizeof(**a)==sizeof(a[i][j])==24
			sizeof(***a)==sizeof(a[i][j][k])==4
			sizeof(&a)==480
- int *a[4] -> a is (int * *)  ( a is an array of 4 int pointers )
			*a is (int *)
			**a is (int)
			sizeof(a)==16 ( 4 pointers each one 4 bytes )
			sizeof(*a)==sizeof(a[i])==4 (a pointer)
			sizeof(**a)==4 (an int)
			a is NOT 'lvalue'  ( note that a is an array )
			*a is 'lvalue' ( *a is the first pointer of the array )
			**a is 'lvalue' ( **a is the place which *a points to )
- int (*a)[4] -> a is (int(*)[4]) ( a is a pointer to an array which has 4 int items.
				But note that a is still only one pointer, not an array)
			*a is (int *)
			**a is (int)
			sizeof(a)==4 (it is only a pointer)
			sizeof(*a)==16 ( 4 int items in the array )
			sizeof(**a)== (a simple int)
			a is 'lvalue'
			*a is 'lvalue'
			**a is 'lvalue'
			a=a+i --> a is increased i*sizeof(*a)==i*16 bytes

- int a[][4] -> 
			a is (int(*)(4)) ( like above but only in function arguments )
			all specifications are the same as above(int (*a)[4])
- int a[3][4] -> 
			in function arguments this definition is exactly like a[][4]
			so sizeof(a)=4 which is size of a pointer
- in function argument definition:
			(*a)[4] <-> a[][4] <-> a[1][4] <-> a[1000][4]

- if X is a pointer (any type of pointer) then
	X <-> &*X	except for that (&*X) is NOT 'lvalue' but X may be 
	example: X==(a) when we have define (a) as 'int *a'
- if X is any 'lvalue' such as an (int) or (int *) variable then 
	X <-> *&X
- &X for any X is allways an 'rvalue' and this value is of some kind of pointer type


- As is mentioned above, in Visual C++ .NET the type of array names is different from older versions.
	It is worth to mention some examples of how it works. In Visual C++ .NET we have:
	- int a[4][5][6] -> 
				&a is NOT 'lvalue' & it is (int(*)[4][5][6])
				a is NOT 'lvalue' & it is (int[4][5][6])
				*a is NOT 'lvalue' & it is (int[5][6])
				**a is NOT 'lvalue' & it is (int[6])
				***a is 'lvalue' & it is (int)
				the value of all above pointers are equal to the address of the first byte of the array in the 
					memory except (***a) which is not a pointer and is the value of the first integer of the array
					(int)(&a)==(int)(a)==(int)(*a)==(int)(**a)!=(int)(***a)
				a[i] is NOT 'lvalue' & it is (int([5][6]))
				a[i][j] is NOT 'lvalue' & it is (int[6])
				a[i][j][k] is 'lvalue' & it is (int)
				*a[i] is NOT 'lvalue' & it is (int [6])
				*a[i][j] is 'lvalue' & it is (int)
				**a[i] is 'lvalue' & it is (int)
				sizeof(a)==480 (in Visual C++)
				sizeof(*a)==sizeof(a[i])==120
				sizeof(**a)==sizeof(a[i][j])==24
				sizeof(***a)==sizeof(a[i][j][k])==4
				sizeof(&a)==480
	- a value of type (int[6]) can be assinged to an 'lvalue' of type (int *). But the opposite side
		is not possible as no 'lvalue' can be of type (int[6])

REFERENCES:


- & operator is used for refrencing purposes. You can use it in your
    definitions, function argument definitions and inside statements.
- example:  
		int i,*p;
		int &ir=i,*&pr=p;
	The above example makes ir as a refrence to i. This means that ir is
	exactly the same as i after this definition. So any changes to ir will
	directly change i as they are two names for one thing(object).
	Also p and pr are exactly the same.
- example: 
		int func(int &ir)
	In this example the (ir) will be a refrence to actual parameter
	(the parameter you use when you call this function ) used in calling
	this function. This means that if you for example call the function as
	func(a) in main() then during this function (ir) will be exactly the same
	as (a) in main(). So any change to (ir) in this function will result in
	immediate change in the value of (a). Also &ir will be the same as &a as
	we know a and ir are two different names for one object.
- &(X);    ( during your program body )
	(X) can be anything which has got an address. Most of 'lvalue's have
	addresses in memory and so can be used in the place of (X). Just to be
	more exact functions also have addresses and function names can be used in
	the place of (X).




CHAR AND CHARACTER 

- char is different from character
  char -> a type in C++ which is a one byte integer between -128..127
  character -> a letter, digit or a simbol existing in the ascii table
- when we want to store a character in memory we store its ASCII code
  in memory and as each ASCII code is in range [0..255] (Extended ASCII)
  we need only one byte per character in memory. So we usually use the char
  type variables to store characters and that's why it is named so.
- What is the difference between (char) and (unsigned char)?
  (char) is an integer in range [-128..127]
  (unsigned char) is an integer in range [0..255]
  this difference is important when we have to interpret the value of a (char) or an (unsighed char)
  as an integer like in mathematical operations. The interesting point is that while we are working 
  only with (char) and (unsigned char) and only using these operations: (=,+,-) which are assignment,
  Addition and subtraction there is no difference between char and unsigned char. This is why we can
  simply use (char) instead of (unsigned char) when we want to work with characters. 
  It is interesting to know that printf("%c",X); expect an int value in the place of X. It means you
  can use any int value or variable instead of X. Even when you use a (char) value in the place of X
  it is casted to int first. The printf function the looks only at the first(lowest) byte of the int
  value as the ASCII code and prints the coresponding character to the output.
  It is some times recommended to use (int) instead of (char) even when you mean to work with characters.


*/

// MyStruct size= 4+8+40*1+4+10*4 = 96;
struct MyStruct
{
 int i;
 double f;
 char line[40];
 char *s;
 int ar[10];
};

MyStruct func(int a,int p[],char s[],char (*lines)[30],
		  int ar1[],int ar2[1001][5],int ar3[][5][6])
{
 MyStruct M;
 M.i=5;
 M.ar[5]=4;
 M.s=M.line;
 return(M);
}

main()
{
 MyStruct M1;
 char ch,s[30],*sp,**spp,lines[10][30],*dlines[10],(*dlines1)[10];
 int a,*p,**pp=&p,ar1[4],ar2[4][5],ar3[4][5][6];
 int *&i=p;
 a=p-*pp;
 M1=func(a,p,s,lines,ar1,ar2,ar3);
 return 0;
}



