当前位置: 首页 > article >正文

C语言指针专题一 -- 指针基础原理

目录

1. 指针概念

地址和变量

指针

2. 指针的声明与初始化

 3. 指针的使用

指针访问

指针的运算

 指针与数组

指针与函数

4. 编程实例

5. 指针的常见陷阱与防御

6. 总结 


 

1. 指针概念

地址和变量

在C语言中,地址和变量是两个基本但非常重要的概念。

1. 变量是指程序中用于存储数据值的命名位置。每个变量都有一个特定的数据类型,这决定了它可以存储的数据种类(如整数、字符、浮点数等)以及需要分配多少内存空间。创建变量时,系统会在计算机的内存中为该变量分配一块空间,并将你选择的名字与这块空间关联起来。之后,你可以通过这个名字来访问或修改这块内存中的数据。

int age = 25;

这里age就是一个变量,它的类型是int,表示它存储的是一个整数值。在这个例子中,age被初始化为25。

2. 地址指的是内存中的具体位置。当你声明一个变量时,系统会为其分配一定的内存空间,而这个空间的起始位置就是该变量的地址。可以通过取地址运算符&来获取变量的地址。地址是一个非常关键的概念,尤其是在处理指针时。指针是一种特殊的变量,它存储的是另一个变量或函数的地址,而不是直接存储数据值。

int age = 25;
printf("变量age的地址: %p", (void*)&age);


这段代码打印出变量age的地址。注意这里的(void*)是为了确保地址以正确的格式输出;在C语言中,使用%p格式说明符来打印指针(地址)。

变量是你用来命名并存储数据的地方。每个变量都有其特定的数据类型,并占用一定数量的内存。地址则是这些变量在内存中的具体位置。

指针

  • 指针变量:指针变量是用来存储内存地址的变量。指针变量的类型决定了它所指向的数据类型。

  • 地址运算符&:用于获取变量的内存地址。

  • 间接寻址运算符*:用于访问指针所指向的内存地址中的数据。

指针指向的内存区域中的数据称为指针的目标。

2. 指针的声明与初始化

指针的声明格式如下:

数据类型 *指针变量名;

例如:

int *p;  // 声明一个指向int类型数据的指针p

 指针的初始化通常是通过将某个变量的地址赋值给指针:

int a = 10;
int *p = &a;  // 将变量a的地址赋值给指针p

 3. 指针的使用

指针访问

通过指针可以访问和修改它所指向的变量的值:

int a = 10;
int *p = &a;
*p = 20;  // 通过指针p修改a的值为20

指针的运算

指针可以进行加减运算,运算的结果是指针向前或向后移动若干个元素的位置。指针的运算通常用于数组操作。

int arr[5] = {1, 2, 3, 4, 5};
int *p = arr;  // p指向数组的第一个元素
p++;  // p现在指向数组的第二个元素

 指针与数组

数组名本身就是一个指针,它指向数组的第一个元素。数组的指针是指数组在内存中的起始地址,数组元素的地址是指数组元素在内存中的起始地址。

通过指针可以遍历数组:

int arr[5] = {1, 2, 3, 4, 5};
int *p = arr;
for (int i = 0; i < 5; i++) {
    printf("%d ", *(p + i));  // 输出数组的每个元素
}

假定指针变量px的地址值等于数组指针x(即指针变量px指向数组的首元数),则:x[i] 、*(px+i)、*(x+i) 和px[i]具有完全相同的功能:访问数组第i+1个数组元素。

指针与函数

指针可以作为函数的参数,使得函数能够修改实参的值:

void increment(int *p) {
    (*p)++;  // 通过指针修改实参的值
}

int main() {
    int a = 10;
    increment(&a);  // 传递a的地址
    printf("%d", a);  // 输出11
    return 0;
}

4. 编程实例

实例1:使用指针交换两个变量的值

#include <stdio.h>

void swap(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

int main() {
    int x = 10, y = 20;
    printf("Before swap: x = %d, y = %d\n", x, y);
    swap(&x, &y);
    printf("After swap: x = %d, y = %d\n", x, y);
    return 0;
}

 实例2:使用指针遍历数组

#include <stdio.h>

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

 实例3:使用指针作为函数参数修改数组元素

#include <stdio.h>

void modifyArray(int *arr, int size) {
    for (int i = 0; i < size; i++) {
        arr[i] *= 2;  // 将数组中的每个元素乘以2
    }
}

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

实例4:函数指针实现排序策略

#include <stdio.h>

// 比较函数类型
typedef int (*CompareFunc)(int, int);

void bubbleSort(int arr[], int n, CompareFunc compare) {
    for (int i=0; i<n-1; i++) {
        for (int j=0; j<n-i-1; j++) {
            if (compare(arr[j], arr[j+1]) > 0) {
                int temp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = temp;
            }
        }
    }
}

int ascending(int a, int b) { return a - b; }  // 升序
int descending(int a, int b) { return b - a; } // 降序

int main() {
    int arr[] = {5, 2, 8, 1, 3};
    int n = sizeof(arr)/sizeof(arr[0]);

    // 使用升序排序
    bubbleSort(arr, n, ascending);
    for (int i=0; i<n; i++) printf("%d ", arr[i]); // 输出1 2 3 5 8

    // 使用降序排序
    bubbleSort(arr, n, descending);
    printf("\n");
    for (int i=0; i<n; i++) printf("%d ", arr[i]); // 输出8 5 3 2 1

    return 0;
}

5. 指针的常见陷阱与防御

  1. 野指针(Dangling Pointer)

    • 指向已释放内存的指针,访问会导致未定义行为。

    • 防御:释放后立即置空指针。

      int *p = (int*)malloc(sizeof(int));
      free(p);
      p = NULL; // 防止野指针
  2. 内存泄漏

    • 分配内存后未释放。

    • 防御:确保每个malloc都有对应的free

  3. 越界访问

    • 指针操作超出合法内存范围。

    • 防御:严格检查指针偏移量。

6. 总结 

指针是C语言中非常强大的工具,它允许直接操作内存地址,提供了灵活的数据访问和操作方式。通过指针,可以实现高效的数据处理、动态内存管理以及复杂的数据结构(如链表、树等)。然而,指针的使用也需要谨慎,因为不当的指针操作可能导致程序崩溃或内存泄漏等问题。,每一个字节单元,都有一个编号,称为地址


http://www.kler.cn/a/527658.html

相关文章:

  • 前端学习-事件委托(三十)
  • 【字符串两大注意事项】
  • UE5 GAS RPG Character Classes
  • [STM32 - 野火] - - - 固件库学习笔记 - - -十二.基本定时器
  • stm32控制直流电机程序
  • 电子电气架构 --- 在智能座舱基础上定义人机交互
  • 【Linux】使用管道实现一个简易版本的进程池
  • Pandas 常用函数
  • 【PLL】杂散生成和调制
  • (动态规划基础 打家劫舍)leetcode 198
  • 简要介绍C++中的 max 和 min 函数以及返回值
  • TensorFlow 简单的二分类神经网络的训练和应用流程
  • Git 常用命令汇总
  • 3.Spring-事务
  • 冯诺依曼结构和进程概念及其相关的内容的简单介绍
  • 99.23 金融难点通俗解释:小卖部经营比喻PPI(生产者物价指数)vsCPI(消费者物价指数)
  • 谈谈你所了解的AR技术吧!
  • 本地部署 DeepSeek 模型并使用 WebUI 调用
  • 32. C 语言 安全函数( _s 尾缀)
  • 1.31 实现五个线程的同步
  • 前端知识速记—JS篇:箭头函数
  • 力扣hot100--2
  • 比较器使用
  • FIDL:Flutter与原生通讯的新姿势,不局限于基础数据类型
  • 剑指offer 字符串 持续更新中...
  • 使用Pygame制作“动态烟花”