#include <iostream>
using namespace std;

/*
任何变量都需要指定数据类型,因为变量都要存在内存里面,那就牵涉到怎么存的问题,数据类型就规定了变量怎么存。
所以变量的定义形式是:数据类型   变量名
对于指针变量,它的数据类型是固定的,因为指针变量装的是一个地址,如果是32位系统,一个地址需要四个字节装,并且地址一定是无符号整形,所以指针变量本身的数据类
型是确定的,不需要专门指定,但是指针变量指向的内容的数据类型是多种多样的。所以例如  int  *p     修饰指针变量的int不是说p是int类型,p应该是unsigned int 型,
而是说p指向的内容是int型。


1、指针是个变量,它本身需要一个位置存放,存放它的位置的地址的标号就是这个指针变量,这个变量里面的内容是某个变量的地址。
2、变量修饰符的优先级和结合方向:下面的例子中,结合顺序都是以变量为核心从右到左边,例如int  *p; int*  p;p都是首先和*结合,表明p是个指针变量。
3、指针的定义: 基本类型  *指针变量名;   int   *a;//基本类型是说a指向的内存空间所装的数据是int型
4、常量指针:是个指针,即是个变量,但是该指针指向的内容是个常量;const int *i;从结合方向上去认识,i表明是个变量,*i表明i是指针变量,
   int *i表明指针指向的内容是整形,const int *i表明i还是个常量。

5、指针常量:是一个被const修饰的指针变量, 这个变量被放入只读区。
   声明指针常量的方法:先声明一个指针   int* p
   int* const i;从结合方向上看,i表明是个变量,但是const i把这个变量限制成了一个常量,然后这个常量又被int*修饰,进一步把这个常量变成了
   不可变动的地址。
   看一下指针常量是如何从产量演变过来的
   定义一个常变量: const int a = 11;
   定义一个指针变量常变量   const  int *p

6、指针函数:返回值是指针的函数,任何函数都有返回值,这个值可以值普通变量也可以是指针变量。
   int* Fn(int);
7、函数指针:函数指针变量的简称
   如果在程序中定义了一个函数,编译后,这个函数被操作系统分配了一段存储空间,这段存储空间的首地址就是这个函数的地址。而且函数名字表示的就是这个地址。
   既然是地址,那么我们就可以定义一个指针变量来存放,这个指针变量就叫做函数的指针变量,简称函数指针。
   函数指针和普通指针的定义不一样,有他的特殊之处。
   int Fn;定义一个普通的变量
   int *Fn;把上面的普通变量改写成普通指针变量
   int  (*Fn)(int)    把上面的普通指针变量改写成函数指针变量
   从优先级和结合顺序来看,由于有括号,所以首先要看(*Fn),表明Fn是个指针变量,然后看(*Fn)(int) ,表明这个指针指向函数的首地址
*/

//1、指针是个变量,它本身需要一个位置存放,存放它的位置的地址的标号就是这个指针变量,这个变量里面的内容是某个变量的地址。
void main01()
{
    int b = 10;
    int *p = NULL;
    int **pp =NULL;//定义一个二级指针,用来存放p的地址

    //验证指针变量的内容是变量b的地址。
    p = &b;  //取b的地址赋值给p;
    cout<<&b<<endl;//打印变量b的地址
    cout<<p<<endl; //打印变量p的内容,p的内容应该就是b的地址    
    
    //验证变量p也是需要一个存储空间存储变量
    pp = &p; //取指针p的地址赋值给二级指针pp
    cout<<&p<<endl; //打印指针p的地址
    cout<<pp<<endl; //打印变量pp的内容,pp的内容应该就是p的地址    
    cout<<"hello..."<<endl;
    system("pause");
    return;
}

//4、常量指针:是个指针,即是个变量,但是该指针指向的内容是个常量;const int *i;从结合方向上去认识,i表明是个变量,*i表明i是指针变量,
//int *i表明指针指向的内容是整形,const int *i表明i还是个常量。
void main11()
{
    int a = 0;
    int b = 20;

    //下面这两种定义常量指针的方法都合法。
    const int *p0;    //常量指针,是个变量,*是修饰p的,而const和 int都是修饰p所指向的内容的
    int const *p1;

    p0 = &a;
    p1 = &b;
    cout<<"p0指向的内容"<<*p0<<endl;
    cout<<"p1指向的内容"<<*p1<<endl;

    cout<<"hello..."<<endl;
    system("pause");
    return;
}

/*
5、指针常量:是一个被const修饰的指针变量, 这个变量被放入只读区。
   声明指针常量的方法:先声明一个指针   int* p
   int* const i;从结合方向上看,i表明是个变量,但是const i把这个变量限制成了一个常量,然后这个常量又被int*修饰,进一步把这个常量变成了
   不可变动的地址。
   看一下指针常量是如何从产量演变过来的
   定义一个常变量: const int a = 11;
   定义一个指针变量常变量   const  int *p
*/

void main21()
{
    int a = 0;
    //定义一个指针常量,初始化之后,p的值就不可变了。p指向的内容是可变的。
    int*    p = &a;     

    //p = &b;//如果这样写会报错。
    //a的地址 =  p的值
    cout<<"a的地址"<<&a<<endl; 
    cout<<"p的值"<<p<<endl;     
    cout<<"p的地址"<<&p<<endl;

    cout<<"hello..."<<endl;
    system("pause");
    return;
}

/*
6、指针函数:返回值为指针的函数  例如int* Fn(int);
*/
int* Fn(int val)
{
    val=val+1;
    return &val;    //返回值必须是个指针,如下写法会报错:return val;
}
void main()
{
    int a = 11;
    int b = 0;
    
    b= *Fn(a);//调用指针函数,该函数返回的是个指针,所以要想接出指针指向的地址空间里面的内容,就要用*取出来,用b接住。
    cout<<b<<endl;

    cout<<"hello..."<<endl;
    system("pause");
    return;
}

/*
7、函数指针:函数指针变量的简称
   如果在程序中定义了一个函数,编译后,这个函数被操作系统分配了一段存储空间,这段存储空间的首地址就是这个函数的地址。而且函数名字表示的就是这个地址。
   既然是地址,那么我们就可以定义一个指针变量来存放,这个指针变量就叫做函数的指针变量,简称函数指针。
   函数指针和普通指针的定义不一样,有他的特殊之处。
   int Fn;定义一个普通的变量
   int *Fn;把上面的普通变量改写成普通指针变量
   int  (*Fn)(int)    把上面的普通指针变量改写成函数指针变量
   从优先级和结合顺序来看,由于有括号,所以首先要看(*Fn),表明Fn是个指针变量,然后看(*Fn)(int) ,表明这个指针指向函数的首地址
*/

int Add1(int val)
{
    val=val+1;
    return val;
}
void main7()
{
    int a = 0;
    int (*p)(int val);//定义一个函数指针,初始化
    
    p = Add1;//把函数的首地址赋给函数指针p

    //直接调用函数
    a = Add1(3);
    cout<<a<<endl;

    //通过函数指针调用函数
    a = p(4);
    cout<<a<<endl;


    cout<<"hello..."<<endl;
    system("pause");
    return;
}