C语言指针用法

对于二维指针,称为指向指针的指针变量,**p也就是*(*p)。下面是一个例子

 

int a[5] = { 1, 3, 5, 7, 9 };
    int *num[5], i;
    int **p;
    for (i = 0; i < 5; i++) {
        num[i] = &a[i];
        printf("&a[%d]=%d,num[%d]=%d,&num[%d]=%d\n", i, &a[i], i, num[i], i,&num[i]);
    }
    p = num + 2;
    printf("p=%d,*p=%d,**p=%d", p, *p, **p);

 

结果是

&a[0]=2686756,num[0]=2686756,&num[0]=2686736
&a[1]=2686760,num[1]=2686760,&num[1]=2686740
&a[2]=2686764,num[2]=2686764,&num[2]=2686744
&a[3]=2686768,num[3]=2686768,&num[3]=2686748
&a[4]=2686772,num[4]=2686772,&num[4]=2686752
p=2686744,*p=2686764,**p=5

 其中,指针数组num存放的是int型数组中个元素的地址,所以num[i]==&a[i]。而且num[i]是指针数组,也有自己的一个地址。若

令p=num+2,其中p是指向指针的指针变量,则p中存放的就是num中元素的地址,也就是num[i]的地址。而*p就是num[i]中存放的数据,这个数据就是a[i]的地址。

所以**p就是a[i]的值。

 

下面这篇文章是转自cdsn上的一篇。

在C语言中, 允许用一个变量来存放指针,这种变量称为指针变量。因此, 一个指针变量的值就是某个内存单元的地址或称为某内存单元的指针。

为了避免混淆,我们约定:“指针”是指地址, 是常量,“指针变量”是指取值为地址的变量。 定义指针的目的是为了通过指针去访问内存单元。

凡是出现数组,函数的地方都可以用一个指针变量来表示, 只要该指针变量中赋予数组或函数的首地址即可。

  1. 指针变量的类型说明

对指针变量的类型说明包括三个内容:

  • 指针类型说明,即定义变量为一个指针变量;
  • 指针变量名;
  • 变量值(指针)所指向的变量的数据类型。

int *p1;表示p1是一个指针变量,它的值是某个整型变量的地址。

staic int *p2; /*p2是指向静态整型变量的指针变量*/

float *p3; /*p3是指向浮点变量的指针变量*/

char *p4; /*p4是指向字符变量的指针变量*/

应该注意的是,一个指针变量只能指向同类型的变量,如P3 只能指向浮点变量,不能时而指向一个浮点变量, 时而又指向一个字符变量。

  2.指针变量的赋值

(1)指针变量初始化的方法 int a;

int *p=&a;

(2)赋值语句的方法 int a;

  int *p;

  p=&a;
不允许把一个数赋予指针变量,故下面的赋值是错误的: int *p;p=1000; 被赋值的指针变量前不能再加“*”说明符,如写为*p=&a 也是错误的

3.指针变量的运算

  指针变量只能进行赋值运算和部分算术运算及关系运算。

  (1)取地址运算符&

  (2)取内容运算符*

    用来表示指针变量所指的变量,在*运算符之后跟的变量必须是指针变量。需要注意的是指针运算符*和指针变量说明中的指针说明符* 不是一回事。

  (3)指针变量的运算    

      (1)赋值运算

        指针变量的赋值运算有以下几种形式:

        ①指针变量初始化赋值,前面已作介绍。

        ②把一个变量的地址赋予指向相同数据类型的指针变量。例如:

int a,*pa;
pa=&a; /*把整型变量a的地址赋予整型指针变量pa*/ 

        ③把一个指针变量的值赋予指向相同类型变量的另一个指针变量。如:

int a,*pa=&a,*pb;
pb=pa; /*把a的地址赋予指针变量pb*/
/*由于pa,pb均为指向整型变量的指针变量,因此可以相互赋值。 

        ④把数组的首地址赋予指向数组的指针变量。

int a[5],*pa;
pa=a; /*数组名表示数组的首地址,故可赋予指向数组的指针变量pa*/
//也可写为:
pa=&a[0]; /*数组第一个元素的地址也是整个数组的首地址,
也可赋予pa*/
//当然也可采取初始化赋值的方法:
int a[5],*pa=a;

         ⑤把字符串的首地址赋予指向字符类型的指针变量。

char *pc;
pc="c language"; //或用初始化赋值的方法写为: char *pc="C Language";

 

            这里应说明的是并不是把整个字符串装入指针变量, 而是把存放该字符串的字符数组的首地址装入指针变量。 在后面还将详细介绍。

          ⑥把函数的入口地址赋予指向函数的指针变量。

int (*pf)();
pf=f; /*f为函数名*/

 

      (2)加减算术运算

          对于指向数组的指针变量,可以加上或减去一个整数n。设pa是指向数组a的指针变量,则pa+n,pa-n,pa++,++pa,pa--,--pa 运算都是合法

          的。指针变量加或减一个整数n的意义是把指针指向的当前位置(指向某数组元素)向前或向后移动n个位置。

          应该注意,数组指针变量向前或向后移动一个位置和地址加1或减1 在概念上是不同的。因为数组可以有不同的类型, 各种类型的数组元素

          所占的字节长度是不同的。如指针变量加1,即向后移动1 个位置表示指针变量指向下一个数据元素的首地址。而不是在原地址基础上加1。

nt a[5],*pa;
pa=a; /*pa指向数组a,也是指向a[0]*/
pa=pa+2; /*pa指向a[2],即pa的值为&pa[2]*/

 


          指针变量的加减运算只能对数组指针变量进行, 对指向其它类型变量的指针变量作加减运算是毫无意义的。

      (3)两个指针变量之间的运算只有指向同一数组的两个指针变量之间才能进行运算, 否则运算毫无意义。

        ①两指针变量相减

           两指针变量相减所得之差是两个指针所指数组元素之间相差的元素个数。实际上是两个指针值(地址) 相减之差再除以该数组元素的长度

           (字节数)。例如pf1和pf2 是指向同一浮点数组的两个指针变量,设pf1的值为2010H,pf2的值为2000H,而浮点数组每个元素占4个字节,

           所以pf1-pf2的结果为(2000H-2010H)/4=4,表示pf1和 pf2之间相差4个元素。两个指针变量不能进行加法运算。 例如,pf1+pf2是什么意

           思呢?毫无实际意义。

        ②两指针变量进行关系运算

           指向同一数组的两指针变量进行关系运算可表示它们所指数组元素之间的关系。

            pf1==pf2表示pf1和pf2指向同一数组元素

            pf1>pf2表示pf1处于高地址位置

            pf1<pf2表示pf2处于低地址位置

        注:指针变量还可以与0比较。设p为指针变量,则p==0表明p是空指针,它不指向任何变量;p!=0表示p不是空指针。指针变量未赋值时,可以

          是任意值,是不能使用的。否则将造成意外错误。而指针变量赋0值后,则可以使用,只是它不指向具体的变量而已。

  4.数组指针变量的说明和使用

int a[5],*pa;
pa=a;

 

      pa,a,&a[0]均指向同一单元,它们是数组a的首地址,也是0 号元素a[0]的首地址。pa+1,a+1,&a[1]均指向1号元素a[1]。类推可知a+i,a+i,&a[i]指向i号

      元素a[i]。应该说明的是pa是变量,而a,&a[i]都是常量。引入指针变量后,就可以用两种方法来访问数组元素了。

   第一种方法为下标法,即用a[i]形式访问数组元素。

   第二种方法为指针法,即采用*(pa+i)形式,用间接访问的方法来访问数组元素。

int a[5], i, *pa;
    pa = a;
//将变量i的值赋给由指针pa指向的a[]的数组单元
for (i = 0; i < 5; i++) { *pa = i; pa++; } pa = a; for (i = 0; i < 5; i++) { printf("a[%d]=%d\n", i, *pa); pa++; }

 

  5.数组名和数组指针变量作函数参数

float aver(float *pa);
main() {
    float sco[5], av, *sp;
    int i;
    sp = sco;
    printf("\ninput 5 scores:\n");
    fflush(stdout);
    for (i = 0; i < 5; i++)
        scanf("%f", &sco[i]);
    av = aver(sp);
    printf("average score is %5.2f", av);
}
float aver(float *pa) {
    int i;
    float av, s = 0;
    for (i = 0; i < 5; i++)
        s = s + *pa++;
    av = s / 5;
    return av;
}

 

 

  6.指向多维数组的指针变量

    C语言允许把一个二维数组分解为多个一维数组来处理。因此数组a[3][4]可分解为三个一维数组,即a[0],a[1],a[2]。每一个一维数组又含有四个元素。

    例如a[0]数组,含有a[0][0],a[0][1],a[0][2],a[0][3]四个元素。数组及数组元素的地址表示如下:a是二维数组名,也是二维数组0行的首地址,等于

    1000。a[0]是第一个一维数组的数组名和首地址,因此也为1000。*(a+0)或*a是与a[0]等效的, 它表示一维数组a[0]0 号元素的首地址。 也为1000。

    &a[0][0]是二维数组a的0行0列元素首地址,同样是1000。因此,a,a[0],*(a+0),*a,&a[0][0]是相等的。同理,a+1是二维数组1行的首地址,等于

    1008。a[1]是第二个一维数组的数组名和首地址,因此也为1008。&a[1][0]是二维数组a的1行0列元素地址,也是1008。因此a+1,a[1],*(a+1),&a[1][0]是等

    同的。 由此可得出:a+i,a[i],*(a+i),&a[i][0]是等同的。 此外,&a[i]和a[i]也是等同的。

    因为在二维数组中不能把&a[i]理解为元素a[i]的地址,不存在元素a[i]。

    a[i]+j是一维数组a[i]的j号元素首地址,它等于&a[i][j]。由a[i]=*(a+i)得a[i]+j=*(a+i)+j,由于*(a+i)+j是二维数组a的i行j列元素的首地址

    该元素的值等于*(*(a+i)+j)。

    

#define PF "%d,%d,%d,%d,%d,/n"
main() {
    static int a[3][4]= {0,1,2,3,4,5,6,7,8,9,10,11};
    printf(PF,a,*a,a[0],&a[0],&a[0][0]);
    printf(PF,a+1,*(a+1),a[1],&a[1],&a[1][0]);
    printf(PF,a+2,*(a+2),a[2],&a[2],&a[2][0]);
    printf("%d,%d\n",a[1]+1,*(a+1)+1);
    printf("%d,%d\n",*(a[1]+1),*(*(a+1)+1));
} 
    1. 把二维数组a 分解为一维数组a[0],a[1],a[2]之后,设p为指向二维数组的指针变量。可定义为: int (*p)[4] 它表示p是一个指针变量,它指向二维数组a 或指向第一个一维数组a[0],其值等于a,a[0],或&a[0][0]等。

       而p+i则指向一维数组a[i]。从前面的分析可得出*(p+i)+j是二维数组i行j 列的元素的地址,而*(*(p+i)+j)则是i行j列元素的值。

      2. 二维数组指针变量说明的一般形式为: 类型说明符 (*指针变量名)[长度] 其中“类型说明符”为所指数组的数据类型。“*”表示其后的变量是指针类

        型。 “长度”表示二维数组分解为多个一维数组时, 一维数组的长度,也就是二维数组的列数。 

      

main() {
    static int a[3][4]= {0,1,2,3,4,5,6,7,8,9,10,11};
    int(*p)[4];
    int i,j;
    p=a;
    for(i=0;i<3;i++)
    for(j=0;j<4;j++) printf("%2d ",*(*(p+i)+j));
}

 

main() {
    char *ps = "this is a book";
    int n = 10;
    ps = ps + n;
    printf("%s\n", ps);
}
/*运行结果为:book
在程序中对ps初始化时,即把字符串首地址赋予ps,当ps= ps+10之后,ps指向字符“b”,因此输出为"book"。
*/

   7.使用字符串指针变量与字符数组的区别

      1.字符串指针变量本身是一个变量,用于存放字符串的首地址。而字符串本身是存放在以该首地址为首的一块连续的内存空间中并以‘/0’作为串的结

       束。字符数组是由于若干个数组元素组成的,它可用来存放整个字符串。

      2. 对字符数组作初始化赋值,必须采用外部类型或静态类型,如: static char st[]={“C Language”};

       而对字符串指针变量则无此限制,如: char *ps="C Language";

      3. 对字符串指针方式 char *ps="C Language";可以写为: char *ps; ps="C Language";而对数组方式:

        static char st[]={"C Language"};

        不能写为:

          char st[20];st={"C Language"};而只能对字符数组的各元素逐个赋值。

  8.函数指针变量

      函数指针变量定义的一般形式为:类型说明符 (*指针变量名)();例如: int (*pf)();

int max(int a, int b) {
    if (a > b)
        return a;
    else
        return b;
}
main() {
    int max(int a, int b);
    int (*pmax)();
    int x, y, z;
    pmax = max;
    printf("input two numbers:\n");
    fflush(stdout);
    scanf("%d%d", &x, &y);
    z = (*pmax)(x, y);
    printf("maxmum=%d", z);
}

        从上述程序可以看出用,函数指针变量形式调用函数的步骤如下:

      1. 先定义函数指针变量,如上程序中第9行 int (*pmax)();定义pmax为函数指针变量。
      2. 把被调函数的入口地址(函数名)赋予该函数指针变量,如程序中第11行 pmax=max;
      3. 用函数指针变量形式调用函数,如程序第15行 z=(*pmax)(x,y); 调用函数的一般形式为: (*指针变量名) (实参表)

使用函数指针变量还应注意以下两点:

    1. 函数指针变量不能进行算术运算,这是与数组指针变量不同的。数组指针变量加减一个整数可使指针移动指向后面或前面的数组元素,而函数指针的移动是毫无意义的。
    2. 函数调用中"(*指针变量名)"的两边的括号不可少,其中的*不应该理解为求值运算,在此处它只是一种表示符号

9.指针型函数

  函数名之前加了“*”号表明这是一个指针型函数,即返回值是一个指针。

  注:int(*p)()int *p()是两个完全不同的量。int(*p)()是一个变量说明,说明p 是一个指向函数入口的指针变量,该函数的返回值是整型量。

    int *p() 则不是变量说明而是函数说明,说明p是一个指针型函数,其返回值是一个指向整型量的指针,*p两边没有括号

main() {
    int i;
    char *day_name(int n);
    printf("input Day No:\n");
    scanf("%d", &i);
    if (i < 0)
        exit(1);
    printf("Day No:%2d-->%s\n", i, day_name(i));
}
char *day_name(int n) {
    static char *name[] = { "Illegal day", "Monday", "Tuesday", "Wednesday",
            "Thursday", "Friday", "Saturday", "Sunday" };
    return ((n < 1 || n > 7) ? name[0] : name[n]);
}

 

       本程序是通过指针函数,输入一个1~7之间的整数, 输出对应的星期名。

      指针数组是一组有序的指针的集合。 指针数组的所有元素都必须是具有相同存储类型和指向相同数据类型的指针变量。

  10.指针数组

    指针数组说明的一般形式为: 类型说明符  *数组名[数组长度]

     int *pa[3] 表示pa是一个指针数组,它有三个数组元素, 每个元素值都是一个指针,指向整型变量。

  11.小结

    1. 指针是C语言中一个重要的组成部分,使用指针编程有以下优点:

      (1)提高程序的编译效率和执行速度。

      (2)通过指针可使用主调函数和被调函数之间共享变量或数据结构,便于实现双向数据通讯。

      (3)可以实现动态的存储分配。

      (4)便于表示各种数据结构,编写高质量的程序。

    2. 指针的运算

      (1)取地址运算符&:求变量的地址

      (2)取内容运算符*:表示指针所指的变量

      (3)赋值运算

        ·把变量地址赋予指针变量

        ·同类型指针变量相互赋值

        ·把数组,字符串的首地址赋予指针变量

        ·把函数入口地址赋予指针变量

      (4)加减运算

        对指向数组,字符串的指针变量可以进行加减运算,如p+n,p-n,p++,p--等。对指向同一数组的两个指针变量可以相减。对指向其它类型的指针变

        量作加减运算是无意义的。

      (5)关系运算

        指向同一数组的两个指针变量之间可以进行大于、小于、 等于比较运算。指针可与0比较,p==0表示p为空指针。

    3. 与指针有关的各种说明和意义见下表。

      int *p;     p为指向整型量的指针变量

      int *p[n];   p为指针数组,由n个指向整型量的指针元素组成。

      int (*p)[n];  p为指向整型二维数组的指针变量,二维数组的列数为n

      int *p()    p为返回指针值的函数,该指针指向整型量

      int (*p)()   p为指向函数的指针,该函数返回整型量

      int **p     p为一个指向另一指针的指针变量,该指针指向一个整型量。

    4. 有关指针的说明很多是由指针,数组,函数说明组合而成的。

      但并不是可以任意组合,例如数组不能由函数组成,即数组元素不能是一个函数;函数也不能返回一个数组或返回另一个函数。例如

      int a[5]();就是错误的。

    5. 关于括号

      在解释组合说明符时, 标识符右边的方括号和圆括号优先于标识符左边的“*”号,而方括号和圆括号以相同的优先级从左到右结合。但可以用圆括号

      改变约定的结合顺序。

    6. 阅读组合说明符的规则是“从里向外”。

        从标识符开始,先看它右边有无方括号或园括号,如有则先作出解释,再看左边有无*号。 如果在任何时候遇到了闭括号,则在继续之前必须用

        相同的规则处理括号内的内容。

        例如:

        int*(*(*a)())[10]

        ↑ ↑↑↑↑↑↑

        7 6 4 2 1 3 5

      上面给出了由内向外的阅读顺序,下面来解释它:

      (1)标识符a被说明为;

      (2)一个指针变量,它指向;

      (3)一个函数,它返回;

      (4)一个指针,该指针指向;

      (5)一个有10个元素的数组,其类型为;

      (6)指针型,它指向;

      (7)int型数据。

      因此a是一个函数指针变量,该函数返回的一个指针值又指向一个指针数组,该指针数组的元素指向整型量。

 

 

 

 

 

 

 

 

 

 

 

 

转载于:https://www.cnblogs.com/ruiqingliu/p/7356814.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值