问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

C语言中如何赋予变量地址:指针、取地址符号与动态内存分配详解

创作时间:
作者:
@小白创作中心

C语言中如何赋予变量地址:指针、取地址符号与动态内存分配详解

引用
1
来源
1.
https://docs.pingcode.com/baike/1010871


在C语言中,赋予变量地址的主要方法包括使用指针、取地址符号(&)、以及通过动态内存分配函数(如malloc)。使用指针是C语言中处理内存地址的核心技术,尤其在系统编程和嵌入式开发中被广泛应用。我们将详细介绍如何通过这些方法操作和管理变量的地址。

一、使用指针

指针是C语言中一个非常重要的概念,它是用来存储另一个变量的地址的。通过指针,我们可以直接访问和操作内存中的数据。

1. 定义和初始化指针

要使用指针,首先需要定义一个指针变量。定义指针变量时,需指定指针变量所指向的数据类型。

int a = 10;
int *p = &a;  

在上面的代码中,

  • int *p 表示一个指向整数类型变量的指针。

  • p 被初始化为变量 a 的地址,通过 &a 取得。

2. 访问和修改指针指向的变量

通过指针可以访问和修改其指向的变量的值。

printf("Value of a: %dn", *p); // 输出10
*p = 20;  
printf("New value of a: %dn", a); // 输出20  

在这里,

  • *p 表示指针 p 所指向的变量的值。通过 *p,我们可以直接访问和修改 a 的值。

二、取地址符号(&)

取地址符号 & 用于获取变量的地址。它是指针操作的基础。

1. 获取变量地址

取地址符号可以直接获取一个变量的地址,并将其赋给一个指针。

int b = 30;
int *q;  
q = &b;  

在这段代码中,

  • q 被赋值为变量 b 的地址。此后,q 可以用于访问或修改变量 b 的值。

2. 指针与数组

数组名本身就是一个指针,指向数组的第一个元素。我们可以通过指针来遍历和操作数组。

int arr[3] = {1, 2, 3};
int *ptr = arr; // 等价于 int *ptr = &arr[0];  
for(int i = 0; i < 3; i++) {  
    printf("%d ", *(ptr + i));  
}  

在这里,

  • ptr 指向数组 arr 的第一个元素。通过 ptr,我们可以访问和操作数组的每一个元素。

三、动态内存分配

动态内存分配允许程序在运行时分配和释放内存。C语言提供了几个标准库函数来进行动态内存分配,如 malloccallocfree

1. 使用malloc函数

malloc 函数用于在运行时动态分配一块指定大小的内存,并返回一个指向该内存块的指针。

int *p = (int *)malloc(sizeof(int));
if (p != NULL) {  
    *p = 50;  
    printf("Value: %dn", *p);  
    free(p);  
}  

在这段代码中,

  • malloc 函数分配了一块足够存储一个整数的内存,并返回一个指向该内存的指针。通过指针 p,我们可以访问和修改这块内存中的数据。

2. 使用calloc函数

calloc 函数用于分配一块连续的内存空间,并将其初始化为零。

int *q = (int *)calloc(5, sizeof(int));
if (q != NULL) {  
    for (int i = 0; i < 5; i++) {  
        printf("%d ", q[i]);  
    }  
    free(q);  
}  

在这段代码中,

  • calloc 函数分配了一块可以存储5个整数的连续内存空间,并将每个元素初始化为零。通过指针 q,我们可以访问和操作这块内存。

3. 释放内存

使用 free 函数释放动态分配的内存,以避免内存泄漏。

free(p);
free(q);  

在这段代码中,我们释放了之前分配的内存空间 pq。释放后,这些指针将不再指向有效的内存空间,不能继续使用。

四、指针的高级用法

指针不仅可以指向基本数据类型,还可以指向数组、结构体、函数等。理解和掌握这些高级用法,可以更好地利用指针的强大功能。

1. 指向结构体的指针

指针可以指向结构体,并通过指针访问结构体成员。

struct Point {
    int x;  
    int y;  
};  
struct Point p1 = {10, 20};  
struct Point *ptr = &p1;  
printf("x: %d, y: %dn", ptr->x, ptr->y);  

在这段代码中,

  • ptr 指向结构体变量 p1。通过 ptr,我们可以访问和修改结构体成员 xy

2. 指向函数的指针

指针可以指向函数,并通过指针调用函数。

int add(int a, int b) {
    return a + b;  
}  
int (*funcPtr)(int, int) = add;  
printf("Result: %dn", funcPtr(5, 3));  

在这段代码中,

  • funcPtr 指向函数 add。通过 funcPtr,我们可以调用函数并传递参数。

五、指针运算

指针运算是指针的另一重要特性,通过指针运算可以方便地访问数组和内存块。

1. 指针的加减运算

指针加减运算用于在数组或内存块中移动指针位置。

int arr[5] = {1, 2, 3, 4, 5};
int *p = arr;  
for (int i = 0; i < 5; i++) {  
    printf("%d ", *(p + i));  
}  

在这段代码中,通过指针加法 p + i,我们可以访问数组 arr 中的每个元素。

2. 指针的比较运算

指针比较运算用于比较两个指针是否指向相同的内存地址。

int a = 10;
int b = 20;  
int *p1 = &a;  
int *p2 = &b;  
if (p1 == p2) {  
    printf("p1 and p2 point to the same address.n");  
} else {  
    printf("p1 and p2 point to different addresses.n");  
}  

在这段代码中,通过指针比较运算 p1 == p2,我们可以判断两个指针是否指向相同的内存地址。

六、指针的内存管理

指针的内存管理是使用指针的关键问题之一。正确的内存管理可以避免内存泄漏和悬空指针等问题。

1. 避免内存泄漏

内存泄漏是指程序在动态分配内存后没有正确释放,导致内存无法再被使用。

int *p = (int *)malloc(sizeof(int));
if (p != NULL) {  
    *p = 100;  
    // 忘记释放内存  
}  

在这段代码中,动态分配的内存没有被释放,导致内存泄漏。正确的做法是使用 free 函数释放内存。

free(p);  

2. 避免悬空指针

悬空指针是指指向已释放内存的指针,使用悬空指针会导致程序异常。

int *p = (int *)malloc(sizeof(int));
free(p);  
// p 仍然指向已释放的内存  
*p = 200; // 错误  

在这段代码中,

  • p 在内存被释放后仍然指向已释放的内存,导致悬空指针。正确的做法是将指针置空。
free(p);  
p = NULL;  

七、指针与函数参数

指针可以作为函数参数传递,实现函数间的数据共享和修改。

1. 使用指针传递数组

通过指针传递数组,可以避免数组拷贝,提高程序效率。

void printArray(int *arr, int size) {
    for (int i = 0; i < size; i++) {  
        printf("%d ", arr[i]);  
    }  
}  
int main() {  
    int arr[5] = {1, 2, 3, 4, 5};  
    printArray(arr, 5);  
    return 0;  
}  

在这段代码中,通过指针 arr 传递数组,实现了数组在函数间的共享。

2. 使用指针修改函数外部变量

通过指针,可以在函数内部修改函数外部的变量。

void increment(int *p) {
    (*p)++;  
}  
int main() {  
    int a = 5;  
    increment(&a);  
    printf("a: %dn", a); // 输出6  
    return 0;  
}  

在这段代码中,通过指针 p 传递变量 a 的地址,实现了在函数内部修改函数外部变量 a 的值。

八、指针的安全性

指针的安全性是C语言编程中的一个重要问题。未初始化指针、悬空指针、越界访问等都会导致程序异常,甚至引发安全漏洞。

1. 初始化指针

指针在定义时应及时初始化,避免使用未初始化指针。

int *p = NULL;  

在这段代码中,指针 p 在定义时被初始化为 NULL,避免了未初始化指针的使用。

2. 检查指针有效性

在使用指针前,检查指针是否有效。

int *p = (int *)malloc(sizeof(int));
if (p != NULL) {  
    *p = 100;  
}  

在这段代码中,通过检查指针 p 是否为 NULL,确保指针有效后再使用。

3. 避免越界访问

指针操作时应注意避免越界访问,防止访问非法内存。

int arr[5] = {1, 2, 3, 4, 5};
int *p = arr;  
for (int i = 0; i < 5; i++) {  
    printf("%d ", *(p + i));  
}  

在这段代码中,通过控制循环变量 i 的范围,避免了指针越界访问。

九、指针的应用

指针在C语言中有广泛的应用,特别是在数据结构、系统编程和嵌入式开发中。

1. 链表

链表是一种常用的数据结构,通过指针实现节点间的链接。

struct Node {
    int data;  
    struct Node *next;  
};  
struct Node* createNode(int data) {  
    struct Node *newNode = (struct Node *)malloc(sizeof(struct Node));  
    if (newNode != NULL) {  
        newNode->data = data;  
        newNode->next = NULL;  
    }  
    return newNode;  
}  

在这段代码中,通过指针实现了链表节点的创建和链接。

2. 动态数组

通过指针和动态内存分配,可以实现动态数组,支持数组大小的灵活变化。

int *createArray(int size) {
    int *arr = (int *)malloc(size * sizeof(int));  
    if (arr != NULL) {  
        for (int i = 0; i < size; i++) {  
            arr[i] = i + 1;  
        }  
    }  
    return arr;  
}  

在这段代码中,通过指针和 malloc 函数实现了动态数组的创建和初始化。

3. 字符串操作

字符串在C语言中是以字符数组形式存在的,通过指针可以方便地操作字符串。

void printString(char *str) {
    while (*str != '\0') {
        printf("%c", *str);
        str++;
    }
}

在这段代码中,通过指针遍历字符串中的每个字符并打印出来,展示了指针在字符串操作中的应用。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号