为您推荐:
精华内容
最热下载
问答
  • 5星
    40KB weixin_51194902 2021-01-08 12:19:19
  • 5星
    56KB qq_40905804 2021-01-27 19:09:08
  • 5星
    1.46MB weixin_44573410 2021-03-15 21:57:51
  • 5星
    1.25MB weixin_44573410 2021-03-13 20:36:25
  • 5星
    4.4MB I520ZYS 2021-02-19 09:53:22
  • 5星
    137KB weixin_44573410 2021-03-02 23:27:18
  • 5星
    435KB weixin_44573410 2021-03-13 20:29:44
  • 5星
    712KB weixin_44573410 2021-03-01 13:55:37
  • 5星
    69KB weixin_43474701 2021-08-06 22:35:01
  • 5星
    451B qq_24896243 2021-06-15 23:47:28
  • 要了解指针,多多少少会出现一些比较复杂的类型,所以我先介绍一下如何完全理解一个复杂类型,要理解复杂类型其实很简单,一个类型里会出现很多运算符,他们也像普通的表达式一样,有优先级,其优先级和运算优先级一样,所以...

    前言:复杂类型说明 


        要了解指针,多多少少会出现一些比较复杂的类型,所以我先介绍一下如何完全理解一个复杂类型,要理解复杂类型其实很简单,一个类型里会出现很多运算符,他们也像普通的表达式一样,有优先级,其优先级和运算优先级一样,所以我总结了一下其原则:从变量名处起,根据运算符优先级结合,一步一步分析.下面让我们先从简单的类型开始慢慢分析吧:

    int p; //这是一个普通的整型变量  
    int *p; //首先从P 处开始,先与*结合,所以说明P 是一个指针,然后再与int 结合,说明指针所指向的内容的类型为int 型.所以P是一个返回整型数据的指针  
    int p[3]; //首先从P 处开始,先与[]结合,说明P 是一个数组,然后与int 结合,说明数组里的元素是整型的,所以P 是一个由整型数据组成的数组  
    int *p[3]; //首先从P 处开始,先与[]结合,因为其优先级比*高,所以P 是一个数组,然后再与*结合,说明数组里的元素是指针类型,然后再与int 结合,说明指针所指向的内容的类型是整型的,所以P 是一个由返回整型数据的指针所组成的数组  
    int (*p)[3]; //首先从P 处开始,先与*结合,说明P 是一个指针然后再与[]结合(与"()"这步可以忽略,只是为了改变优先级),说明指针所指向的内容是一个数组,然后再与int 结合,说明数组里的元素是整型的.所以P 是一个指向由整型数据组成的数组的指针  
    int **p; //首先从P 开始,先与*结合,说是P 是一个指针,然后再与*结合,说明指针所指向的元素是指针,然后再与int 结合,说明该指针所指向的元素是整型数据.由于二级指针以及更高级的指针极少用在复杂的类型中,所以后面更复杂的类型我们就不考虑多级指针了,最多只考虑一级指针.  
    int p(int); //从P 处起,先与()结合,说明P 是一个函数,然后进入()里分析,说明该函数有一个整型变量的参数,然后再与外面的int 结合,说明函数的返回值是一个整型数据  
    Int (*p)(int); //从P 处开始,先与指针结合,说明P 是一个指针,然后与()结合,说明指针指向的是一个函数,然后再与()里的int 结合,说明函数有一个int 型的参数,再与最外层的int 结合,说明函数的返回类型是整型,所以P 是一个指向有一个整型参数且返回类型为整型的函数的指针  
    int *(*p(int))[3]; //可以先跳过,不看这个类型,过于复杂从P 开始,先与()结合,说明P 是一个函数,然后进入()里面,与int 结合,说明函数有一个整型变量参数,然后再与外面的*结合,说明函数返回的是一个指针,,然后到最外面一层,先与[]结合,说明返回的指针指向的是一个数组,然后再与*结合,说明数组里的元素是指针,然后再与int 结合,说明指针指向的内容是整型数据.所以P 是一个参数为一个整数据且返回一个指向由整型指针变量组成的数组的指针变量的函数. 

    说到这里也就差不多了,我们的任务也就这么多,理解了这几个类型,其它的类型对我们来说也是小菜了,不过我们一般不会用太复杂的类型,那样会大大减小程序的可读性,请慎用,这上面的几种类型已经足够我们用了.

    一、细说指针


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

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

    (1)int*ptr;  
    (2)char*ptr;  
    (3)int**ptr;  
    (4)int(*ptr)[3];  
    (5)int*(*ptr)[4];

    1.指针的类型
    从语法的角度看,你只要把指针声明语句里的指针名字去掉,剩下的部分就是这个指针的类型。这是指针本身所具有的类型。让我们看看例一中各个指针的类型:
    (1)int*ptr;//指针的类型是int*
    (2)char*ptr;//指针的类型是char*
    (3)int**ptr;//指针的类型是int**
    (4)int(*ptr)[3];//指针的类型是int(*)[3]
    (5)int*(*ptr)[4];//指针的类型是int*(*)[4]

    怎么样?找出指针的类型的方法是不是很简单?
    2.指针所指向的类型
    当你通过指针来访问指针所指向的内存区时,指针所指向的类型决定了编译器将把那片内存区里的内容当做什么来看待。
    从语法上看,你只须把指针声明语句中的指针名字和名字左边的指针声明符*去掉,剩下的就是指针所指向的类型。例如:
    (1)int*ptr; //指针所指向的类型是int
    (2)char*ptr; //指针所指向的的类型是char
    (3)int**ptr; //指针所指向的的类型是int*
    (4)int(*ptr)[3]; //指针所指向的的类型是int()[3]
    (5)int*(*ptr)[4]; //指针所指向的的类型是int*()[4]

    在指针的算术运算中,指针所指向的类型有很大的作用。
    指针的类型(即指针本身的类型)和指针所指向的类型是两个概念。当你对C 越来越熟悉时,你会发现,把与指针搅和在一起的"类型"这个概念分成"指针的类型"和"指针所指向的类型"两个概念,是精通指针的关键点之一。我看了不少书,发现有些写得差的书中,就把指针的这两个概念搅在一起了,所以看起书来前后矛盾,越看越糊涂。
    3.指针的值----或者叫指针所指向的内存区或地址
    指针的值是指针本身存储的数值,这个值将被编译器当作一个地址,而不是一个一般的数值。在32 位程序里,所有类型的指针的值都是一个32 位整数,因为32 位程序里内存地址全都是32 位长。指针所指向的内存区就是从指针的值所代表的那个内存地址开始,长度为si zeof(指针所指向的类型)的一片内存区。以后,我们说一个指针的值是XX,就相当于说该指针指向了以XX 为首地址的一片内存区域;我们说一个指针指向了某块内存区域,就相当于说该指针的值是这块内存区域的首地址。指针所指向的内存区和指针所指向的类型是两个完全不同的概念。在例一中,指针所指向的类型已经有了,但由于指针还未初始化,所以它所指向的内存区是不存在的,或者说是无意义的。
    以后,每遇到一个指针,都应该问问:这个指针的类型是什么?指针指的类型是什么?该指针指向了哪里?(重点注意)
    4 指针本身所占据的内存区
    指针本身占了多大的内存?你只要用函数sizeof(指针的类型)测一下就知道了。在32 位平台里,指针本身占据了4 个字节的长度。指针本身占据的内存这个概念在判断一个指针表达式(后面会解释)是否是左值时很有用。


    二、指针的算术运算


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

     

      char a[20];  
        int *ptr=(int *)a; //强制类型转换并不会改变a 的类型  
        ptr++;  

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

    例三:

     

       int array[20]={0};  
        int *ptr=array;  
        for(i=0;i<20;i++)  
        {  
            (*ptr)++;  
            ptr++;  
        }  

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

    再看例子:
    例四:

    char a[20]="You_are_a_girl";  
    int *ptr=(int *)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 个字节。
    下面请允许我再举一个例子:(一个误区)

    例五:

       

     #include<stdio.h>  
        int main()  
        {  
            char a[20]=" You_are_a_girl";  
            char *p=a;  
            char **ptr=&p;  
            //printf("p=%d\n",p);  
            //printf("ptr=%d\n",ptr);  
            //printf("*ptr=%d\n",*ptr);  
            printf("**ptr=%c\n",**ptr);  
            ptr++;  
            //printf("ptr=%d\n",ptr);  
            //printf("*ptr=%d\n",*ptr);  
            printf("**ptr=%c\n",**ptr);  
        }  

    误区一、输出答案为Y 和o
    误解:ptr 是一个char 的二级指针,当执行ptr++;时,会使指针加一个sizeof(char),所以输出如上结果,这个可能只是少部分人的结果.
    误区二、输出答案为Y 和a误解:ptr 指向的是一个char *类型,当执行ptr++;时,会使指针加一个sizeof(char *)(有可能会有人认为这个值为1,那就会得到误区一的答案,这个值应该是4,参考前面内容), 即&p+4; 那进行一次取值运算不就指向数组中的第五个元素了吗?那输出的结果不就是数组中第五个元素了吗?答案是否定的.
    正解: ptr 的类型是char **,指向的类型是一个char *类型,该指向的地址就是p的地址(&p),当执行ptr++;时,会使指针加一个sizeof(char*),即&p+4;那*(&p+4)指向哪呢,这个你去问上帝吧,或者他会告诉你在哪?所以最后的输出会是一个随机的值,或许是一个非法操作.
    总结一下:
    一个指针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 的结果也是个指针,且这两个指针  
    //的类型和所指向的类型是一样的,所以用&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 当然也有了自己的位置。


    五、数组和指针的关系


    数组的数组名其实可以看作一个指针。看下例:
    例九:

    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,thisisasample!",  
        "Hi,goodmorning.",  
        "Helloworld"  
    };  
    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,thisisasample!"的第一个字符的地址,即'H'的地址。注意:字符串相当于是一个数组,在内存中以数组的形式储存,只不过字符串是一个数组常量,内容不可改变,且只能是右值.如果看成指针的话,他即是常量指针,也是指针常量.
    str+1 也是一个指针,它指向数组的第1 号单元,它的类型是char**,它指向的类型是char*。
    *(str+1)也是一个指针,它的类型是char*,它所指向的类型是char,它指向"Hi,goodmorning."的第一个字符'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)测出的是指针类型的大小。在32 位程序中结果是4
    例十一:

    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;  
        };  
        struct MyStruct ss={20,30,40};  
        //声明了结构对象ss,并把ss 的成员初始化为20,30 和40。  
        struct MyStruct *ptr=&ss;  
        //声明了一个指向结构对象ss 的指针。它的类型是  
        //MyStruct *,它指向的类型是MyStruct。  
        int *pstr=(int*)&ss;  
        //声明了一个指向结构对象ss 的指针。但是pstr 和  
        //它被指向的类型ptr 是不同的。  
    请问怎样通过指针ptr 来访问ss 的三个成员变量?
    答案:
    ptr->a; //指向运算符,或者可以这们(*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);
    //通过函数指针调用函数。
    可以把指针作为函数的形参。在函数调用语句中,可以用指针表达式来作为实参。
    例十四:

       

    int fun(char *);  
        inta;  
        char str[]="abcdefghijklmn";  
        a=fun(str);  
        int fun(char *s)  
        {  
            int num=0;  
            for(int i=0;;)  
            {  
                num+=*s;s++;  
            }  
            return num;  
        }  


    这个例子中的函数fun 统计一个字符串中各个字符的ASCII 码值之和。前面说了,数组的名字也是一个指针。在函数调用中,当把str作为实参传递给形参s 后,实际是把str 的值传递给了s,s 所指向的地址就和str 所指向的地址一致,但是str 和s 各自占用各自的存储空间。在函数体内对s 进行自加1 运算,并不意味着同时对str 进行了自加1 运算。


    八、指针类型转换


    当我们初始化一个指针或给一个指针赋值时,赋值号的左边是一个指针,赋值号的右边是一个指针表达式。在我们前面所举的例子中,绝大多数情况下,指针的类型和指针表达式的类型是一样的,指针所指向的类型和指针表达式所指向的类型是一样的。
    例十五:

    float f=12.3;  
    float *fptr=&f;  
    int *p;
    在上面的例子中,假如我们想让指针p 指向实数f,应该怎么办?
    是用下面的语句吗?
    p=&f;

    不对。因为指针p 的类型是int *,它指向的类型是int。表达式&f 的结果是一个指针,指针的类型是float *,它指向的类型是float。
    两者不一致,直接赋值的方法是不行的。至少在我的MSVC++6.0 上,对指针的赋值语句要求赋值号两边的类型一致,所指向的类型也一致,其它的编译器上我没试过,大家可以试试。为了实现我们的目的,需要进行"强制类型转换":
    p=(int*)&f;
    如果有一个指针p,我们需要把它的类型和所指向的类型改为TYEP *TYPE, 那么语法格式是: (TYPE *)p;
    这样强制类型转换的结果是一个新指针,该新指针的类型是TYPE *,它指向的类型是TYPE,它指向的地址就是原指针指向的地址。
    而原来的指针p 的一切属性都没有被修改。(切记)
    一个函数如果使用了指针作为形参,那么在函数调用语句的实参和形参的结合过程中,必须保证类型一致,否则需要强制转换
    例十六:

     

      void fun(char*);  
        int a=125,b;  
        fun((char*)&a);  
        void fun(char*s)  
        {  
            charc;  
            c=*(s+3);*(s+3)=*(s+0);*(s+0)=c;  
            c=*(s+2);*(s+2)=*(s+1);*(s+1)=c;  
        }  


    注意这是一个32 位程序,故int 类型占了四个字节,char 类型占一个字节。函数fun 的作用是把一个整数的四个字节的顺序来个颠倒。注意到了吗?在函数调用语句中,实参&a 的结果是一个指针,它的类型是int *,它指向的类型是int。形参这个指针的类型是char *,它指向的类型是char。这样,在实参和形参的结合过程中,我们必须进行一次从int *类型到char *类型的转换。
    结合这个例子,我们可以这样来
    想象编译器进行转换的过程:编译器先构造一个临时指针char *temp,然后执行temp=(char *)&a,最后再把temp 的值传递给s。所以最后的结果是:s 的类型是char *,它指向的类型是char,它指向的地址就是a 的首地址。
    我们已经知道,指针的值就是指针指向的地址,在32 位程序中,指针的值其实是一个32 位整数。
    那可不可以把一个整数当作指针的值直接赋给指针呢?就象下面的语句:

        unsigned int a;  
        TYPE *ptr; //TYPE 是int,char 或结构类型等等类型。  
        a=20345686;  
        ptr=20345686; //我们的目的是要使指针ptr 指向地址20345686  
          
        ptr=a; //我们的目的是要使指针ptr 指向地址20345686  
        //编译一下吧。结果发现后面两条语句全是错的。那么我们的目的就不能达到了吗?不,还有办法:  
        unsigned int a;  
        TYPE *ptr; //TYPE 是int,char 或结构类型等等类型。  
        a=N //N 必须代表一个合法的地址;  
        ptr=(TYPE*)a; //呵呵,这就可以了。  

    严格说来这里的(TYPE *)和指针类型转换中的(TYPE *)还不一样。这里的(TYPE*)的意思是把无符号整数a 的值当作一个地址来看待。上面强调了a 的值必须代表一个合法的地址,否则的话,在你使用ptr 的时候,就会出现非法操作错误。想想能不能反过来,把指针指向的地址即指针的值当作一个整数取出来。完全可以。下面的例子演示了把一个指针的值当作一个整数取出来,然后再把这个整数当作一个地址赋给一个指针:
    例十七:

    int a=123,b;  
    int *ptr=&a;  
    char *str;  
    b=(int)ptr; //把指针ptr 的值当作一个整数取出来。  
    str=(char*)b; //把这个整数的值当作一个地址赋给指针str

    现在我们已经知道了,可以把指针的值当作一个整数取出来,也可以把一个整数值当作地址赋给一个指针。

    九、指针的安全问题


    看下面的例子:
    例十八:

    char s='a';  
    int *ptr;  
    ptr=(int *)&s;  
    *ptr=1298;

    指针ptr 是一个int *类型的指针,它指向的类型是int。它指向的地址就是s 的首地址。在32 位程序中,s 占一个字节,int 类型占四个字节。最后一条语句不但改变了s 所占的一个字节,还把和s 相临的高地址方向的三个字节也改变了。这三个字节是干什么的?只有编译程序知道,而写程序的人是不太可能知道的。也许这三个字节里存储了非常重要的数据,也许这三个字节里正好是程序的一条代码,而由于你对指针的马虎应用,这三个字节的值被改变了!这会造成崩溃性的错误。
    让我们再来看一例:
    例十九:

    char a;  
    int *ptr=&a;  
    ptr++;  
    *ptr=115;

    该例子完全可以通过编译,并能执行。但是看到没有?第3 句对指针ptr 进行自加1 运算后,ptr 指向了和整形变量a 相邻的高地址方向的一块存储区。这块存储区里是什么?我们不知道。有可能它是一个非常重要的数据,甚至可能是一条代码。
    而第4 句竟然往这片存储区里写入一个数据!这是严重的错误。所以在使用指针时,程序员心里必须非常清楚:我的指针究竟指向了哪里。在用指针访问数组的时候,也要注意不要超出数组的低端和高端界限,否则也会造成类似的错误。
    在指针的强制类型转换:ptr1=(TYPE *)ptr2 中,如果sizeof(ptr2的类型)大于sizeof(ptr1 的类型),那么在使用指针ptr1 来访问ptr2所指向的存储区时是安全的。如果sizeof(ptr2 的类型) 小于sizeof(ptr1 的类型),那么在使用指针ptr1 来访问ptr2 所指向的存储区时是不安全的。至于为什么,读者结合例十八来想一想,应该会明白的。
     

    展开全文
    qq_33098049 2019-04-10 21:44:15
  • 刚开始接触C语言,对C语言中的指针有一点不能理解,接着还有一个"&"符号感觉两者有点接近和混淆。两者的定义如下: &是取地址符号 *是定义指针变量,即指向内存单元的指针 指针是C/C++语言的特色,它允许...

    前言

    刚开始接触C语言,对C语言中的指针有一点不能理解,接着还有一个"&"符号感觉两者有点接近和混淆。两者的定义如下:

    • &是取地址符号
    • *是定义指针变量,即指向内存单元的指针

    指针是C/C++语言的特色,它允许程序员直接操纵内存,所以说C语言是一种高效的语言。很多语言屏蔽了编程人员直接操纵内存的权限去降低软件开发的难度。

    &

    #include<stdio.h>
    int main()
    {
        int a=20;
    	printf("a 的值为%d\n",a);
    	printf("a 的地址为%d\n",&a);
    	return 0;
    }
    //打印结果:
    a 的值为20
    a 的地址为1703740(1703740为a地址0019FF3C的十进制)
    

    & 符号的作用很清晰, &a就是取变量a的地址。


    *

    #include<stdio.h>
    int main()
    {
        int a=20;
    	int *ip;
    	ip=a;
    	
    
    	printf("ip 的值为%d\n",ip);
    	printf("*ip 的值为%d\n",*ip);
    	return 0;
    }
    //打印结果:
    ip 的值为20
    

    为什么只打印了一个值呢?刚开始学习不是很懂,回去查指针变量的定义,再加上网上查阅各种资料,定义说:*表示一个指针变量,指向内存单元,指向内存单元,应该就是地址吧,那么ip变量应该被赋值一个地址,前边说的 & 不就表示一个地址吗,试一下

    #include <stdio.h>
     
     
    int main ()
    {
    	int a=5;
    	int *b;
    	b=&a;
    	printf("a 的值为 %d\n",a);
    	printf("&a 的值为 %d\n",&a);
    	printf("b的值为%d\n",b);
    	printf("*b 的值为 %d\n",*b);
    }
    //打印结果:
    a 的值为 5
    &a 的值为 1703740
    b的值为1703740
    *b 的值为 5
    

    定义变量时int* a;int* 表示取指针,在地址前加*表示这个地址的内容。

    函数指针和指针函数

    函数指针的定义:
    returnType (*pointerName)(param list);
    指针函数的定义:
    returnType *pointerName(paramlist);

    returnType 为函数返回值类型,pointerNmae 为指针名称,param list 为函数参数列表,函数指针赋值为函数地址。注意:( )的优先级高于*,第一个括号不能省略,如果写作returnType *pointerName(paramlist);就成了函数原型(指针函数),它表明函数的返回值类型为returnType *,返回值类型为1个指针。

    调用函数直接调用就好了,为什么还要定义一个函数指针呢,才接触时很不理解,后了解到由于函数指针存储的是一个内存地址,在传参的时候传入一个地址比传入一个地址对应的函数名更高效,运算速度更快。

    数组指针

    数组是一种结构类型,在某种意义上来说数组名就是指针,可以转化为指向其实体的指针,指针则是变量,仅仅意味着数组的存放地址,数组名概念比较宽泛,也更加复杂,而指针仅仅代表数组的首地址

    #include <stdio.h>
     
    int main()
    {
    	int a[]={1,2,3,4,5};
    	int *p=a;
    	printf("数组a首地址%d\n",&a);//数组a首地址1703724
    	printf("数组a首地址%d\n",p);//数组a首地址1703724
    	printf("a所占空间%d\n",sizeof(a));//a所占空间20(a代表整个数组,大小为4*5=20)
    	printf("*p所占内存空间%d\n",sizeof(p));//*p所占内存空间4(*p仅仅指向首地址)
    	printf("*p指向地址的内容%d\n",*p);//*p指向地址的内容1
    
    
    	return 0;
     
    }
    

    指向指针的指针

    #include<stdio.h>
    #include<stdlib.h>
    
    int main(){
    	
    	char *s[]={"aaa","bbb","ccc","ddd"};
    	char **p;//定义指向指针的指针
    	int k;
    	for(k=0;k<4;k++){
    		p=&s[k];//指针赋值为地址 如果不加则为s[k]的内容,会出错
    		printf("%s\n",*p);//使用*p访问指针所指向地址的内容
    	
    	}
    	return 0;
    
    }
    

    总结

    1. &a表示a变量的地址,scanf("%d",&a)中&a就是将输入的值存入a变量的地址中
    2. *ip定义一个指针变量ip,ip代表指针变量,赋值内容为地址(即指向变量的地址)
    3. *ip表示值是地址的变量,访问的是ip地址内容
    4. 在数组中指针变量仅仅代表数组的首地址,数组名就数组的首地址
    展开全文
    qq_36754767 2019-04-21 10:43:25
  • 文章转自:无际单片机大家好,我是无际。今天给大家来讲解一下指针。我会由浅到深,最后结合实际应用讲解,让大家学会指针的同时,知道大佬们都用指针来干嘛!长文预警!全文大约5200多字,学指针看...

    文章转自:无际单片机

    大家好,我是无际。

    今天给大家来讲解一下指针。

    我会由浅到深,最后结合实际应用讲解,让大家学会指针的同时,知道大佬们都用指针来干嘛!

    长文预警!全文大约5200多字,学指针看这篇文章就够了!

    很多人跟我刚学习c语言一样,都害怕指针。

    我也是后面做了一些物联网网关才知道,指针是c语言的灵魂这句话真正含义

    没有指针,很多功能实现起来确实很不方便,比如做不到真正的模块化编程。

    Ok,废话不多说,下面正式进入主题。

    一、通过这篇文章你能掌握以下知识:

    1. 指针的相关概念

    2. 掌握指针与数组之间的关系

    3. 掌握指针指向的指针

    4. 掌握如何使用指针变量做函数参数

    5. 掌握如何使用指针函数

    6. 掌握如何使用指针数组函数

    那么这篇文章对应有视频教程,如果不喜欢看文章的可以去看视频,教程在小破站可以搜无际单片机编程找到,也可以找我们拿

    二、指针的作用

    指针是C语言中一个比较重要的东西,有人说指针是C语言的灵魂这句话说的一点也没错。

    正确灵活地运用它,可以有效地表达一些复杂的数据结构,比如系统的动态分配内存、消息机制、任务调度、灵活矩阵定时等等。

    掌握指针可以使你的程序更加简洁、紧凑、高效。

    那么在单片机领域,如果是做稍微大一点的项目,需要把每个功能做成模块化,硬件驱动层和应用层分别独立运行。

    即使更换单片机型号也不用修改应用层程序,即移植性非常强,这些都离不开指针。

    甚至没指针会很难实现,即使实现代码的可移植性也很差。

    三、指针的概念

    前面讲了指针的作用,这里再强调一点,指针是一把双刃剑。

    用好了能十分灵活而且提高程序的效率,但是如果使用不当,则会出现程序”死机”等致命问题。

    而这些问题往往是由于错误地使用指针而造成的,最常见的就是内存溢出错误,指针指向未知地址。

    1.地址与指针

    指针是一个比较抽象的概念,如果想真正了解指针,那么要先从数据是如何存储的说起,我们通过一个图来看一下数据在内存里存储的情况。

    在这个图中,都是以16进制显示。

    红色标注的0x00000400代表地址内存地址绿色37,30代表数据而橙色标注的00 01代表地址递增量,即代表0x000004000x00000401,每个地址存储1个字节数据。

    那么我们把这个图看作是数据在内存里的存储形式,0x00000400这个内存地址存储着数据37,0x00000401这个内存地址存储着数据30。

    当我们在程序里定义一个字节的变量,那么在编译器编译时就会给这个变量分配一个这样的内存地址来存储。

    假设我们定义以下变量

    unsigned char a;

    a = 0x37;

    对应这个图就是,编译器在编译时会为变量a分配一个字节的内存空间并把0x37这个数据存储进去,并将变量名a改成地址0x00000400,以便CPU的访问。

    通过这个地址就能找到变量a数据的存储位置,而这个地址0x00000400其实就是指针通过这个指针可以访问变量a的数据

    2.指针变量

    通过上面讲解我们明白了通过地址能访问内存的数据,这个地址啊就是指针。

    那么指针和指针变量呢是不一样的概念,大家一定要记住了。

    指针是概念、指针变量是这个概念的具体应用之一,我们先来看一下C语言里怎么定义指针变量。

    指针变量定义的一般形式:

    变量类型 *变量名

    unsigned char *p;

    通过这种语法,我们就能够定义一个指针变量p。

    指针变量赋值

    指针和指针变量是两个概念,指针变量跟普通变量一样,在使用前一定要定义和赋值(指向地址)

    给指针变量赋的值和普通变量不同,给指针变量赋值只能赋地址,而不能赋予其他任何值,否则会引起错误。

    那么怎么获取普通变量的地址呢,在C语言里可以使用”&”来获取普通变量的地址,一般用以下格式来表示:

    &变量名

    那么通过&变量名取得变量地址后就可以赋值给指针变量

    举例:

     unsigned char a;

     unsigned char *p

     int main()

     {

           p = &a;
     }

    这个代码里,我们定义了一个变量a, 定义了一个指针变量p

    我们通过运算符&把变量a的内存地址赋值给变量p,所以p指向了变量a的内存存储地址

    上面说了指针变量赋值的问题,那么怎么获取和改变指针变量指向那个内存地址的数据呢?我们可以通过:

    *指针变量 = 数值

    如:*p = 10;

    这样的操作可以改变指针变量指向那个内存地址的数据。

    通过:

    a = *p;

    来获取指针变量指向那个内存地址的数据。

    下面我们通过一个代码实验来举例。

    这里我们定义了变量a和指针变量p,然后a的值初始化为10。

    把a的地址赋值给指针变量p,接着我们输出a的地址是0x60ff33。

    由于前面我们把a的地址赋值给了指针变量p,所以p指向的地址也是0x60ff33。

    那么我们再来看一下,指针变量的在内存里的存储地址是0x60ff2c。

    所以大家这里要注意了,我们定义指针变量时,即便指针变量是指向地址用的,但是编译器也会分配一块内存地址来存储指针变量

    我们接着来看下变量a的输出值。

    a=10, *p是获取指针指向内存地址的数据,所以也是10。

    下面就是通过指针变量来改变变量a的值,因为指针变量p指向的是变量a的地址,所以改变指针变量p指向内存地址的数据就可以改变变量a的值。

    那么通过这么原理,我们是不是不用指针变量,也不用a等于多少来改变a的值呢?当然可以!

    我们看下面通过内存地址改变变量a的值,我们前面知道a的地址是0x60ff33,那我们可以直接写0x60ff33=12来改变变量a的值。

    当然这里要注意,编译器编译时并不知道0x60ff33是什么东西,所以要把这个整形地址转换成指针类型。

    最后通过*+地址语法改变这个地址里面的数据。

    我们看输出结果,可以发现a的值已经成功被改成了12。

    其实通过指针变量改变某个内存地址的数据就是这个原理,但是指针变量好处可以任意起名字。

    也不用像这样先把变量a的地址读出来,然后通过地址去改变它的值,用起来就很方便,所以通过指针变量来替代了这种做法。

    四、数组与指针

    一般系统或编译器会分配连续地址的内存来存储数组里的元素,如果把数组地址赋值给指针变量,那么就可以通过指针变量来引用数组,读写数组里的元素了。我们来做个实验:

    从这个代码来看,定义了一个数组buff并初始化为1,2,3,4,5。

    定义了2个指针变量p1和p1,分别指向buff, &buff[0]。

    buff默认的是数组下标为0元素的存储地址。

    所以这里buff和&buff[0]是同一个内存地址,只是写法不一样。

    我们从输出结果可以看的出来,数组和指针变量的地址都是一样的,所以大家用这几种写法都是可以的。

    那么我们来看下输出结果,都是1,说明操作是对的。

    指针自加自减运算

    指针变量除了可以用来获取内存地址的值以外,还可以用来进行加减运算。

    但是这个加减呢跟普通变量加减不一样,普通变量加减的是数值,而指针变量加减的是地址,我们来通过代码来讲解下。

    同样这里定义了数组buff并初始化为1,2,3,4,5。

    我们把指针变量p1指向数组第一个元素的地址,即0x402000。

    然后我们直接看p1++的操作,p1++后我们看到p=0x402001,所以指针变量的加减等运算是指向地址的运算

    其他减法乘除法也是基于地址的运算。

    二维数组与指针

    通过一维数组与指针的讲解,相信大家已经掌握。

    那么二维数组与指针的操作也是一样的, 二维数组和一维数组一样,都是分配连续的地址来存储的数据的。

    我们还是通过一个例子来实践一下:

    首先我们定义了一个二维数组buff和指针变量p1。

    p1指向二维数组的[0][0]这个元素地址,这个就是为这个数组分配时的首地址。

    然后打印二维数组里每个元素的地址和值,接着打印指针变量地址和值,这些就是指针和二维数组的用法,比较简单,这些代码大家可以去做下实验。

    四、指向指针的指针

    一个指针变量指向整型变量或者字符型变量,当然也可以指向指针变量,这种指针变量用于指向指针类型变量时,就称为指向指针的变量,也叫双重指针。

    定义方法:

    数据类型 **指针变量名;

    例如:unsigned char **p;

    这个含义就是定义一个指向指针的指针变量p,它指向另一个指针变量,我们通过代码来说明一下会更好理解一点。

    我们定义一个变量a, 定义一个指针变量p1,定义一个双重指针变量p2,然后打印这3个变量的内存地址。

    编译器在编译的时候呢,也会为指针变量和双重指针变量分配一个存储空间。

    虽然指针变量是指向别的内存地址的,但是变量本身还是需要一个地址空间来存储的

    指针容易把人搞晕的就是,指针变量本身的存储地址和指向的地址分不清楚,这个是两个概念,大家要记住了。

    下面我们通过实验来看下双重指针怎么用:

    这里我们定义了变量a并初始化值为10,指针变量p1,双重指针变量p2。

    我们把p1指向变量a,p2指向变量p1的存储地址,这里要注意,不是p1指针指向的地址。

    然后我们打印看下结果,可以看到a的地址是0x404090。

    指针变量p1的存储地址通过&运算符获得即0x4040b0,p1指向a的地址,所以p1也等于0x404090。

    所以指针变量分为存储地址和指向地址,这两个是不一样的概念。

    而p2是双重指针,p2指向p1的存储地址0x4040b0,通过*p2获得0x4040b0这个地址里指向的地址0x404090,即p1指向的地址或变量a的地址。

    再通过**p2来获取0x404090地址里的值,得到10。

    这里还有一个问题需要注意,”*”这个运算符是从右到左进行运算的

    所以,**p2就是*(*p2),先取指向地址,再取指向地址里面存储的值

    一般在单片机程序中,尽量少使用这种指向指针的指针,防止出现Bug的时候非常难排查,目前我就在队列中使用过。

    五、指针变量作为函数形参

    一般我们都是以字符型、整型、数组等作为函数的形参带入。

    除此以外,指针变量也可以作为形参使用,而且用的非常多,主要目的是为了改变指针指向地址的值,专业术语是通过形参改变实参的值。

    我们直接写个代码来举个例子:

    这个代码中,我们定义一个SetValue函数,并且形参为指针变量p1。

    我们调用SetValue时把&a的地址赋值给形参指针变量p1。

    当我们通过*p1=5后就能把p1指向地址的值改成5,所以a的值也从1变成了5。

    这个就是指针变量作为函数形参的一种作用。

    实际当中使用功能当然不会这么简单。

    比如说我们常用的memset库函数,他的原型就是:

    Void *memset(void *s, int ch, size_t n);

    这个函数的作用是给某个数组或者结构体初始化用的。

    那么这个函数就使用了无指定数据类型的指针变量s,这样我们就可以很轻易的把实现某些功能的代码封装起来,使用者不用关心功能代码的实现,只需要了解函数怎么用即可。

    这样的话代码很简洁紧凑,移植性也好,这是把指针作为形参的一种作用,不过这些都只是冰山一角,在后面的学习当中,你会慢慢发现指针的魅力和强大。

    六、函数指针

    如果在程序中定义了一个函数,那么在编译时系统就会为这个函数代码分配一段存储空间,这段存储空间的首地址称为这个函数的地址

    而且函数名表示的就是这个地址

    既然是地址我们就可以定义一个指针变量来存放,这个指针变量就叫作函数指针变量,简称函数指针。

    在这个章节我们大家只要学会怎么定义和使用就行了,后面章节课程我们无际单片机编程会教大家函数指针的一些实际应用。

    我们学东西主要还是看能运用在哪里是吧?

    那么这个函数指针怎么定义呢?我们定义函数指针的格式如下:

    函数返回值类型 (* 指针变量名) (函数参数列表);

    这样就定义了一个函数指针变量func, 该函数指针返回值为unsigned char类型,然后有2个形参,分别是unsigned char类型。

    那么我们定义了这个函数指针变量以后要怎么使用呢?我们写个代码来解析一下。

    我们看下这个代码,首先我们定义一个函数指针func,再定义一个加法函数add,函数返回值为形参1+形参2的值。

    然后我们把func指向加法函数add,因为函数名称就是函数首地址,所以我们直接func=add就可以实现func指向add了。

    接着(*func)(1,2)代表执行func函数指针指向的函数,所以结果等于3。

    函数指针func的返回参数和形参不一定要和函数add定义成一样,func也可以不设置返回值或者形参,但是一般不建议这样做,避免引起一些不必要的错误。

    那么这里呢其实还有一个知识点要和大家说一下,我们先来写一段代码:

    这段代码调用函数指针的时候没有使用(*func)(1,2),这种用法也是可以的,执行的效果是一样,那么到底有什么区别呢?

    其实这个是编译器实现的问题,我们不用去纠结这种对我们没有意义的东西,除非你想去做编译器。

    大家只要记住函数指针是这样用的就行了。

    后期应用时再把它们多练几遍,以后做产品都用上,那么基本就熟了,而且产品的程序架构也更好了。

    七、函数指针数组

    像字符型,整形都是可以单独定义,也可以定义成数组,同样函数指针也可以定义成数组,同样,这里我们不讲那么多理论上的概念,直接记住怎么定义,怎么使用、用在哪里就行了。

    函数指针数组定义格式如下:

    函数返回值类型 (* 指针变量名[数组大小]) (函数参数列表);

    我们用程序表示如下:

    这样就定义了一个可以指向3个函数的函数指针数组。

    定义了以后,我们函数指针需要赋值,赋值的意思就是让它们指向函数首地址,一般初始化的方式有两种。

    这是第一种,定义函数指针数组的时候直接初始化。

    这是第二种,先定义然后再初始化,这里我们主要是要记住它们这两种的写法就行了。

    函数指针数组赋值以后通过以下代码来执行。

    我们可以看到直接写func[0]();就可以执行函数指针数值指向的函数了。

    那么这种函数指针数组到底有什么用呢?

    其实真正产品应用中函数指针数组是非常有用的。

    我举一个例子,写控制5个LED灯亮的函数,如果用传统方式,流程是先要判断控制哪个LED,然后再控制指定GPIO口高低电平。

    而函数指针只要一条语句,这就是所谓的代码的简洁、紧凑的特点,代码简洁紧凑以后自然也能节约cpu和内存的资源。

    下面是演示代码:

    Ok,终于码完了,原创不易,各位看官给我点个【赞】和【在看】吧,爱你们哟!


    推荐阅读:

    专辑|Linux文章汇总

    专辑|程序人生

    专辑|C语言

    我的知识小密圈

    关注公众号,后台回复「1024」获取学习资料网盘链接。

    欢迎点赞,关注,转发,在看,您的每一次鼓励,我都将铭记于心~

    嵌入式Linux

    微信扫描二维码,关注我的公众号

    展开全文
    weiqifa0 2021-07-18 00:10:30
  • 210KB limuzi20 2019-05-16 13:03:15
  • C语言是面向过程的,而C++是面向对象的 C和C++的区别: C是一个结构化语言,它的重点在于算法和数据结构。C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到输出(或实现过程...

    C语言是面向过程的,而C++是面向对象的

    C和C++的区别:

    C是一个结构化语言,它的重点在于算法和数据结构。C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到输出(或实现过程(事务)控制)。

    C++,首要考虑的是如何构造一个对象模型,让这个模型能够契合与之对应的问题域,这样就可以通过获取对象的状态信息得到输出或实现过程(事务)控制。 所以C与C++的最大区别在于它们的用于解决问题的思想方法不一样。之所以说C++比C更先进,是因为“ 设计这个概念已经被融入到C++之中 ”。

    C与C++的最大区别:在于它们的用于解决问题的思想方法不一样。之所以说C++比C更先进,是因为“ 设计这个概念已经被融入到C++之中 ”,而就语言本身而言,在C中更多的是算法的概念。那么是不是C就不重要了,错!算法是程序设计的基础,好的设计如果没有好的算法,一样不行。而且,“C加上好的设计”也能写出非常好的东西。

    相信对于学习过C语言的人来说指针一直是一个难点。其实C语言也就只有这么一个难点,攻破了这个难点,要做的就是不断实践了,毕竟想好学好一门语言不实践是不行的。

    每一个变量都有一个内存位置,每一个内存位置都定义了可使用连字号(&)运算符访问的地址,它表示了在内存中的一个地址,我们先了解什么是内存地址以及如何访问它。看下面的实例,它将输出定义的变量地址:

    小编推荐一个学C语言/C++的学习裙【 815393859】,无论你是大牛还是小白,是想转行还是想入行都可以来了解一起进步一起学习!裙内有开发工具,很多干货和技术资料分享!

    当上面的代码被编译和执行时,它会产生下列结果:

    接下来让我们看看什么是指针吧!

    指针是一个变量,其值为另一个变量的地址,即内存位置的直接地址。就像其他变量或常量一样,您必须在使用指针存储其他变量地址之前,对其进行声明。指针变量声明的一般形式为:

    type *name;

    这里的type 是指针的基类型,它必须是一个有效的 C 数据类型,name 是指针变量的名称。用来声明指针的星号 * 与乘法中使用的星号是相同的。但是,在这个语句中,星号是用来指定一个变量是指针。以下是有效的指针声明:

    所有指针的值的实际数据类型,不管是整型、浮点型、字符型,还是其他的数据类型,都是一样的,都是一个代表内存地址的长的十六进制数。不同数据类型的指针之间唯一的不同是,指针所指向的变量或常量的数据类型不同。

    在此列出一些平常可能用到的指针例子

    小编推荐一个学C语言/C++的学习裙【 815393859】,无论你是大牛还是小白,是想转行还是想入行都可以来了解一起进步一起学习!裙内有开发工具,很多干货和技术资料分享!

    接下来我们看下什么是函数指针

    一个函数总是占用一段连续的内存区域,函数名在表达式中有时也会被转换为该函数所在内存区域的首地址,这和数组名非常类似。我们可以把函数的这个首地址(或称入口地址)赋予一个指针变量,使指针变量指向函数所在的内存区域,然后通过指针变量就可以找到并调用该函数。这种指针就是函数指针。

    函数指针的定义形式为:

    returnType (*pointerName)(param list);

    returnType 为函数返回值类型,pointerNmae 为指针名称,param list 为函数参数列表。

    注意:

    ( )的优先级高于*,第一个括号不能省略,如果写作returnType *pointerName(paramlist);

    就成了函数原型,它表明函数的返回值类型为returnType *。

    下面写一个简单的栗子:

    编译并运行结果如下:

    上面对msg函数进行了调用,fun_p是一个函数指针。

    总结:

    指针(Pointer)就是内存的地址,C语言允许用一个变量来存放指针,这种变量称为指针变量。指针变量可以存放基本类型数据的地址,也可以存放数组、函数以及其他指针变量的地址。

    常见的指针含义:

    小编推荐一个学C语言/C++的学习裙【 815393859】,无论你是大牛还是小白,是想转行还是想入行都可以来了解一起进步一起学习!裙内有开发工具,很多干货和技术资料分享!

    这些是C/C++能做的

    服务器开发工程师、人工智能、云计算工程师、信息安全(黑客反黑客)、大数据 、数据平台、嵌入式工程师、流媒体服务器、数据控解、图像处理、音频视频开发工程师、游戏服务器、分布式系统、游戏辅助等

    展开全文
    qq_42785431 2018-07-26 19:11:22
  • Qxiaofei_ 2021-08-26 13:12:32
  • kangkanglhb88008 2018-11-09 20:02:06
  • kelehaier 2017-07-11 08:33:50
  • weixin_34598113 2021-05-21 13:24:37
  • 35KB lingjunxu01 2013-06-14 10:59:26
  • qq_45657288 2019-12-22 22:46:07
  • 4星
    1.64MB zxw2446 2011-12-14 12:06:52
  • modi000 2020-07-08 22:11:19
  • 41KB weixin_38599545 2021-01-01 05:03:42
  • qq_43775721 2020-11-03 15:16:13
  • m0_43450897 2020-07-24 15:11:06
  • weixin_26934905 2021-05-21 09:50:47
  • hm__2016 2020-02-26 21:09:47
  • weixin_36207483 2021-05-19 05:20:42
  • weixin_40629244 2021-11-01 14:12:21
  • Honeycomb_1 2020-02-04 15:47:32
  • zouchengzhi1021 2021-03-01 12:50:43
  • weixin_39886929 2021-05-21 08:32:27
  • weixin_32780623 2021-05-20 15:16:24
  • weixin_30369779 2021-05-21 09:50:49
  • weixin_39587113 2020-11-21 09:51:05

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 10,770
精华内容 4,308
关键字:

c语言指针通俗理解

c语言 订阅