zoukankan      html  css  js  c++  java
  • 指针 数组指针 指针数组 函数指针等说明。

    指针的概念

    原文地址:http://www.cnblogs.com/ggjucheng/archive/2011/12/13/2286391.html

    指针是一个特殊的变量,它里面存储的数值被解释成为内存里的一个地址。要搞清一个指针需要搞清指针的四方面的内容:指针的类型,指针所指向的类型,指针的值或者叫指针所指向的内存区,还有指针本身所占据的内存区。让我们分别说明。 

    先声明几个指针放着做例子:  

    例一:  

    int *ptr;  
    char *ptr;
    int **ptr;
    int (*ptr)[3];
    int *(*ptr)[4];

    指针的类型

    从语法的角度看,你只要把指针声明语句里的指针名字去掉,剩下的部分就是这个指针的类型。这是指针本身所具有的类型。让我们看看例一中各个指针的类型:  

    int *ptr; //指针的类型是int *  
    char *ptr; //指针的类型是char *
    int **ptr; //指针的类型是 int **
    int (*ptr)[3]; //指针的类型是 int(*)[3]
    int *(*ptr)[4]; //指针的类型是 int *(*)[4]

    怎么样?找出指针的类型的方法是不是很简单?  

    指针所指向的类型

    当你通过指针来访问指针所指向的内存区时,指针所指向的类型决定了编译器将把那片内存区里的内容当做什么来看待。

    从语法上看,你只须把指针声明语句中的指针名字和名字左边的指针声明符*去掉,剩下的就是指针所指向的类型。例如:  

    int *ptr; //指针所指向的类型是int  
    char *ptr; //指针所指向的的类型是char
    int **ptr; //指针所指向的的类型是 int *
    int (*ptr)[3]; //指针所指向的的类型是 int()[3]
    int *(*ptr)[4]; //指针所指向的的类型是 int *()[4]

    在指针的算术运算中,指针所指向的类型有很大的作用。  

    指针的类型(即指针本身的类型)和指针所指向的类型是两个概念。当你对C越来越熟悉时,你会发现,把与指针搅和在一起的“类型”这个概念分成“指针的类型”和“指针所指向的类型”两个概念,是精通指针的关键点之一。我看了不少书,发现有些写得差的书中,就把指针的这两个概念搅在一起了,所以看起书来前后矛盾,越看越糊涂。

     指针的值


    指针的值是指针本身存储的数值,这个值将被编译器当作一个地址,而不是一个一般的数值。在32位程序里,所有类型的指针的值都是一个32位整数,因为32位程序里内存地址全都是32位长。 

    指针所指向的内存区就是从指针的值所代表的那个内存地址开始,长度为sizeof(指针所指向的类型)的一片内存区。以后,我们说一个指针的值是XX,就相当于说该指针指向了以XX为首地址的一片内存区域;我们说一个指针指向了某块内存区域,就相当于说该指针的值是这块内存区域的首地址。 

    指针所指向的内存区和指针所指向的类型是两个完全不同的概念。在例一中,指针所指向的类型已经有了,但由于指针还未初始化,所以它所指向的内存区是不存在的,或者说是无意义的。 

    以后,每遇到一个指针,都应该问问:这个指针的类型是什么?指针指向的类型是什么?该指针指向了哪里?  

    指针本身所占据的内存区

    指针本身占了多大的内存?你只要用函数sizeof(指针的类型)测一下就知道了。在32位平台里,指针本身占据了4个字节的长度。

    指针本身占据的内存这个概念在判断一个指针表达式是否是左值时很有用。  

    指针的算术运算

    指针可以加上或减去一个整数。指针的这种运算的意义和通常的数值的加减运算的意义是不一样的。例如:  
    例二:  

     char a[20];  
    int *ptr=a;
    ...
    ...
    ptr++;


    在上例中,指针ptr的类型是int*,它指向的类型是int,它被初始化为指向整形变量a。接下来的第3句中,指针ptr被加了1,编译器是这样处理的:它把指针ptr的值加上了sizeof(int),在32位程序中,是被加上了4。由于地址是用字节做单位的,故ptr所指向的地址由原来的变量a的地址向高地址方向增加了4个字节。
    由于char类型的长度是一个字节,所以,原来ptr是指向数组a的第0号单元开始的四个字节,此时指向了数组a中从第4号单元开始的四个字节。
    我们可以用一个指针和一个循环来遍历一个数组,看例子:  
    例三:  

    复制代码
    int array[20];  
    int *ptr=array;
    ...
    //此处略去为整型数组赋值的代码。
    ...
    for(i=0;i<20;i++)
    {
    (*ptr)++;
    ptr++;
    }
    复制代码


    这个例子将整型数组中各个单元的值加1。由于每次循环都将指针ptr加1,所以每次循环都能访问数组的下一个单元。再看例子:  

    例四:  

    char a[20];  
    int *ptr = a;
    ...
    ...
    ptr += 5;


    在这个例子中,ptr被加上了5,编译器是这样处理的:将指针ptr的值加上5乘sizeof(int),在32位程序中就是加上了5乘4=20。由于地址的单位是字节,故现在的ptr所指向的地址比起加5后的ptr所指向的地址来说,向高地址方向移动了20个字节。在这个例子中,没加5前的ptr指向数组a的第0号单元开始的四个字节,加5后,ptr已经指向了数组a的合法范围之外了。虽然这种情况在应用上会出问题,但在语法上却是可以的。这也体现出了指针的灵活性。 

    如果上例中,ptr是被减去5,那么处理过程大同小异,只不过ptr的值是被减去5乘sizeof(int),新的ptr指向的地址将比原来的ptr所指向的地址向低地址方向移动了20个字节。 

    总结一下,一个指针ptrold加上一个整数n后,结果是一个新的指针ptrnew,ptrnew的类型和ptrold的类型相同,ptrnew所指向的类型和ptrold所指向的类型也相同。ptrnew的值将比ptrold的值增加了n乘sizeof(ptrold所指向的类型)个字节。就是说,ptrnew所指向的内存区将比ptrold所指向的内存区向高地址方向移动了n乘sizeof(ptrold所指向的类型)个字节。一个指针ptrold减去一个整数n后,结果是一个新的指针ptrnew,ptrnew的类型和ptrold的类型相同,ptrnew所指向的类型和ptrold所指向的类型也相同。ptrnew的值将比ptrold的值减少了n乘sizeof(ptrold所指向的类型)个字节,就是说,ptrnew所指向的内存区将比ptrold所指向的内存区向低地址方向移动了n乘sizeof(ptrold所指向的类型)个字节。

    运算符&和*

    这里&是取地址运算符,*是...书上叫做“间接运算符”。&a的运算结果是一个指针,指针的类型是a的类型加个*,指针所指向的类型是a的类型,指针所指向的地址嘛,那就是a的地址。*p的运算结果就五花八门了。总之*p的结果是p所指向的东西,这个东西有这些特点:它的类型是p指向的类型,它所占用的地址是p所指向的地址。

    例五:  

    复制代码
    int a=12;  
    int b;
    int *p;
    int **ptr;
    p=&a;//&a的结果是一个指针,类型是int*,指向的类型是int,指向的地址是a的地址。
    *p=24;//*p的结果,在这里它的类型是int,它所占用的地址是p所指向的地址,显然,*p就是变量a。
    ptr=&p;//&p的结果是个指针,该指针的类型是p的类型加个*,在这里是int**。该指针所指向的类型是p的类型,这里是int*。该指针所指向的地址就是指针p自己的地址。
    *ptr=&b;//*ptr是个指针,&b的结果也是个指针,且这两个指针的类型和所指向的类型是一样的,所以?amp;b来给*ptr赋值就是毫无问题的了。
    **ptr=34;//*ptr的结果是ptr所指向的东西,在这里是一个指针,对这个指针再做一次*运算,结果就是一个int类型的变量。
    复制代码



    指针表达式

    一个表达式的最后结果如果是一个指针,那么这个表达式就叫指针表达式。下面是一些指针表达式的例子:  

    例六:  

    复制代码
    int a,b;  
    int array[10];
    int *pa;
    pa=&a;//&a是一个指针表达式。
    int **ptr=&pa;//&pa也是一个指针表达式。
    *ptr=&b;//*ptr和&b都是指针表达式。
    pa=array;
    pa++;//这也是指针表达式。
    复制代码


    例七:  

    char *arr[20];  
    char **parr=arr;//如果把arr看作指针的话,arr也是指针表达式
    char *str;
    str=*parr;//*parr是指针表达式
    str=*(parr+1);//*(parr+1)是指针表达式
    str=*(parr+2);//*(parr+2)是指针表达式


    由于指针表达式的结果是一个指针,所以指针表达式也具有指针所具有的四个要素:指针的类型,指针所指向的类型,指针指向的内存区,指针自身占据的内存。

    好了,当一个指针表达式的结果指针已经明确地具有了指针自身占据的内存的话,这个指针表达式就是一个左值,否则就不是一个左值。 在例七中,&a不是一个左值,因为它还没有占据明确的内存。*ptr是一个左值,因为*ptr这个指针已经占据了内存,其实*ptr就是指针pa,既然pa已经在内存中有了自己的位置,那么*ptr当然也有了自己的位置。

    数组和指针的关系

    如果对声明数组的语句不太明白的话,请参阅我前段时间贴出的文章<<如何理解c和c++的复杂类型声明>>。 数组的数组名其实可以看作一个指针。看下例:  

    例八:  

    int array[10]={0,1,2,3,4,5,6,7,8,9},value;  
    ...
    ...
    value=array[0];//也可写成:value=*array;
    value=array[3];//也可写成:value=*(array+3);
    value=array[4];//也可写成:value=*(array+4);




    上例中,一般而言数组名array代表数组本身,类型是int [10],但如果把array看做指针的话,它指向数组的第0个单元,类型是int *,所指向的类型是数组单元的类型即int。因此*array等于0就一点也不奇怪了。同理,array+3是一个指向数组第3个单元的指针,所以*(array+3)等于3。其它依此类推。 

    例九:  

    复制代码
    char *str[3]={  
    "Hello,this is a sample!",
    "Hi,good morning.",
    "Hello world"
    };
    char s[80];
    strcpy(s,str[0]);//也可写成strcpy(s,*str);
    strcpy(s,str[1]);//也可写成strcpy(s,*(str+1));
    strcpy(s,str[2]);//也可写成strcpy(s,*(str+2));
    复制代码


    上例中,str是一个三单元的数组,该数组的每个单元都是一个指针,这些指针各指向一个字符串。把指针数组名str当作一个指针的话,它指向数组的第0号单元,它的类型是char**,它指向的类型是char *。

    *str也是一个指针,它的类型是char*,它所指向的类型是char,它指向的地址是字符串"Hello,this is a sample!"的第一个字符的地址,即'H'的地址。 str+1也是一个指针,它指向数组的第1号单元,它的类型是char**,它指向的类型是char *。 

    *(str+1)也是一个指针,它的类型是char*,它所指向的类型是char,它指向"Hi,good morning."的第一个字符'H',等等。  

    下面总结一下数组的数组名的问题。声明了一个数组TYPE array[n],则数组名称array就有了两重含义:第一,它代表整个数组,它的类型是TYPE [n];第二,它是一个指针,该指针的类型是TYPE*,该指针指向的类型是TYPE,也就是数组单元的类型,该指针指向的内存区就是数组第0号单元,该指针自己占有单独的内存区,注意它和数组第0号单元占据的内存区是不同的。该指针的值是不能修改的,即类似array++的表达式是错误的。 

    在不同的表达式中数组名array可以扮演不同的角色。  

    在表达式sizeof(array)中,数组名array代表数组本身,故这时sizeof函数测出的是整个数组的大小。  

    在表达式*array中,array扮演的是指针,因此这个表达式的结果就是数组第0号单元的值。sizeof(*array)测出的是数组单元的大小。  

    表达式array+n(其中n=0,1,2,....。)中,array扮演的是指针,故array+n的结果是一个指针,它的类型是TYPE*,它指向的类型是TYPE,它指向数组第n号单元。故sizeof(array+n)测出的是指针类型的大小。 

    例十:  

    int array[10];  
    int (*ptr)[10];
    ptr=&array;


    上例中ptr是一个指针,它的类型是int (*)[10],他指向的类型是int [10],我们用整个数组的首地址来初始化它。在语句ptr=&array中,array代表数组本身。 

    本节中提到了函数sizeof(),那么我来问一问,sizeof(指针名称)测出的究竟是指针自身类型的大小呢还是指针所指向的类型的大小?答案是前者。例如:

    int (*ptr)[10];  


    则在32位程序中,有:  

    sizeof(int(*)[10])==4  
    sizeof(int [10])==40
    sizeof(ptr)==4

    实际上,sizeof(对象)测出的都是对象自身的类型的大小,而不是别的什么类型的大小。  

    指针和结构类型的关系

    可以声明一个指向结构类型对象的指针。  

    例十一:  

    复制代码
    struct MyStruct  
    {
    int a;
    int b;
    int c;
    }

    MyStruct ss={20,30,40};//声明了结构对象ss,并把ss的三个成员初始化为20,30和40。
    MyStruct *ptr=&ss;//声明了一个指向结构对象ss的指针。它的类型是
    MyStruct*,它指向的类型是MyStruct。
    int *pstr=(int*)&ss;//声明了一个指向结构对象ss的指针。但是它的类型和它指向的类型和ptr是不同的。
    复制代码

    请问怎样通过指针ptr来访问ss的三个成员变量?  

    答案:  

    ptr->a;  
    ptr->b;
    ptr->c;


    又请问怎样通过指针pstr来访问ss的三个成员变量?  
    答案:  
     

    *pstr;//访问了ss的成员a。  
    *(pstr+1);//访问了ss的成员b。
    *(pstr+2)//访问了ss的成员c。


    呵呵,虽然我在我的MSVC++6.0上调式过上述代码,但是要知道,这样使用pstr来访问结构成员是不正规的,为了说明为什么不正规,让我们看看怎样通过指针来访问数组的各个单元: 
    例十二:  

    int array[3]={35,56,37};  
    int *pa=array;

    通过指针pa访问数组array的三个单元的方法是:  

    *pa;//访问了第0号单元  
    *(pa+1);//访问了第1号单元
    *(pa+2);//访问了第2号单元


    从格式上看倒是与通过指针访问结构成员的不正规方法的格式一样。

    所有的C/C++编译器在排列数组的单元时,总是把各个数组单元存放在连续的存储区里,单元和单元之间没有空隙。但在存放结构对象的各个成员时,在某种编译环境下,可能会需要字对齐或双字对齐或者是别的什么对齐,需要在相邻两个成员之间加若干个“填充字节”,这就导致各个成员之间可能会有若干个字节的空隙。

    所以,在例十二中,即使*pstr访问到了结构对象ss的第一个成员变量a,也不能保证*(pstr+1)就一定能访问到结构成员b。因为成员a和成员b之间可能会有若干填充字节,说不定*(pstr+1)就正好访问到了这些填充字节呢。这也证明了指针的灵活性。要是你的目的就是想看看各个结构成员之间到底有没有填充字节,嘿,这倒是个不错的方法。 

    通过指针访问结构成员的正确方法应该是象例十二中使用指针ptr的方法。

    指针和函数的关系

    可以把一个指针声明成为一个指向函数的指针。  

    int fun1(char*,int);  
    int (*pfun1)(char*,int);
    pfun1=fun1;
    ....
    ....
    int a=(*pfun1)("abcdefg",7);//通过函数指针调用函数。

    可以把指针作为函数的形参。在函数调用语句中,可以用指针表达式来作为实参。  

    数组指针和指针数组的区别


    原文地址:http://c.biancheng.net/cpp/html/476.html

    数组指针(也称行指针)
    定义 int (*p)[n];
    ()优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n,也可以说是p的步长。也就是说执行p+1时,p要跨过n个整型数据的长度。

    如要将二维数组赋给一指针,应这样赋值:
    int a[3][4];
    int (*p)[4]; //该语句是定义一个数组指针,指向含4个元素的一维数组。
     p=a;        //将该二维数组的首地址赋给p,也就是a[0]或&a[0][0]
     p++;       //该语句执行过后,也就是p=p+1;p跨过行a[0][]指向了行a[1][]

    所以数组指针也称指向一维数组的指针,亦称行指针。

    指针数组
    定义 int *p[n];
    []优先级高,先与p结合成为一个数组,再由int*说明这是一个整型指针数组,它有n个指针类型的数组元素。这里执行p+1时,则p指向下一个数组元素,这样赋值是错误的:p=a;因为p是个不可知的表示,只存在p[0]、p[1]、p[2]...p[n-1],而且它们分别是指针变量可以用来存放变量地址。但可以这样 *p=a; 这里*p表示指针数组第一个元素的值,a的首地址的值。
    如要将二维数组赋给一指针数组:
    int *p[3];
    int a[3][4];
    p++; //该语句表示p数组指向下一个数组元素。注:此数组每一个元素都是一个指针
    for(i=0;i<3;i++)
    p[i]=a[i]
    这里int *p[3] 表示一个一维数组内存放着三个指针变量,分别是p[0]、p[1]、p[2]
    所以要分别赋值。

    这样两者的区别就豁然开朗了,数组指针只是一个指针变量,似乎是C语言里专门用来指向二维数组的,它占有内存中一个指针的存储空间。指针数组是多个指针变量,以数组形式存在内存当中,占有多个指针的存储空间。
    还需要说明的一点就是,同时用来指向二维数组时,其引用和用数组名引用都是一样的。
    比如要表示数组中i行j列一个元素:
    *(p[i]+j)、*(*(p+i)+j)、(*(p+i))[j]、p[i][j]

    优先级:()>[]>*

    出处:http://www.cnblogs.com/hongcha717/archive/2010/10/24/1859780.html

    =========================================================================

    一、指针数组和数组指针的内存布局

    初学者总是分不出指针数组与数组指针的区别。其实很好理解:
    指针数组:首先它是一个数组,数组的元素都是指针,数组占多少个字节由数组本身的大小决定,每一个元素都是一个指针,在32 位系统下任何类型的指针永远是占4 个字节。它是“储存指针的数组”的简称。
    数组指针:首先它是一个指针,它指向一个数组。在32 位系统下任何类型的指针永远是占4 个字节,至于它指向的数组占多少字节,不知道,具体要看数组大小。它是“指向数组的指针”的简称。

    下面到底哪个是数组指针,哪个是指针数组呢:
    A)
    int *p1[10];
    B)
    int (*p2)[10];
    每次上课问这个问题,总有弄不清楚的。这里需要明白一个符号之间的优先级问题。

    “[]”的优先级比“*”要高。p1 先与“[]”结合,构成一个数组的定义,数组名为p1,int *修饰的是数组的内容,即数组的每个元素。那现在我们清楚,这是一个数组,其包含10 个指向int 类型数据的指针,即指针数组。至于p2 就更好理解了,在这里“()”的优先级比“[]”高,“*”号和p2 构成一个指针的定义,指针变量名为p2,int 修饰的是数组的内容,即数组的每个元素。数组在这里并没有名字,是个匿名数组。那现在我们清楚p2 是一个指针,它指向一个包含10 个int 类型数据的数组,即数组指针。我们可以借助下面的图加深理解:

    二、int (*)[10] p2-----也许应该这么定义数组指针

    这里有个有意思的话题值得探讨一下:平时我们定义指针不都是在数据类型后面加上指针变量名么?这个指针p2 的定义怎么不是按照这个语法来定义的呢?也许我们应该这样来定义p2:
       int (*)[10] p2;
    int (*)[10]是指针类型,p2 是指针变量。这样看起来的确不错,不过就是样子有些别扭。其实数组指针的原型确实就是这样子的,只不过为了方便与好看把指针变量p2 前移了而已。你私下完全可以这么理解这点。虽然编译器不这么想。^_^

    三、再论a 和&a 之间的区别

    既然这样,那问题就来了。前面我们讲过a 和&a 之间的区别,现在再来看看下面的代码:
    int main()
    {
       char a[5]={'A','B','C','D'};
       char (*p3)[5] = &a;
       char (*p4)[5] = a;
       return 0;
    }
    上面对p3 和p4 的使用,哪个正确呢?p3+1 的值会是什么?p4+1 的值又会是什么?毫无疑问,p3 和p4 都是数组指针,指向的是整个数组。&a 是整个数组的首地址,a是数组首元素的首地址,其值相同但意义不同。在C 语言里,赋值符号“=”号两边的数据类型必须是相同的,如果不同需要显示或隐式的类型转换。p3 这个定义的“=”号两边的数据类型完全一致,而p4 这个定义的“=”号两边的数据类型就不一致了。左边的类型是指向整个数组的指针,右边的数据类型是指向单个字符的指针。在Visual C++6.0 上给出如下警告:
       warning C4047: 'initializing' : 'char (*)[5]' differs in levels of indirection from 'char *'。
    还好,这里虽然给出了警告,但由于&a 和a 的值一样,而变量作为右值时编译器只是取变量的值,所以运行并没有什么问题。不过我仍然警告你别这么用。

    既然现在清楚了p3 和p4 都是指向整个数组的,那p3+1 和p4+1 的值就很好理解了。

    但是如果修改一下代码,把数组大小改小点,会有什么问题?p3+1 和p4+1 的值又是多少呢?
    int main()
    {
       char a[5]={'A','B','C','D'};
       char (*p3)[3] = &a;
       char (*p4)[3] = a;
       return 0;
    }

    甚至还可以把代码再修改,把数组大小改大点:
    int main()
    {
       char a[5]={'A','B','C','D'};
       char (*p3)[10] = &a;
       char (*p4)[10] = a;
       return 0;
    }
    这个时候又会有什么样的问题?p3+1 和p4+1 的值又是多少?

    上述几个问题,希望读者能仔细考虑考虑,并且上机测试看看结果。

    测试结果:
    (1).char (*p2)[5]=a;必须使用强制转换,如:char (*p2)[5]=(char (*)[5])a;
    (2).把数组大小改变,都会编译不通过,提示:
    error C2440: 'initializing' : cannot convert from 'char (*)[5]' to 'char (*)[3]'
    error C2440: 'initializing' : cannot convert from 'char (*)[5]' to 'char (*)[10]'

    (3).把以上程序测试代码如下:
    int main()
    {
       char a[5]={'a','b','c','d'};
       char (*p1)[5]= &a;
       char (*p2)[5]=(char (*)[5])a;

       printf("a=%d\n",a);
       printf("a=%c\n",a[0]);
       printf("p1=%c\n",**p1);
       printf("p2=%c\n",**p2);
       printf("p1+1=%c\n",**(p1+1));
       printf("p2+1=%c\n",**(p2+1));

       return 0;
    }

    输出:
    a=1638208
    a=a
    p1=a
    p2=a
    p1+1=?
    p2+1=?
    Press any key to continue

    结论:
    根据指针类型及所指对象,表示指针大小,每次加1,表示增加指针类型大小的字节.----后面还会有解释说明.

    四、地址的强制转换

    先看下面这个例子:
    struct Test
    {
       int Num;
       char *pcName;
       short sDate;
       char cha[2];
       short sBa[4];
    }*p;

    假设p 的值为0x100000。如下表表达式的值分别为多少?
       p + 0x1 = 0x___ ?
       (unsigned long)p + 0x1 = 0x___?
       (unsigned int*)p + 0x1 = 0x___?
    我相信会有很多人一开始没看明白这个问题是什么意思。其实我们再仔细看看,这个知识点似曾相识。一个指针变量与一个整数相加减,到底该怎么解析呢?

    还记得前面我们的表达式“a+1”与“&a+1”之间的区别吗?其实这里也一样。指针变量与一个整数相加减并不是用指针变量里的地址直接加减这个整数。这个整数的单位不是byte 而是元素的个数。所以:p + 0x1 的值为0x100000+sizof(Test)*0x1。至于此结构体的大小为20byte,前面的章节已经详细讲解过。所以p +0x1 的值为:0x100014。

    (unsigned long)p + 0x1 的值呢?这里涉及到强制转换,将指针变量p 保存的值强制转换成无符号的长整型数。任何数值一旦被强制转换,其类型就改变了。所以这个表达式其实就是一个无符号的长整型数加上另一个整数。所以其值为:0x100001。

    (unsigned int*)p + 0x1 的值呢?这里的p 被强制转换成一个指向无符号整型的指针。所以其值为:0x100000+sizof(unsigned int)*0x1,等于0x100004。

    上面这个问题似乎还没啥技术含量,下面就来个有技术含量的:在x86 系统下,其值为多少?
    intmain()
    {
       int a[4]={1,2,3,4};
       int *ptr1=(int *)(&a+1);//指向a数组后面的内存单元,&a+1表示向后移16个存储单元
       int *ptr2=(int *)((int)a+1);//表示a的存储单元的地址增加一个字节
       printf("%x,%x",ptr1[-1],*ptr2);//ptr1[-1]其实指向的是a数组的最后一个单元,*ptr1则表示a数组的地址后移一个字节之后的4个连续存储单元所存储的值
       return 0;
    }
    这是我讲课时一个学生问我的题,他在网上看到的,据说难倒了n 个人。我看题之后告诉他,这些人肯定不懂汇编,一个懂汇编的人,这种题实在是小case。下面就来分析分析这个问题:

    根据上面的讲解,&a+1 与a+1 的区别已经清楚。

    ptr1:将&a+1 的值强制转换成int*类型,赋值给int* 类型的变量ptr,ptr1 肯定指到数组a 的下一个int 类型数据了。ptr1[-1]被解析成*(ptr1-1),即ptr1 往后退4 个byte。所以其值为0x4。
    ptr2:按照上面的讲解,(int)a+1 的值是元素a[0]的第二个字节的地址。然后把这个地址强制转换成int*类型的值赋给ptr2,也就是说*ptr2 的值应该为元素a[0]的第二个字节开始的连续4 个byte 的内容。

    其内存布局如下图:
    好,问题就来了,这连续4 个byte 里到底存了什么东西呢?也就是说元素a[0],a[1]里面的值到底怎么存储的。这就涉及到系统的大小端模式了,如果懂汇编的话,这根本就不是问题。既然不知道当前系统是什么模式,那就得想办法测试。大小端模式与测试的方法在第一章讲解union 关键字时已经详细讨论过了,请翻到彼处参看,这里就不再详述。我们可以用下面这个函数来测试当前系统的模式。
    int checkSystem()
    {
      union check
      {
          int i;
          char ch;
      } c;
      c.i = 1;
      return (c.ch ==1);//如果当前系统为大端模式这个函数返回0;如果为小端模式,函数返回1。
    }

    如果当前系统为大端模式这个函数返回0;如果为小端模式,函数返回1。也就是说如果此函数的返回值为1 的话,*ptr2 的值为0x2000000。如果此函数的返回值为0 的话,*ptr2 的值为0x100。

    用函数指针变量调用函数

    原文地址:http://c.biancheng.net/cpp/biancheng/view/164.html

    指针变量也可以指向一个函数。一个函数在编译时被分配给一个入口地址。这个函数入口地址就称为函数的指针。可以用一个指针变量指向函数,然后通过该指针变量调用此函数。

    例6.14 求a和b中的大者。

    先按一般方法写程序:

    1. #include <iostream>
    2. using namespace std;
    3. int main()
    4. {
    5. int max(int x,int y); //函数声明
    6. int a,b,m;
    7. cin>>a>>b;
    8. m=max(a,b); //调用函数max,求出最大值,赋给m
    9. cout<<"max="<<m<<endl;
    10. return 0;
    11. }
    12. int max(int x,int y)
    13. {
    14. int z;
    15. if(x>y) z=x;
    16. else z=y;
    17. return(z);
    18. }


    可以用一个指针变量指向max函数,然后通过该指针变量调用此函数。定义指向max函数的指针变量的方法是:


    请将它和函数max的原型作比较:
        int max(int, int);  //max函数原型

    可以看出: 只是用(*p)取代了max,其他都一样。现在将上面程序的主函数修改如下:

    1. #include <iostream>
    2. using namespace std;
    3. int main( )
    4. {
    5. int max(int x,int y); //函数声明
    6. int (*p)(int,int); //定义指向函数的指针变量p
    7. int a,b,m;
    8. p=max; //使p指向函数max
    9. cin>>a>>b;
    10. m=p(a,b);
    11. cout<<″max=″<<m<<endl;
    12. return 0;
    13. }

    请注意第7行的赋值语句“p=max;”。此语句千万不要漏写,它的作用是将函数max的入口地址赋给指针变量p。这时,p才指向函数max。见图6.20。


    图6.20


    指向函数的指针变量的一般定义形式为:
        函数类型  (*指针变量名)(函数形参表);

    用指向函数的指针作函数参数

    在C语言中,函数指针变量常见的用途之一是作为函数的参数,将函数名传给其他函数的形参。这样就可以在调用一个函数的过程中根据给定的不同实参调用不同的函数。

    例如,利用这种方法可以编写一个求定积分的通用函数,用它分别求5个函数的定积分:


    可以看出,每次需要求定积分的函数是不一样的。可以编写一个求定积分的通用函数integral,它有3个形参: 下限a、上限b,以及指向函数的指针变量fun。函数原型可写为:
        double integral (double a, double b, double (*fun)(double));
    分别编写5个函数f1,f2,f3,f4,f5, 用来求上面5个函数的值。然后先后调用integral函数5次,每次调用时把a,b以及f1,f2,f3,f4,f5之一作为实参,即把上限、下限以及有关函数的入口地址传送给形参fun。在执行integral函数过程中求出各函数定积分的值。

    在面向对象的C++程序设计中,这种用法就比较少了。

  • 相关阅读:
    快速入门各种跨域
    常用知识点
    比较少用的格式
    git
    “没有用var声明的为全局变量”这种说法不准确
    类数组对象
    函数上下文的变量对象实例
    var a =10 与 a = 10的区别
    原型链与作用域链、执行上下文
    闭包的作用
  • 原文地址:https://www.cnblogs.com/zc347656462/p/7111985.html
Copyright © 2011-2022 走看看