深入理解计算机系统之四 -- 指针

臭大佬 2021-03-15 23:30:29 370
linux 
简介 C语言里,变量存放在内存中,而内存其实就是一组有序字节组成的数组,每个字节有唯一的内存地址。指针是一种保存变量地址的变量。

C语言里,变量存放在内存中,而内存其实就是一组有序字节组成的数组,每个字节有唯一的内存地址。指针是一种保存变量地址的变量。

指针声明

每个指针都对应一个类型。这个类型表明该指针指向的是哪一类对象 。以下面的指针声明为例:

int  *ip
char **cpp

变量 ip 是一个指向 int类型对象的指针,而 cpp指针指向的对象自身就是一个指向char类型对象的指针。通常 ,如果对象类型为T,那么指针的类型为: T*。特殊的void*类型代表通用指针。

NULL指针

每个指针都有一个值。这个值是某个指定类型的对象的地址。特殊的NULL(O) 值表示该指针没有指向任何地方。

#include "stdio.h"

int main()
{
    int *p = NULL;
    printf("p的地址为%p\n", p);

    return 0;
}

// p的地址为(nil)

数组与指针

对于数据类型 T 和整型常数 N, 声明如下,

T A[N]

起始位置表示为Xa, 这个声明有两个效果。首先,它在内存中分配一个L*N字节的连续区域,这里L 是数据类型: T的大小(单位为字节 )。其次,它引人了标识符A,可以用A来作为指向数组开头的指针,这个指针的值就是Xa,可以用0~ N-1的整数索引来访问该数组元素。数组元素i会被存放在地址为为Xa+L*i的地方。

单操作数操作符 ‘&’ 和 ‘*’ 可以产生指针和间接引用指针。也就是,对于一个表示某个对象的表达式 Expr&Expr是给出该对象地址的一个指针。对于一个表示地址的表达式 AExpr*AExpr 给出该地址处的值。因此,表达式Expr*&Expr是 等价的 。可以对数组和指针应用数组下标操作。数组引用 A[i] 等同于表达式* (A+i)。 它计算第个数组元素的地址,然后访问这个内存位置。

数组与指针紧密联系。一个数组的名字可以像一个指针变量一样引用(但是不能修改)。数组引用(例如 a[3]) 与指针运算和间接引用(例如 * (a+ 3)) 有一样的效果。数组引用和指针运算都需要用对象大小对偏移量进行伸缩 。当我们写表达式 p+ i,这里指针P的值为p,得到的地址计算为P +L*i,这里L 是与 p 相关联的数据类型的大小。

#include "stdio.h"
int main()
{
    // 声明一个int类型的数组,这个数组有10个元素
    int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
    // 声明int类型的指针变量
    int *p;
    int *p1;
    int *p2;
    // 声明一个int类型的变量
    int sub;
    // 对指针进行初始化,p将指向数组 a 的第 1 个元素 a[0] 
    p = &a[0];
    // 数组类型的变量或表达式的值是该数组第 1 个元素的地址,且数组名所代表的的就是该数组第 1 个元素的地址,所以:p = a;   
    printf("a的地址为:%p\n", a);
    printf("a[0]的地址为:%p\n", &a[0]);
    printf("p的地址为:%p\n", &p);
    // 打印指针 p 的地址,并不是指针所指向的地方的地址
    // a的地址为:0x7ffe2ad296d0
    // a[0]的地址为:0x7ffe2ad296d0
    // p的地址为:0x7ffe2ad296b8
    p1 = &a[2];
    p2 = &a[8];
    sub = p2 - p1;
    printf("sub=%d\n", sub);
    // sub=6
    p += 2;
    printf("*(p+2)的值为:%d\n", *p);
    // *(p+2)的值为:3
    // 输出结果为 3,*(p+2)指向了 x[2]
    return 0;
}

将指针从一种类型强制转换成另一种类型,只改变它的类型,而不改变它的值。强制类型转换的一个效果是改变指针运算的伸缩 。例如,如果P是一个 char*类型的指针,它的值为p,那么表达式 (int*)(p+7)计算为p+7。(强制类型转换的优先级高于加法。)

指针与函数

指针也可以指向函数 。这提供了一个很强大的存储和向代码传递引用的功能,这些引用可以被程序的某个其他部分调用。

#include "stdio.h"
// 参数为普通的 int 变量
void swap1(int a, int b)
{
    int temp;
    temp = a;
    a = b;
    b = temp;
}
// 参数为指针,接受调用函数传递过来的变量地址作为参数,对所指地址处的内容进行操作
void swap2(int *a, int *b)
{
    // 最终结果是,地址本身并没有改变,但是这一地址所对应的内存段中的内容发生了变化,即x,y的值发生了变化
    int temp;
    temp = *a;
    *a = *b;
    *b = temp;
}
int main()
{
    int x = 1, y = 2;
    swap1(x, y);              // 将 x,y 的值本身作为参数传递给了被调函数
    printf("%d %5d\n", x, y); // 输出结果为:1     2
    swap2(&x, &y);            // 将 x,y 的地址作为参数传递给了被调函数,传递过去的也是一个值,与传值调用不冲突
    printf("%d %5d\n", x, y); // 输出结果为:2     1
    return 0;
}

部分参考:https://www.cnblogs.com/tongye/p/9650573.html