C语言之指针

简介

指针是 C 语言中非常重要的概念。指针是一个变量,它存储的不是具体的数据值,而是另一个变量的内存地址。

简单地说,指针就是指向内存中某个地址的一个变量。通过使用指针,我们可以间接访问和操作内存中的数据。

首先,我们需要理解内存的基本结构。计算机的内存可以看作是一个线性的字节序列,每个字节都有一个独特的地址。这些地址通常用十六进制数表示,如 0x1000、0x1001 等。

指针就是一个存储内存地址的变量。比如,我们定义一个整型变量 int x = 10;。在内存中,这个变量 x 会被分配一个地址,比如 0x1000。我们可以声明一个指针变量 int* p = &x;。这样,指针变量 p 就会存储变量 x 的地址 0x1000。

通过指针变量 p,我们可以间接访问存储在地址 0x1000 的值 10。这种通过指针访问内存的方式,被称为"间接寻址"。

几个重要的运算符

& 运算符

返回变量的地址。如 &x 会返回变量 x 的内存地址。

* 运算符

解引用运算符,用于访问指针指向的值。如 *p 会返回指针 p 指向的值。

此外

我们还可以对指针进行加减运算。比如 p++ 会使指针 p 指向下一个内存单元,p-- 会使其指向上一个内存单元。这在处理数组和链表等数据结构时非常有用。

应用

函数参数传递

通常,我们将变量作为值传递给函数,函数只能访问该变量的副本。但如果我们传递变量的地址(指针),函数就可以直接修改调用者的变量。这种传递方式被称为"引用传递"。

其他

动态内存分配和释放
实现复杂的数据结构,如链表、树等
在函数之间传递大型数据结构
访问硬件设备的寄存器

简单示例

假设我们要实现一个可以动态增长的整型数组。我们需要定义以下数据结构:

typedef struct {
    int* data; // 指向动态分配的数组
    size_t size; // 当前数组大小
    size_t capacity; // 当前数组容量
} DynArray;

下面是一些常见的操作函数:

// 初始化动态数组
void init_dyn_array(DynArray* arr) {
    arr->data = NULL;
    arr->size = 0;
    arr->capacity = 0;
}

// 向动态数组中添加元素
void push_back(DynArray* arr, int value) {
    // 如果数组已满,先扩容
    if (arr->size == arr->capacity) {
        size_t new_capacity = (arr->capacity == 0) ? 1 : arr->capacity * 2;
        arr->data = realloc(arr->data, new_capacity * sizeof(int));
        arr->capacity = new_capacity;
    }
    arr->data[arr->size++] = value;
}

// 获取动态数组中的元素
int get(DynArray* arr, size_t index) {
    if (index >= arr->size) {
        // 越界处理
        return 0;
    }
    return arr->data[index];
}

// 释放动态数组
void free_dyn_array(DynArray* arr) {
    free(arr->data);
    arr->data = NULL;
    arr->size = 0;
    arr->capacity = 0;
}

下面是一个使用示例:

int main() {
    DynArray arr;
    init_dyn_array(&arr);

    push_back(&arr, 1);
    push_back(&arr, 2);
    push_back(&arr, 3);

    printf("Array size: %zu\n", arr.size);
    for (size_t i = 0; i < arr.size; i++) {
        printf("%d ", get(&arr, i));
    }
    printf("\n");

    free_dyn_array(&arr);
    return 0;
}

在这个例子中,我们使用指针定义了一个动态数组结构 DynArray。通过对指针的操作,我们实现了数组的动态扩容、元素的添加和访问,以及最终的内存释放。

##DynArray* arr与DynArray arr

DynArray* arr;
这是一个指针变量,用于指向 DynArray 类型的对象。
当你声明这个指针变量时,它只是分配了存储地址的空间,但并没有实际分配 DynArray 对象的内存。
要使用这个指针,你需要通过 malloc() 或 calloc() 等函数动态分配 DynArray 对象的内存,然后将指针指向这块内存。
例如: DynArray* arr = (DynArray*)malloc(sizeof(DynArray));
使用完毕后,需要手动释放内存: free(arr);
DynArray arr
这是一个 DynArray 类型的变量,会自动在栈上分配内存空间。
当你声明这个变量时,它会自动初始化为一个默认的 DynArray 对象。
你可以直接使用这个对象,不需要手动分配内存。
当这个变量超出作用域时,它会自动被销毁,不需要手动释放内存。
小结

DynArray* arr; 使用动态内存分配,需要手动管理内存,灵活性更高。
DynArray arr; 使用栈上的内存,更简单易用,但不太灵活。

指针与二级指针

指针用于存变量的内存地址,二级指针用于存指针的内存地址。

指针

指针存储的是变量的内存地址。
通过解引用指针 (*ptr) 可以访问变量的值。
例如:

int x = 10;
int* ptr = &x; // ptr 存储了变量 x 的地址
*ptr = 20; // 通过 ptr 修改了变量 x 的值

二级指针

二级指针存储的是指针变量的内存地址。
二级指针指向一个指针变量,通过二级指针可以间接访问和修改指针变量所指向的值。
例如:

int x = 10;
int* ptr = &x;
int** ptr_to_ptr = &ptr; // ptr_to_ptr 存储了指针 ptr 的地址

**ptr_to_ptr = 30; // 通过二级指针修改了 ptr 所指向的值

在这个例子中:

ptr 是一个指针,指向变量 x。
ptr_to_ptr 是一个二级指针,它存储了指针 ptr 的地址。
通过解引用二级指针 **ptr_to_ptr,我们可以间接修改 ptr 所指向的值,也就是变量 x 的值。

二级指针的常见用途

在函数中传递指针参数,以便函数能够修改指针所指向的值。
处理动态分配的二维数组或链表等复杂数据结构。
总之,指针用于存储变量的地址,而二级指针则用于存储指针变量的地址。掌握指针和二级指针的概念和用法,对于编写复杂的 C 程序非常重要。

指针和二级指针的使用约定

关于指针和二级指针的使用约定,确实不是一成不变的,但是有一些公认的最佳实践和常见用法。

指针的使用约定

在大多数情况下,我们都会遵循将指针命名为 ptr 的约定。这是一个相当普遍的 C 编程习惯。
但这并不是强制性的,开发者也可以根据具体情况选择其他更有意义的命名,只要保持一致性即可。

二级指针的使用约定

对于二级指针,通常会遵循将其命名为 ptr_to_ptr 或 dptr 等形式的约定。
这种命名约定可以更清晰地表达二级指针的用途,增加代码的可读性。

arr->size 与arr.size

在 C 语言中,arr->size 的使用方式与其他面向对象语言中的 a.b 非常相似。

在 C 语言中,当我们有一个指向结构体的指针时,我们可以使用 -> 运算符来访问结构体内的成员变量。这种访问方式与点号 . 运算符非常相似,但又有所不同。

使用点号 . 运算符

当我们有一个直接的结构体变量时,可以使用点号 . 来访问其成员变量。
例如: struct DynArray arr; arr.size;

使用箭头 -> 运算符

当我们有一个指向结构体的指针时,需要使用箭头 -> 来访问其成员变量。
例如: struct DynArray* arr; arr->size;

这两种访问方式在功能上是等价的,只是语法略有不同。使用哪种方式取决于你是否有一个直接的结构体变量,还是一个指向结构体的指针。

其他面向对象语言中的 a.b 的相似之处

在 Java 或 C# 中,你可以使用 a.b 来访问对象 a 的成员变量 b。
在 C++ 中,你也可以使用 a->b 来访问指向对象 a 的指针的成员变量 b。

  • 15
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值