首页 > 其他 > 详细

指针详解

时间:2016-01-26 01:45:47      阅读:169      评论:0      收藏:0      [点我收藏+]

一、指针基础:

  指针其实就是地址,指针变量就是存储地址的变量。只不过我们通常看到的是变量存储整形,字符型等这样的变量。指针变量,唯一特殊的,存放的是地址变量。通常一个指针变量所占的存储空间是8个字节。

    int i = 5;

    int *p; //定义一个指针类型的变量

    p = &i; //p 里面存放的是i这个变量的地址

    int *q; //定义一个q指针变量

    q = &p; //q里面存放的是一个指向p的地址。

    如下图所示:

    技术分享

    指针变量p 本身的地址是:0X3000,其实其存储空间的内容是0X2000.同理q是一个指向p的指针。有了指针,我访问i值得方法就很多了。

    例如:int a;

    a = i //把i的值5 赋值给a变量

    a = *p //*是取值运算。意思是:取出p变量所存储的地址指向空间的值。显然是5

    a = **q //*q的意思同上,即是:0X2000,那么 **q的含义就是取出0X2000地址所指向的存储空间的值,就是5.

二、指针辨析

2.1、空指针和野指针

    空指针:定义方式是:void *p = NULL;任何类型的指针都可以赋值给空指针。当不确定指针类型的时候可以先申明成空指针。

    野指针:int *p;只有定义,没有初始化,也就是不清楚指针所指向的地址到底是哪儿。非常危险。通常定义指针,可以 int *p = NULL;全0的地址空间,OS是不分配给任何空间使用的。所以可以确保安全。

2.2、指针与一维数组的关系:

    例如:

    int a[3] = {1,2,3};

    int *p;

    p = a;

    需要注意的是:数组是一段连续的存储空间。数组名a就是数组的起始地址。所以可以将a直接赋值给指针变量p

    a本身就是地址的话。那么*a就相当于相当于*p,就是去a数组的第一个元素值。*(a+1)就是取第二个元素的值。

    a本身是数组。但是a是常量,常量在程序的使用过程中可以参与运算,但是值不能被修改。所以*(a++)是错误的。p是指针变量。值是可以改变的。所以*(p++)是可以的。

    a[i] 和*(a+i)和 *(p+i);

2.3、指针与二维数组

    int a[2][3] = {{1,2,3},{4,5,6}};

   需要理解二维数组的存储规律

   二维数组可以看成多个一维数组组成。例如。上述的二维数组可以看成,两个三个元素的一维数组组成的。

   二维数组是以行顺序存储的。

    技术分享

    数组名a同上也是一个地址。a的值是0X2000,但是定义int *p; p=a 是错误的。编译的时候会产生警告。因为。a是地址,但是a是一个行地址。a指向的地址不仅只有一个数据元素,而是3个数据元素【元素的个数取决于二维数据的列元素的个数】。那么a+1 指向的 就是第二行了。具体地址如图上:0X2012.

    a[0]是一个列指针,指向的是一个一维数组的起始地址。a[0]的值和a一样都是0X2000,值一样,但是含义不同。a[0]等价于*(a+0).【此时的*相当于将a这样行地址降级为列地址。】所以*a+1指向的地址是0X2004. 那么a[0][1] 等价于 *(*a+1).    

    int a[2][3]={{1,2,3},{4,5,6}};

int *p;

p = *a;

    //下面的两行输出完全一样

printf ("%p---->%d\n",p+1,*(p+1)); //需要彻底理解p此时作为列指针的用法

printf ("%p---->%d\n",&a[0][1],a[0][1]);

    //典型的把二维数组当一维数组使用。同时,按行顺序存储得到验证。

p = &a[0][0];

printf ("%p----->%d\n",p+4,*(p+4));


    二维数组与指针结合使用:int *p ; *p = *a;【*p=*a 的含义是将第一列的首地址赋给p,p+1 指向第一行的第二个元素。就是p+1 的值是:0X2004】.通过此种方式定义列地址,把二维数组抽象成一维数组进行遍历使用。【int *p = &a[0][0],也是没有问题的】

    也可以通过定义数组指针的方式,使用二维数组。数组指针,就是一个指针类型,它指向的是一个数组。具体定义方式如下:    

    int a[2][3]={{1,2,3},{4,5,6}};

int (*p) [3] = NULL;//数组指针的定义方式。3取决二维数组的列元素个数

//p此时跟a的用法基本等价,只不过p可以自增自减,a是常量,不可以

    //a+1 和p+1一样都是指向下一行的第一个元素

    p = a;

    //以下三行输出完全一样

printf("%p--->%d\n",(*(p+1)+1),p[1][1]);

printf("%p--->%d\n",&a[1][1],a[1][1]);

printf("%p--->%d\n",&p[1][1],a[1][1]);

2.5、指针常量与常量指针的关系  

   指针常量:落脚点常量。所以首先是一个常量,只不过这个常量是指针的。而且这个常量的值不能修改。说白了就是指针的指向不能改变

   示例:

    int i = 5;

int j = 3;

int * const p = &i; //注意定义方式

p = &j; //这句是有错误的。错误在于,p是常量指针,指向是不能改变的。

   常量指针:落脚点是指针。首先是个指针。这个指针指向的是常量。也就是指针指向的内容是不允许修改的。但是指针的指向是可以改变的。

    示例:    

    #if 0

    //int *p = &i;

    //*p = 6; // 通过此种方法可以强行改变一个常量的值

    #endif

    int const *p = &i;

    *p = 6 //此时编译报错,不能改变指针常量所指向的值。

     const int  *const p;    即是常量指针,又是指针常量,就是说,既不能改变指向,也不能改变指向的值

 2.6、函数传参是指针与数组的关系:

           一维数组:

            int a[N];

            int *p = a;

             

实参形参
a 【地址】int *p
p 【地址】int *p
a[0] 【整值】int a
p+1【地址,第二个元素地址】int *p
a+1【地址,第二个元素地址】int *p
&a[3]【地址,第四个元素地址】int *p
*p【整值:第0个元素值】int a
*a【整值,第0个元素值】int a

  二维数组:

          int a[M][N] = {,,,,,,,,}; 

          int *p = *a //注意是个列指针哦

          int (*q) [N] = a //是一个指向N个元素的行指针             

实参形参
a[i][j]   【整形值】int a
*(a+i)+j  【a[i][j]的地址】 int *p
*(*(a+i)+j)【整型值】int a
a[i]+j    【a[i][j]的地址】int *p
p[i]      【第0行第i个元素地址】int *p
*p       【a[0][0]的值,整型值】int a
q[i][j]   【a[i][j]的值,整型值】int a
*q      【a[0][0]的地址,需要好好理解】int *p
q 或 a    【二维数组起始地址】  int (*p)[N]
p+3      【第0行第三个元素地址】int *p
q+2     【第二行地址】int (*p)[N]


2.7、指针函数、函数指针、函数指针数组

        1、指针函数:说到底是函数,函数的返回值是指针类型

            int * func(形参列表);

        2、函数指针:说到底是指针,指针指向的函数的入口地址。

            示例:

        

#include <stdlib.h>


int add (int a,int b)

{

    return a+b;

}


int sub (int a, int b)

{

    return a-b;

}


int main()

{

    int a=3,b=5;

    int val;

    //定义函数指针:含义是:定义一个指针,指向的是一个函数,通过指针名调用函数  

    int (*p) (int,int);

    // 注意第一个int 同要指向的函数的返回值类型,后两个int 同函数的形参列表。

    int (*q) (int ,int);

    p = add;

    // p = add 也可以写成 p = &add 是一样的。也就是说在函数指针的环节,可以省略 &取地址,* 取值

    q = sub;

    val = p(a,b);

    printf("add result is %d \n",val);

    val = q(a,b);

    printf("sub result is %d \n",val);

    exit(0);

}


   3、函数指针数组:首先是一个数组。数组的内容是一个函数指针。什么函数指针,就是一个指针,指向函数的所以总体说来是一个指向函数的指针组成的数组

    定义形式:

    类型  (* 数组名[下标]) (形参)

   int  (*funcp[2])  (int ,init);

    定义了一个名叫funcp的数组,大小为2. 2个元素的内容是指向返回值是int,有两个int形参的函数

    

              #include <stdio.h>

#include <stdlib.h>


int add (int a,int b)

{

    return a+b;

}


int sub (int a, int b)

{

    return a-b;

}


int main()

{

    int a=3,b=5;

    int val;

    //定义函数指针:含义是:定义一个指针,指向的是一个函数,通过指针名调用函数  

   

    int (*funcp[2]) (int ,int);

    funcp[0] = add;

    funcp[1] = sub;    

    val = funcp[0](a,b);

    printf("add result is %d \n",val);

    val = funcp[1](a,b);

    printf("sub result is %d \n",val);

    exit(0);

}

本文出自 “hylinux” 博客,请务必保留此出处http://hongyilinux.blog.51cto.com/8030513/1738485

指针详解

原文:http://hongyilinux.blog.51cto.com/8030513/1738485

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!