C语言中的指针

作者: 刚刚悟道 | 来源:发表于2016-11-09 14:18 被阅读32次

    指针概念

    指针是一个变量,该变量的数值是地址,或者说,指针是一个数据对象。

    类似于,int 类型变量的数值是整数。

    与指针相关的运算符

    间接运算符:*

    ptr 指向 bath,ptr = &bath

    获取 bath 中存放的数值,val = * ptr

    上面两句语句等价于 val = bath

    地址运算符:&

    后跟一个变量名时,& 给出该变量的地址。

    指针声明

    int * pi;
    

    int 表明被指向的变量的类型是整型,* 表示该变量是一个指针。pi 所指向的值(*pi)int 类型,pi
    类型是“指向 int 的指针”。

    * 和指针名之间的空格是可选的。

    指针的输出格式是 %p

    使用指针在函数间通信

    结合 PHP 中函数的引用赋值来理解。

    变量的值在函数中改变还是全局改变。

    指针和数组

    概念

    在 C 中,对一个指针加 1 的结果是对该指针增加 1 个存储单元(storage unit)。对数组而言,地址会增加到下一个元素的地址,而
    不是下一个字节。归纳如下:

    1. 指针的数值就是它所指向的对象的地址。对于包含多个字节的数据类型,对象的地址通常是指其首字节的地址。

    2. 在指针前运用运算符 * 可以得到该指针所指向的对象的数值。

    3. 对指针加1,等价于对指针的值加上它所指向的对象的字节大小。

    函数、数组和指针

    声明数组参量

    下面的四种原型都是等价的

    int sum(int *ar, int n);
    int sum(int *, int);
    int sum(int ar[], int n);
    int sum(int [], int);
    

    定义函数时,下面两种形式等价

    int sum(int * ar, int n)
    {
    }
    
    int sum(int ar[], int n)
    {
    }
    

    声明形式参量时,int *arint ar[] 都表示 ar 是指向 int 的指针。

    sizeof 求变量、指针、数组大小。

    使用指针参数

    使用数组形参的函数需要知道数组的起点和终点。告知终点有两种方法,一种是直接使用整数参量指明数组元素的个数,一种是用指针
    指明数组的结束地址。比如,

    int sum(int * start, int * end);
    

    若数组的元素个数是 SIZE,那么,* end 的值是 &start + SIZE(表示数组的最后一个元素后面的下一个元素)。

    一元运算符 *++ 具有相等的优先级别,但它在结合时是从右向左进行的。

    指针操作

    指针基本操作

    1. 赋值(assignment)。

    通常使用数组名或地址运算符&进行地址赋值。地址应该和指针类型兼容。

    1. 求职(value-finding)或取值(dereferencing)。

    2. 取指针地址。

    3. 将一个整数加给指针。

    4. 增加指针的值。

    5. 从指针中减去一个整数。

    6. 减小指针的值。

    7. 求差值(Differencing)。指向同一个数组内两个元素的指针使用此运算。

    8. 比较。两个指针具有相同的类型。

    对未初始化的指针取值

    不能对未初始化的指针取值。例如

    int * pt;   // 未初始化的指针
    *pt = 5;
    

    合法的代码

    int i = 5;
    int * pt = &i;
    

    或者

    double * ptd;
    ptd = (double)malloc(30 * sizeof(double));
    

    指针和多维数组

    例程

    #include <stdio.h>
    int main(void)
    {
        int zippo[4][2] = {{2, 4}, {6, 8}, {1, 3}, {5, 7}};
    
        printf("    zippo = %p,    zippo + 1 = %p\n",
               zippo,    zippo + 1);
        printf("zippo[0] = %p, zippo[0] + 1 = %p\n",
               zippo[0],    zippo[0] + 1);
        printf("  *zippo = %p,   *zippo + 1 = %p\n",
               *zippo, *zippo + 1);
        printf("zippo[0][0] = %d\n", zippo[0][0]);
        printf(" *zippo[0] = %d\n", *zippo[0]);
        printf("   **zippo = %d\n", **zippo);
        printf("    zippo[2][1] = %d\n", zippo[2][1]);
        printf("*(*(zippo+2) + 1) = %d\n", *(*(zippo+2) + 1));
    
        return 0;
    }
    

    代码见 E:\code\c\c_primer_plus_c\10_15_zippo1.c

    这段代码中的疑点:*zippo[0]**zippo

    声明指向二维数组的指针变量

    正确的代码

    int (* pz)[2];  // 声明一个指向二维数组pz[n][2]的指针 
    

    错误的代码

    int * pax[2];   // 创建一个两个指针的数组
    

    指针兼容

    int n = 5;
    double x;
    int * pi = &n;
    double * pd = &x;
    x = n;  // 隐藏的类型转换
    pd = pi;    // 编译时错误
    

    假如有如下声明:

    int * pt;
    int (*pa)[3];
    int ar1[2][3];
    int ar2[3][2];
    int **p2;   // 指向指针的指针
    

    那么,有如下结论:

    pt = &ar1[0][0];    // 都指向int
    pt = ar1[0];        // 都指向int 
    pt = ar1;   // 非法
    pa = ar1;   // 都指向int[3]
    pa = ar2;   // 非法
    p2 = &pt;   // 都指向 int *
    *p2 = ar2[0];   // 都指向int。不理解
    p2 = ar2;   // 非法。不理解
    

    保护数组内容

    对形参使用 const

    如果不打算在函数中修改数组,在函数原型和函数定义中对参数使用 const 可以达到目的。例程如下:

    int sum(const int ar[], int n);
    
    int sum(const int ar[], int n)
    {
        int i;
        int total = 0;
        
        for(i = 0; i < n; i++)
            total += ar[i];
            
        return total;
    }
    

    使用了 const,在函数中试图修改使用了 const 的参数时,编译时会发现此错误。

    有关 const 的其他内容

    使用 const 创建符号常量。

    const double PI = 3.14159;
    

    指向常量的指针不能用于修改数值,但可以指向其他地址。

    double rates[5] = {88.99, 100.12, 59.45, 183.11, 340.5};
    const double * pd = rates;      // pd指向数组开始处
    
    * pd = 29.89;   // 不允许
    pd[2] = 222.22;     // 不允许 
    rates[0] = 99.99;   // 允许,因为 rates 不是常量
    pd++;   // 让pd指向rates[1],允许
    

    将常量或非常量数据的地址赋给指向常量的指针是合法的。

    double rates[5] = {88.99, 100.2, 59.45, 183.11, 340.5};
    const double locked[4] = {0.08, 0.075, 0.0725, 0.07};
    const double * pc = rates;  // 合法 
    pc = locked;    // 合法 
    pc = &rates[3]; // 合法 
    

    只有非常量数据的地址才可以赋给普通的指针。

    double rates[5] = {88.99, 100.2, 59.45, 183.11, 340.5};
    const double locked[4] = {0.08, 0.075, 0.0725, 0.07};
    
    double * pnc = rates;   // 合法 
    pnc = locked;   // 非法 
    pnc = &rates[3];    // 合法 
    

    使用 const 保证指针不会指向别处。

    double rates[5] = {88.99, 100.2, 59.45, 183.11, 340.5};
    double * const pc = rates;  // 指针指向数组的开始处
    pc = &rates[2];     // 不允许 
    *pc = 92.99;        // 可以 
    

    使用 const 禁止修改所指向的数据。

    double rates[5] = {88.99, 100.2, 59.45, 183.11, 340.5};
    const double * const pc = rates;    // 指针指向数组的开始处
    pc = &rates[2];     // 不允许 
    *pc = 92.99;        // 不允许  
    

    参考资料

    《C Primer Plus(第五版)中文版》

    1. 指针简介: P236-9.7
    2. 指针和数组: P254-10.3
    3. 函数、数组和指针: P256-10.4
    4. 指针操作: P260-10.5
    5. 指针和多维数组: P267-10.7
    6. 保护数组内容: P263-10.6

    相关文章

      网友评论

        本文标题:C语言中的指针

        本文链接:https://www.haomeiwen.com/subject/sszzuttx.html