06C语言——指针

news/2025/2/24 7:55:07

一、指针入门

(1)、准备知识

0、图解:

1、内存地址

  • 字节:字节是内存的容量单位,英文称为 byte,一个字节有8位,即 1byte(0000 0000 --- 1111 1111) = 8bits(0 --- 1)
  • 地址:系统为了便于区分每一个字节而对它们逐一进行的编号,称为内存地址,简称地址。

32位系统:

说明:

地址+1就是加1个字节

为什么选32位系统来讲内存??

32位:4G内存:

64位:理论:1800亿亿GB,操作系统支持:16TB,电脑实际的内存条:4G-32G(所以本质上还是基于4G)

2、基地址

  • 单字节数据:对于单字节数据而言,其地址就是其字节编号。
  • 多字节数据:对于多字节数据而言,其地址是其所有字节中编号最小的那个,称为基地址。

3、取址符

  • 每个变量都是一块内存,都可以通过取址符 & 获取其地址
  • 注意:
    • 虽然不同的变量的尺寸是不同的,但是他们的地址的尺寸确实一样的。
    • 不同的地址虽然形式上看起来是一样的,但由于他们代表的内存尺寸和类型都不同,因此它们在逻辑上是严格区分的。
  • 示例代码:
// (1)、如何获取一个内存的地址??
char   ch1 = 200;
int   num1 = 100;
float  f1  = 3.14;
double f2  = 6.18;

// 1、不同的变量的尺寸是不同的
printf("ch1的地址  == %p\n", &ch1);
printf("num1的地址 == %p\n", &num1);
printf("f1的地址   == %p\n", &f1);
printf("f2的地址   == %p\n", &f2);
/*
    // 3- 不同的地址虽然形式上看起来是一样的,但由于他们代表的内存尺寸和类型都不同,因此它们在逻辑上是严格区分的。
    解析:
        ch1的地址  == 0x7ffd1cf9c917        // char型内存占1字节
        num1的地址 == 0x7ffd1cf9c918        // int型内存占4字节
        f1的地址   == 0x7ffd1cf9c91c        // float型内存占4字节
        f2的地址   == 0x7ffd1cf9c920        // float型内存占8字节
*/

// 2、但是他们的地址的尺寸确实一样的
printf("ch1的地址的尺寸  == %lu\n", sizeof(&ch1));
printf("num1的地址的尺寸 == %lu\n", sizeof(&num1));
printf("f1的地址的尺寸   == %lu\n", sizeof(&f1));
printf("f2的地址的尺寸   == %lu\n", sizeof(&f2));

(2)、指针的概念

0、图解:

1、指针的说明

由于翻译的问题,以及口语表达的习惯,在日常表述中,指针在不同的场合会代表以下几个含义:

  • 指 地址
    • 比如变量a的地址 &a,这是一个地址当然也是一个指针,我们可以说指针 &a 指向变量 a。
  • 指 指针变量
    • 比如 int *p; 此处变量p是指针变量,又常被简称指针。
  • 示例代码:
// (1)、指针的说明
// 1、指地址
int num2 = 200;
printf("num2的地址 == %p\n", &num2) ;   // 可以认为&num2为指针,这个指针&num指向了变量num2(或者num2的地址被&num2给存放了)

// 2、指指针变量
int num3 = 300;
int *p1  = &num3;                      // 指针变量p里面存放了num3的内存的地址(指针变量p指向了num的内存)

2、指针的初始化(定义)

// 1、未初始化的类型                    // 注意:定义指针的时候,必须要有具体的合法指向,否则指针将会在内存中乱指,导致数据处理出错
char   *p1;                            // 字符型指针类型
int    *p2;                            // 整型指针类型
float  *p3;                            // 浮点型指针类型
double *p4;                            // 双精度浮点型指针类型

// 2、推荐的初始化
char ch  = 0;                         
char *p5 = &ch;                        // 指向确定的我们申请的合法内存(可读可写)
char *p6 = NULL;                       // 指向内存中一个临时的安全的保留区域(不可访问区域)

3、指针的赋值

int num4 = 100;
int *p7  = NULL;
p7   = &num4;                           // 一般性的赋值操作,和初始化(定义)是不一样的,不需要加*号,p8本身就是指针变量

4、指针的索引(引用)

int num5 = 500;
num5     = 555;                         // 直接通过内存的名字,对其内存进行赋值操作

int *p8  = &num5;                       // 让指针p8指向这块内存
*p8      = 888;                         // 间接通过指针p8来控制num5的内存,然后给其赋值
printf("num5 == %d\n", num5);

/*
    int *p和*p的不同:
        int *p: 这个是初始化,只是表明这个p变量是个指针
        *p:    这个不是初始化,是后面的语句用的,这个时候表明其是指针p指向的那块内存
        p:      这个不是初始化,是后面的语句用的,这个时候表明其是指针变量p
*/

二、特殊指针

(1)、野指针

  • 概念:指向一块未知区域的指针,被称为野指针。野指针是危险的。

  • 危害:
  1. 引用野指针,相当于访问了非法的内存,常常会导致段错误(segmentation fault)
  2. 引用野指针,可能会破坏系统的关键数据,导致系统崩溃等严重后果
  • 产生原因:
  1. 指针定义之后,未初始化
  2. 指针所指向的内存,被系统回收
  3. 指针越界
  • 如何防止:
  1. 指针定义时,及时初始化
  2. 绝不引用已被系统回收的内存
  3. 确认所申请的内存边界,谨防越界

示例代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 计算数组元素的个数
#define CAL_ARR_NUM(A) (sizeof(A)/sizeof(A[0]))
                        // 整个数组的大小 / 数组的首元素的大小 == 数组元素个数

// 自己定义的NULL
#define SELF_NULL ((void *)0) 

// 官方定义的NULL
// #define NULL ((void *)0)   // 空指针

// 主函数
int main(int argc, char const *argv[])
{
    // (1)、野指针
    // 1、指针定义之后,未初始化
    char *p1;
    /*
        解决方法:
            方法一:char *p1 = NULL;  // char *p1 =  SELF_NULL
            方法二:
                char ch  = 'a';
                char *p1 = &ch;
    */

    // 2、指针所指向的内存,被系统回收
    char *str = malloc(100);    // 申请堆内存空间:申请一块100个字节的内存空间(堆空间),str指针指向了这块内存
    strcpy(str, "hello");       // 通过str指针将"hello"字符串数据,复制到str指向的内存中(堆空间)

    // free(str);               // 释放堆内存空间:通过str指针释放其指向的内存空间

    if (str != NULL)            // str指针释放堆内存空间后,其指向依然是那个空间的位置,不会是NULL(具体看系统,看编译器)
    {
        strcpy(str, "world");   // 之前已经释放了str指针指向的堆内存空间了,对其已经没有访问权限了,所以会报错误或警告
        printf("str == %s\n", str);
    }

    // 解决方法:使用完该内存,再释放,绝不再次使用

    // 3、指针越界
    char buf[] = "shijie";
    char *p2   = buf;

    // 数组的地址
    for (int i = 0; i < 7; i++)
    {
        printf("buf[%d]的地址 == %p\n", i, &buf[i]);
    }
    
    // p2逐渐加1的地址
    for (int i = 0; i < 7; i++)
    {
        printf("p2+%d的地址 == %p\n", i, p2+i); // 将指针变量p2里面的地址打印出来
    }
    
    *(p2+10) = 'a';     // 此处不能赋值,因为越界了,不是我门申请的合法内存空间
    /*
        解决方法:
            让指针赋值操作在其限定的合法内存的区域上进行
            比如:使用这个函数CAL_ARR_NUM(A)   // 计算其合法内存区域的长度
    */

    return 0;
}

(2)、空指针

很多情况下,我们不可避免地会遇到野指针,比如刚定义的指针内无法立即为其分配一块恰当的存,又或者指针所指向的内存被释放了等等。一般的做法就是将这些危险的野指针指向一块确定的内存,比如零地址内存。

  • 概念:空指针即保存了零地址的指针,亦即指向零地址的指针。
// 自写的NULL空指针
#define SELF_NULL ((void *)0)   

// 官方定义的NULL空指针
#define NULL ((void *)0)

三、指针运算

(1)、计算指针的大小

  • 指针的大小(尺寸)   --- 只和系统位数相关(32位:4字节, 64字节),和数据类型无关
char    *p1 = NULL;
int     *p2 = NULL;
float   *p3 = NULL;
double  *p4 = NULL;

printf("sizeof(f1) == %lu\n", sizeof(p1));
printf("sizeof(f2) == %lu\n", sizeof(p2));
printf("sizeof(f3) == %lu\n", sizeof(p3));
printf("sizeof(f4) == %lu\n", sizeof(p4));

/*
    32位系统:
        sizeof(f1) == 4
        sizeof(f2) == 4
        sizeof(f3) == 4
        sizeof(f4) == 4
        
    64位系统:
        sizeof(f1) == 8
        sizeof(f2) == 8
        sizeof(f3) == 8
        sizeof(f4) == 8
*/

(2)、指针的加减乘除(乘除没有任何意义,因此无需理会)

  • 指针加法意味着地址向上(高地址方向)移动若干个目标
  • 指针减法意味着地址向下(低地址方向)移动若干个目标

图解:

示例代码:

char ch1   = 'a';
int num1   = 100;
double f1  = 3.14;

char  *p5  = &ch1;
int   *p6  = &num1;
double *p7 = &f1;


// 1、乘除没有任何意义的解释
/*
    地址: 
        乘法: 0x7fffcadca377 * 5: 你很难知道这块内存到底在哪里(所以毫无意义)
        除法: 0x7fffcadca377 / 5
*/

// 2、指针的加减法
// a、char型指针
printf("\n");
printf("指针变量p5存放的地址(ch1变量的地址)     == %p\n", p5);
printf("指针变量p5+1存放的地址(ch1变量的地址+1) == %p\n", p5+1);
/*
    解析:
        指针变量p5存放的地址(ch1变量的地址)     == 0x7ffdbddf20f7
        指针变量p5+1存放的地址(ch1变量的地址+1) == 0x7ffdbddf20f8

    地址只移动了一个字节,证明指针p5的作用范围是1个字节
*/

// b、int型指针
printf("\n");
printf("指针变量p6存放的地址(num1变量的地址)     == %p\n", p6);
printf("指针变量p6+1存放的地址(num1变量的地址+1) == %p\n", p6+1);
/*
    解析:
        指针变量p6存放的地址(num1变量的地址)     == 0x7ffdc805c918
        指针变量p6+1存放的地址(num1变量的地址+1) == 0x7ffdc805c91c

    地址只移动了4个字节,证明指针p6的作用范围是4个字节
*/

// c、double型指针
printf("\n");
printf("指针变量p7存放的地址(f1变量的地址)     == %p\n", p7);
printf("指针变量p7+1存放的地址(f1变量的地址+1) == %p\n", p7+1);
/*
    解析:
        指针变量p7存放的地址(f1变量的地址)     == 0x7ffcba66ad08
        指针变量p7+1存放的地址(f1变量的地址+1) == 0x7ffcba66ad10

    地址只移动了8个字节,证明指针p7的作用范围是8个字节
*/

至此,希望看完这篇文章的你有所收获,我是Bardb,译音八分贝,期待下期与你相见!

 


http://www.niftyadmin.cn/n/5864093.html

相关文章

基于模仿学习(IL)的端到端自动驾驶发展路径

基于模仿学习&#xff08;IL&#xff09;的端到端自动驾驶发展路径 1. 核心论文解析 (1) UniAD&#xff1a;感知-规划一体化 核心思想&#xff1a;首次提出将感知任务&#xff08;如目标检测、车道线识别、轨迹预测&#xff09;与规划任务集成到统一的端到端框架中&#xff…

css+js提问

文章目录 1. css部分隐藏元素的几种方式 2. js部分 1. css部分 隐藏元素的几种方式 overflow: hidden;display: noneopacity: 0position&#xff1a;top和left设置成足够大的负数visibility: hidden设置height&#xff0c;width等盒模型属性为0 盒子模型 标准盒子模型&#xf…

关于order by的sql注入实验

实验描述 本实验基于sqli-lab的第46关进行测试 本关的sql 语句为$sql "SELECT * FROM users ORDER BY $id" 利用sort进行sql注入&#xff0c;我们可以利用报错注入&#xff0c;延时注入来爆出数据 1.报错注入 1.手工测试 爆出数据库 ?sort(extractvalue(1, c…

计算机网络真题练习(高软29)

系列文章目录 计算机网络阶段练习 文章目录 系列文章目录前言一、真题练习总结 前言 计算机网络的阶段练习题&#xff0c;带解析答案。 一、真题练习 总结 就是高软笔记&#xff0c;大佬请略过&#xff01;

【MySQL 一 数据库基础】深入解析 MySQL 的索引(3)

索引 索引操作 自动创建 当我们为一张表加主键约束(Primary key)&#xff0c;外键约束(Foreign Key)&#xff0c;唯一约束(Unique)时&#xff0c;MySQL会为对应的的列自动创建一个索引&#xff1b;如果表不指定任何约束时&#xff0c;MySQL会自动为每一列生成一个索引并用ROW_I…

使用大语言模型(Deepseek)构建一个基于 SQL 数据的问答系统

GitHub代码仓库 架构 从高层次来看&#xff0c;这些系统的步骤如下&#xff1a; 将问题转换为SQL查询&#xff1a;模型将用户输入转换为SQL查询。 执行SQL查询&#xff1a;执行查询。 回答问题&#xff1a;模型根据查询结果响应用户输入。 样本数据 下载样本数据&#xf…

OutOfMemoryError unable to create new native thread

现象 生产环境大量的报OutOfMemoryError: unable to create new native thread Caused by: java.lang.OutOfMemoryError: unable to create new native threadat java.lang.Thread.start0(Native Method) [na:1.8.0_291]at java.lang.Thread.start(Thread.java:717) [na:1.8.…

w803|联盛德|WM IoT SDK2.X测试|window11|TOML 文件|外设|TFT_LCD|测试任务|(5):TFT_LCD_LVGL示例

TFT_LCD_LVGL 功能概述 此应用程序是使用 WM IoT SDK 进行 LVGL 功能的示例。它演示了如何初始化 TFT LCD 设备&#xff0c;并创建 LVGL DEMO Task 进行 LVGL 模块的初始化&#xff0c;并展示 LVGL 原生的不同 Demo 场景, 例如&#xff1a; Widgets, Music Player, Benchmark…