C语言中的指针

指针

什么是指针

指针是一个值为内存地址的变量(或数据对象),也就是说,指针变量的值是地址
假设一个指针变量名是ptr,可以编写如下语句:

ptr = &pooh; //把pooh的地址赋给ptr

对于这条语句,我们说ptr **“指向”**pooh。ptr是变量(指针),&pooh是常量(地址),所以还可以把ptr指向别处:

ptr = &bah; //把ptr指向bah,而不是pooh  

现在ptr的值是bah的地址。
要创建指针变量(例如ptr),先要声明指针变量的类型。需要用到间接运算符:*。

间接运算符:*

假设已知ptr指向bah,即:

ptr = &bah;

使用间接运算符 * 找出储存在bah中的值,该运算符有时也称作解引用运算符

val = *ptr; //找出ptr指向的值  

语句ptr = &bah;和val = *ptr;放在一起相当于:

val = bah;  

  • 小结:与指针相关的运算符
    地址运算符:&
    后跟一个变量名时,&给出该变量的地址。
    地址运算符:*
    后跟一个指针名或地址是,*给出储存在指针指向地址上的值。

声明指针

声明指针变量时必须指定指针所指向变量的类型:

    int * pi; //pi是指向int类型的变量的指针  
    int * pc; //pc是指向char类型的指针

类型说明符表明了指针所指向对象的类型,星号(*)表明声明的变量是一个指针。int * pi;声明的意思是pi是一个指针,* pi是int类型。

声明并使用指针

指针的类型:
pc指向的值(*pc)是char类型,我们描述pc的类型是指向char类型的指针。pc的只是一个地址。

数组中的指针

数组名是数组首元素的地址。也就是说,如果flizny是一个数组,下面的语句成立:

    flizny == &flizny[0]; //数组名是该数组首元素的地址  

*注意:**flizny 和 &flizny[0]都表示数组首元素的内存地址,它们都是常量* ,在程序的运行过程中不会改变。但是,可以把他们赋值给指针变量,然后可以修改指针变量的值。

数组和指针加法

  • 指针的值是它所指向对象的地址。许多计算机一般都是按字节编址。这里,一个较大对象(如short类型)的地址一般是该对象第一个字节的地址。
  • 在指针前面使用*运算符可以得到该指针所指向对象的值。
  • 指针加1,指针的值递增它所指向类型的大小。

    dates + 2 == &dates[2]  //相同的地址
    *(dates + 2) == dates[2]  //相同的值  
    

       以上关系表明了数组和指针的关系十分密切,可以使用指针标识数组的元素和获得元素的值。从本质上看,同一个对象有两种表示法。实际上,C语言标准在描述数组表示法时确实借助了指针。也就是说,定义ar[n]的意思是*(ar + n)。可以认为*(ar + n)的意思是“到内存的ar位置,然后移动n个单元,检索储存在那里的值”。

函数、数组和指针

       假设要编写一个处理数组的函数,该函数返回数组中所有元素之和,待处理的是名为marbles的int类型数组。应该如何调用函数?也许是下面这样:

    total = sum(marbles); //可能的函数调用  

       那么,该函数的原型是什么?数组名是该数组首元素的地址,所以实际参数marbles是一个储存int类型值的地址,应该把它赋给一个指针形式参数,即该形参是一个指向int的指针:

    int sum(int * ar); //对应的函数原型  

       sum()从该参数中获得了什么信息?它获得了该数组首元素的地址,知道要在该位置上找出一个整数。

使用指针形参

指针形参是变量,这意味着可以用索引表明访问数组中的拿一个元素。

    #include <stdio.h>
    #define SIZE 10
    int sump(int * start, int * end);
    int main(void)
    {
        int marbles[SIZE] = {20,10,5,39,4,16,19,26,31,20};
        long answer;

        answer = sump(marbles, marbles + SIZE);
        printf("The total number of marbles is %ld.\n", answer);

        return 0;
    }  
    int sump(int * start, int * end)  
    {
        int total = 0;

        while (start < end)
        {
            total += *start; // add value to total
            start++;         // advance pointer to next element
        }

        return total;
    }  

形参、实参:

        int sump(int * start, int * end);告诉编译器sump()使用两个参数start和end都是指向int类型的指针。这两个变量属于函数私有,是局部变量。每次调用函数,就会给这些变量赋值,也就是说当main()函数中的nswer = sump(marbles, marbles + SIZE);执行时,同时执行了start = marbles;和 end = (marbles + SIZE);实际参数提供了start和end的值
       简而言之,形式参数是被调函数中的变量,实际参数是主调函数赋给被调函数的具体值,有一个赋值的过程而不是单纯的替换。

指针表示法和数组表示法

       对于C语言,ar[i]和*(ar + i)这两个表达式都是等价的。无论ar是数组名还是指针变量,这两个表达式都没问题。但是,只有当ar是指针变量时,才能使用ar++这样的表达式。

指针和多维数组

假设有下面的声明:

    int zippo[4][2]; //内含int数组的数组  

数组名zippo是该数组首元素的地址。zippo的首元素是一个内含**两个**int值的数组,所以zippo是这个内含两个int值的数组的地址。

  • 因为zippo是数组首元素的地址,所以zippo的值和&zippo[0]的值相同。而zippo[0]本身是一个内含两个整数的数组,所以zippo[0]的值和它首元素(一个整数)的地址(即&zippo[0][0]的值)相同。简而言之,zippo[0]是一个占用一个int大小对象的地址,而zippo是一个占用两个int大小对象的地址。由于这个整数和内含内含两个整数的数组都开始于同一个地址,所以zippo和zippo[0]的值相同。
  • 给指针或地址加1,其值会增加对应类型大小的数值。在这方面,zippo和zippo[0]不同,因为zippo指向的对象占用了两个int大小,而zippo[0]指向的对象只占用了一个int大小。因此zippo + 1和zippo[0] + 1的值不同。
  • 解引用一个指针(在指针前使用*运算符)或在数组名后使用带下标的[]运算符,得到解引用对象代表的值。因为zippo[0]是该数组的首元素(zippo[0][0])的地址,所以*(zippo[0])表示储存在zippo[0][0]上的值(即一个int类型的值)。与此类似,*zippo代表首元素(zippo[0])的值,但是zippo[0]本身是一个int类型值的地址。该值地址是&zippo[0][0],所以*zippo就是&zippo[0][0],对两个表达式应用解引用运算符表明,**zippo与*&zippo[0][0]等价,这相当于zippo[0][0],即一个int类型的值。简而言之zippo是地址的地址,必须解引用两次才能获得原始值。地址的地址或指针的指针就是双重间接的例子。

数组的数组

指向多维数组的指针

       如何声明一个指针变量pz指向一个二维数组(如zippo)?把指针声明为int类型还不够。因为指向int只能与zippo[0]的类型匹配,说明该指针指向一个int类型的值。但是zippo是它首元素的地址,该元素是一个内含两个int类型值的一维数组。因此,pz必须指向一个内含两个int类型值的数组,而不是指向一个int类型值,其声明如下:

    int (* pz)[2];  //pz指向一个内含两个int类型值的数组  

以上代码把pz声明为指向一个数组的指针,该数组内含两个int类型值。
如前所述,虽然pz是一个指针,不是数组名,但是也可以使用pz[2][1]这样的写法。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值