Skip to content

指针基本语法

概述

  • 内存地址

    • 在计算机内存中,每个存储单元都有一个唯一的地址 (内存编号)。

      • 通俗理解,内存就是房间,地址就是门牌号

  • 指针和指针变量

    • 指针(Pointer)是一种特殊的变量类型,它用于存储内存地址。

      • 指针的实质就是内存“地址”
    • 指针变量就是存储这个地址的变量。

  • 指针作用

    可间接修改变量的值

指针变量的定义和使用

  • 指针也是一种数据类型,指针变量也是一种变量
  • 指针变量指向谁,就把谁的地址赋值给指针变量

语法格式:

c
类型 变量;
类型 * 指针变量 = &变量;
  • &取址符,返回操作数的内存地址

  • 在定义指针变量时:

    1. * 号表示所声明的变量为指针类型
    2. 指针变量要保存某个变量的地址
    3. 指针变量的类型比这个变量的类型多一个*
  • 在使用指针变量时:

    1. *解引用,也叫 取值符,可以操作(读取、修改)指针所指向的变量的值
    2. * 号可以用来操作指针所指向的内存空间

示例代码:

c
#include <stdio.h>

int main() {

    // 定义一个 int 类型的变量,同时赋值为 10
    int a = 10;
    // 打印变量的地址
    printf("&a = %p\n", &a);
    // 定义一个指针变量,int *保存 int 的地址
    // int *代表是一种数据类型,int *指针类型,p 才是变量名
    int* p;
    // 指针指向谁,就把谁的地址赋值给这个指针变量
    p = &a;
    // 打印 p, *p, p 指向了 a 的地址,*p 就是 a 的值
    printf("p = %p, *p = %d\n", p, *p);

    return 0;
}

通过指针间接修改变量的值

  • 指针变量指向谁,就把谁的地址赋值给指针变量
  • 通过 *指针变量 间接修改变量的值
c
#include <stdio.h>

int main() {
    // 定义一个 int 类型变量 a,同时赋值为 0
    int a = 0;
    // 定义 int *指针变量,同时赋值 a 的地址
    int *p = &a;
    // 通过指针间接修改 a 的值
    *p = 123;
    printf("a = %d\n", a);
    // 定义一个 int 类型变量 b,同时赋值为 5
    int b = 5;
    // p 保存 b 的地址
    p = &b;
    // 通过指针间接修改 b 的值
    *p = 250;
    printf("b = %d\n", b);

    return 0;
}

指针大小

  • 使用 sizeof() 测量指针的大小,得到的总是:4 或 8

  • sizeof() 测的是指针变量指向存储地址的大小

    • 在 32 位平台,所有的指针(地址)都是 32 位 (4 字节)
    • 在 64 位平台,所有的指针(地址)都是 64 位 (8 字节)
c
#include <stdio.h>

int main() {
    int *p1;
	int **p2;
	char *p3;
	char **p4;
	printf("sizeof(p1) = %llu\n", sizeof(p1));
	printf("sizeof(p2) = %llu\n", sizeof(p2));
	printf("sizeof(p3) = %llu\n", sizeof(p3));
	printf("sizeof(p4) = %llu\n", sizeof(p4));
	printf("sizeof(double *) = %llu\n", sizeof(double *));

    return 0;
}

指针步长

  • 指针步长指的是通过指针进行递增或递减操作时,指针所指向的内存地址相对于当前地址的偏移量。

  • 指针的步长取决于所指向的数据类型type

    • 指针加 n 等于指针地址加上 nsizeof(type) 的长度
    • 指针减 n 等于指针地址减去 nsizeof(type) 的长度
c
#include <stdio.h>

int main() {
    char ch;
    char *p1 = &ch;
    printf("p1:%p, p1+1: %p\n", p1, p1 + 1); // 步长为 1 字节

    int a;
    int *p2 = &a;
    printf("p2:%p, p2+1: %p\n", p2, p2 + 1); // 步长为 4 字节

    double d;
    double *p3 = &d;
    printf("p3:%p, p3+1: %p\n", p3, p3 + 1); // 步长为 8 字节

    return 0;
}

野指针和空指针

  • 指针变量也是变量,是变量就可以任意赋值

  • 任意数值赋值给指针变量没有意义,因为这样的指针就成了野指针

    • 此指针指向的区域是未知 (操作系统不允许操作此指针指向的内存区域)
  • 野指针不会直接引发错误,操作野指针指向的内存区域才会出问题

  • 为了标志某个指针变量没有任何指向,可赋值为 NULL

    • NULL 是一个值为 0 的宏常量
c
#include <stdio.h>

int main() {
    int *p;
    p = 0x12345678; // 给指针变量 p 赋值,p 为野指针,ok,不会有问题,但没有意义
    // *p = 1000;      // 操作野指针指向未知区域,内存出问题,err
    printf("111111111111111111\n");

    int *q = NULL;  // 空指针

    return 0;
}

多级指针

  • C 语言允许有多级指针存在,在实际的程序中一级指针最常用,其次是二级指针。
  • 二级指针就是存储了【一级指针变量地址】的指针。
c
#include <stdio.h>

int main() {

    int a = 100;

    // 一级指针
    int* p1 = &a;
    printf("&a=%p\n", &a);
    printf("p1=%p\n", p1);
    printf("&p1=%p\n", &p1);

    // 二级指针,可以存储一级指针变量的地址
    int** p2 = &p1;
    printf("p2=%p\n", p2);
    printf("&p2=%p\n", &p2);

    // 三级指针,可以存储二级指针变量的地址
    int*** p3 = &p2;
    printf("p3=%p\n", p3);
    printf("&p3=%p\n", &p3);

    printf("---------------------\n");

    // 通过一级指针访问 100,打印出来
    printf("*p1=%d\n", *p1);

    // 通过二级指针访问 100,打印出来
    printf("**p2=%d\n", **p2);

    // 通过三级指针访问 100,打印出来
    printf("***p3=%d\n", ***p3);

    return 0;
}

指针与常量

语法格式

c
int a = 1;
// 常量指针
const int *p1 = &a;	// 等价于 int const *p1 = &a;
// 指针常量
int * const p2 = &a;

// 常量指针,指向一个常量整数
const int * const p3 = &a;

记忆技巧:从左往右看,跳过类型,看修饰哪个字符

  • 如果是*p,说明指针指向的变量内容不能改变(即:值不能改)
  • 如果是p,说明指针的指向不能改变(即:地址不能改)

完整实例

c
#include <stdio.h>

int main() {
    int a = 1;
    int b = 2;
    // p1 可以改,*p1 不能改
    const int *p1 = &a; // 等价于 int const *p1 = &a;
    // p1 = &b;    // ok
    // *p1 = 555;  // err

    // p2 不能修改,*p2 可以修改
    int *const p2 = &a;
    // p2 = &b;    //err
    // *p2 = 555;  // ok

    // p3 和 *p 都不能改
    const int *const p3 = &a;
    // p3 = &b;    // err
    // *p3 = 555;  // err

    return 0;
}