跳转至

指针

  • 变量与地址:指针就是地址,变量是某一块空间的抽象表示。指针变量保存了地址值;指针变量的类型描述了其读取或偏移的空间大小。

  • 指针与指针变量:指针可以理解为一个地址;指针变量就是存放一个地址的变量。

  • 指针与字符数组:使用时注意操作的是指针变量还是字符串常量。

  • 空指针与野指针:野指针,当前指针指向的内容是不确定的。空指针,int* p = NULL;

  • 空类型:void 可以赋给任意类型的指针,任意类型的指针也可以赋值给 void;在不确定需要什么样的数据类型时可以使用该类型;特殊情况void* 和函数指针(指针函数?)的相互赋值未定义。

  • 指针运算:&(取地址)、*(取值)、关系运算(比较连续地址的高低)、+ -(地址偏移,+1指的是+1个所指类型的大小)

  • 指针的类型决定了偏移读取的空间大小

直接访问与间接访问:

int i = 10;
int* p = &i;    // int*  类型变量可以保存 int  型变量的地址
int** q = &p;   // int** 类型变量可以保存 int* 型变量的地址

不同类型指针的意义

// 对于所有的指针类型变量的大小都是固定的 8 (当前测试环境,非所有环境)

char* c;
int* n;
double* d;
int** p;
double** q;

printf("%zu\n", sizeof(c));
printf("%zu\n", sizeof(n));
printf("%zu\n", sizeof(d));
printf("%zu\n", sizeof(p));
printf("%zu\n", sizeof(q));

/*
    --- 执行结果 ---
8
8
8
8
8
*/

// 不同类型指针的意义在于取出保存地址值的区别
// int* p;      *p 时将读取一个 int 的大小;
// double* q;   *q 时将读取一个 double 的大小
// char* c;     *c 时将读取一个 char 的大小;
// 以此类推

指针与一维数组

// arr是地址常量,不可以进行自增自减运算
int arr[3] = { 1, 2, 3 };

// p是指针变量,可以自增自减运算
int* p = arr;

p += 1; // 地址发生偏移,p指向改变
p + 1;  // p本身指向没变

指针与二维数组,数组指针

#define N 2

int main(void)
{
    int i = 0, j = 0;
    int arr[N][N] = { 1, 2, 3 };
    int(*p)[N] = arr;               // 数组指针,每次偏移[N]

    printf("%p\n", p);              // 指针类型为数组,偏移一个一维数组
    printf("%p\n", p + 1);       

    printf("%p\n", *p);             // 指针类型为int,偏移一个int
    printf("%p\n", (*p) + 1);

    for (i = 0; i < N; i += 1)
    {
        for (j = 0; j < N; j += 1)
        {
            // 调整指针类型,然后偏移取值
            printf("%p -> %d\n", *(p + i) + j, *(*(p + i) + j));
        }
    }
    return 0;
}

const与指针

int n = 10;
int const* p = &n;      // 常量指针;当前const 限定的是 *p;p可变,n可变
const int *p;           // 限定了 int * 即限定了*p不可变。

int* const p = &n;      // 指针常量;当前const 限定的是 p;*p可变,n可变;
int* const p;           // 限定了变量p,变量p的值不可变。

int const *const p;     // 即是常量指针,也是指针常量;限定 *p 和 p;n可变;
const int* const p;     // 两个都限定。

指针数组与数组指针

int(*p)[N];     // 数组指针,标识每次偏移一个一维数组的空间
int* arr[N];    // 数组指针,存储指针的数组