学懂C语言(十九):C语言指针详解

一、什么是指针?

        指针是一个变量,其存储的是另一个变量的内存地址。在计算机内存中,每个存储单元都有一个唯一的地址,指针就是用来存放这些地址的。因此,通过指针,你可以间接访问和修改内存中的数据。就像其他变量或常量一样,您必须在使用指针存储其他变量地址之前,对其进行声明。指针变量声明的一般形式为:

type *var_name;

        在这里,type 是指针的基类型,它必须是一个有效的 C 数据类型,var_name 是指针变量的名称。用来声明指针的星号 * 与乘法中使用的星号是相同的。但是,在这个语句中,星号是用来指定一个变量是指针。以下是有效的指针声明:

int    *ip;    /* 一个整型的指针 */
double *dp;    /* 一个 double 型的指针 */
float  *fp;    /* 一个浮点型的指针 */
char   *ch;    /* 一个字符型的指针 */

        所有实际数据类型,不管是整型、浮点型、字符型,还是其他的数据类型,对应指针的值的类型都是一样的,都是一个代表内存地址的长的十六进制数。

不同数据类型的指针之间唯一的不同是,指针所指向的变量或常量的数据类型不同。

二、如何使用指针?

        使用指针时会频繁进行以下几个操作:定义一个指针变量、把变量地址赋值给指针、访问指针变量中可用地址的值。这些是通过使用一元运算符 * 来返回位于操作数所指定地址的变量的值。下面的实例涉及到了这些操作:

#include <stdio.h>int main ()
{int  var = 20;   /* 实际变量的声明 */int  *ip;        /* 指针变量的声明 */ip = &var;  /* 在指针变量中存储 var 的地址 */printf("var 变量的地址: %p\n", &var  );/* 在指针变量中存储的地址 */printf("ip 变量存储的地址: %p\n", ip );/* 使用指针访问值 */printf("*ip 变量的值: %d\n", *ip );return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

var 变量的地址: 0x7ffeeef168d8
ip 变量存储的地址: 0x7ffeeef168d8
*ip 变量的值: 20

三、C 中的 NULL 指针

在变量声明的时候,如果没有确切的地址可以赋值,为指针变量赋一个 NULL 值是一个良好的编程习惯。赋为 NULL 值的指针被称为指针。

NULL 指针是一个定义在标准库中的值为零的常量。

#include <stdio.h>int main ()
{int  *ptr = NULL;printf("ptr 的地址是 %p\n", ptr  );return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

ptr 的地址是 0x0

注意:

        在大多数的操作系统上,程序不允许访问地址为 0 的内存,因为该内存是操作系统保留的。然而,内存地址 0 有特别重要的意义,它表明该指针不指向一个可访问的内存位置。但按照惯例,如果指针包含空值(零值),则假定它不指向任何东西。

如需检查一个空指针,您可以使用 if 语句,如下所示:

if(ptr) /* 如果 p 非空,则完成 */

if(!ptr) /* 如果 p 为空,则完成 */

四、C 指针详解 (*重要)

1、指针的算术运算

     可以对指针进行四种算术运算:++、--、+、-

      假设 ptr 是一个指向地址 1000 的整型指针,是一个 32 位的整数,让我们对该指针执行下列的算术运算:

ptr++

     在执行完上述的运算之后,ptr 将指向位置 1004,因为 ptr 每增加一次,它都将指向下一个整数位置,即当前位置往后移 4 字节。这个运算会在不影响内存位置中实际值的情况下,移动指针到下一个内存位置。如果 ptr 指向一个地址为 1000 的字符,上面的运算会导致指针指向位置 1001,因为下一个字符位置是在 1001。

我们概括一下:

  • 指针的每一次递增,它其实会指向下一个元素的存储单元。
  • 指针的每一次递减,它都会指向前一个元素的存储单元。
  • 指针在递增和递减时跳跃的字节数取决于指针所指向变量数据类型长度,比如 int 就是 4 个字节。
指针加法和减法

指针加法和减法会根据指针指向的类型来调整步长。例如,对一个指向int类型的指针加1,实际上是将其地址增加sizeof(int)字节。

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

指针可以进行比较运算,常用于判断指针的先后顺序或是否指向同一内存位置。

if (p1 < p2) {// p1指向的地址在p2之前
}

2、指针数组

  可以定义用来存储指针的数组。

在C语言中,数组名本质上是一个指向数组第一个元素的常量指针。因此,数组和指针在很多情况下可以互换使用。

int arr[5] = {1, 2, 3, 4, 5};
int *p = arr; // 等价于 int *p = &arr[0];

通过指针访问数组元素:

printf("%d\n", *(p + 2)); // 输出3,等价于arr[2]

 

3、指向指针的指针

   指针可以指向另一个指针,形成指针的指针(双重指针)。这在动态二维数组和函数参数传递中非常有用。

int a = 10;
int *p = &a;
int **pp = &p; // pp是指向p的指针printf("%d\n", **pp); // 输出10

4、传递指针给函数

      通过引用或地址传递参数,使传递的参数在调用函数中被改变。 

      指针可以作为函数参数传递,使得函数能够修改调用者提供的变量,或者传递大型数据结构而避免复制开销。

传递指针的简单例子:

void increment(int *p) {(*p)++; // 通过指针修改调用者的变量
}int main() {int a = 10;increment(&a); // 传递a的地址printf("%d\n", a); // 输出11return 0;
}

5、从函数返回指针

C 允许函数返回指针到局部变量、静态变量和动态内存分配。

函数可以返回指针,常用于动态内存分配和数据结构的构建。

示例:

int* create_array(int size) {int *arr = (int*)malloc(size * sizeof(int));for (int i = 0; i < size; i++) {arr[i] = i + 1;}return arr;
}int main() {int *arr = create_array(5);for (int i = 0; i < 5; i++) {printf("%d ", arr[i]); // 输出1 2 3 4 5}free(arr); // 释放动态分配的内存return 0;
}

6. 常见的指针应用

  • 动态内存分配:使用malloccallocreallocfree进行内存管理。
  • 字符串处理:字符串本质上是以空字符('\0')结尾的字符数组,指针在字符串操作中非常常见。
  • 数据结构实现:如链表、树、图等,指针用于连接各个节点。

7. 注意事项

  • 空指针:未初始化的指针或指向无效内存的指针可能导致程序崩溃。使用NULL来表示空指针。
  • 内存泄漏:动态分配的内存必须手动释放,否则会导致内存泄漏。
  • 指针运算:确保指针运算在有效内存范围内,避免越界访问。

通过以上讲解,你应该对C语言中的指针有了全面的了解。指针是C语言的核心特性之一,掌握好指针的使用对于编写高效、灵活的C程序至关重要。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/48208.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux提供的定时器

定时器在许多场景中非常有用&#xff0c;尤其是在需要精确定时或定时执行某些任务的情况下。而Linux专门为定时器提供了一套定时器接口。 timerfd_creat timerfd_create是 Linux 中用于创建定时器文件描述符的函数。这个功能主要是用来在指定的时间后或定时间隔内触发事件&am…

TS的访问修饰符有哪些

如果你和我一样是从强类型语言(如C、C#、Java)转过来的&#xff0c;相信你会一眼就知道是什么 public&#xff08;默认&#xff09; - 全部可访问 protected - 自己和派生类可访问 private - 只有自己可访问 废话不多说&#xff0c;上代码&#xff1a; class Person {publ…

Docker容器逃逸漏洞-CVE-2024-21626

Snyk 在 Docker 引擎以及其他容器化技术(例如 Kubernetes)使用的 runc <=1.1.11 的所有版本中发现了一个漏洞。利用此问题可能会导致容器逃逸到底层主机操作系统,无论是通过执行恶意映像还是使用恶意 Dockerfile 或上游映像构建映像(即使用时FROM) CVE-2024-21626原理…

生成式人工智能之路,从马尔可夫链到生成对抗网络

人工智能&#xff08;Artificial intelligence&#xff0c;AI&#xff09;技术在过去几年中取得了显著进展&#xff0c;其中生成式AI&#xff08;Generative AI&#xff09;因其强大的内容生成能力而备受关注。生成式AI可以创建新的文本、图像、音频、视频、代码以及其他形式的…

SSRF学习笔记

1.NAT学习 Nat&#xff08;Network Address Translation&#xff0c;网络地址转换&#xff09;是 一种网络通信技术主要用于将私有网络中的内部IP地址转换成公共网络中的公共IP地址&#xff0c;以实现局域网内部设备访问互联网的功能。具体来说&#xff0c;Nat有以下几个主要…

Python应用—实现和改良word邮件合并功能

1.实现目的 邮件合并也有其缺陷,那就是每次合并数据都需操作插入合并域,如果经常需要根据数据生成固定模板word文件,那么就显得很麻烦 显然利用程序固定数据替换模式就极为方便,需要生成文件时可以一键操作 2.代码实现 import os.path import xlrd from mailmerge impo…

Python 提示框

安装&#xff1a; pip install tk0.1.0第三方库地址&#xff1a; https://pypi.org/project/tk/ # 方法一 # -*- coding: UTF-8 -*-from tkinter import messagebox from tkinter import Tkroot Tk() root.withdraw() messagebox.showinfo("提示框", "这是一个…

Matlab画不同指标的对比图

目录 一、指标名字可修改 二、模型名字可修改 三、输入数据可修改 软件用的是Matlab R2024a。 clear,clc,close all figure1figure(1); % set(figure1,Position,[300,100,800,600],Color,[1 1 1]) axes1 axes(Parent,figure1);%% Initialize data points 一、指标名字可修…

MongoDB 学习笔记

一、简介 1、MongoDB 是什么 MongoDB 是一个基于分布式文件存储的数据库&#xff0c;官方地址 https://www.mongodb.com/ 2、数据看是什么 数据库&#xff08;DataBase&#xff09;是按照数据结构来组织、存储和管理数据的应用程序。 3、数据库的作用 主要作用是 管理数据…

mybatis中的缓存(一级缓存、二级缓存)

文章目录 前言一、MyBatis 缓存概述二、一级缓存1_初识一级缓存2_一级缓存命中原则1_StatementId相同2_查询参数相同3_分页参数相同4_sql 语句5_环境 3_一级缓存的生命周期1_缓存的产生2_缓存的销毁3_网传的一些谣言 4_一级缓存核心源码5_总结 三、二级缓存1_开启二级缓存2_二级…

利用深度学习模型BiLSTM进行数据预测和分析

1. 导入必要的库和模块&#xff1a; python import pandas as pd from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score import matplot…

如何选择适合的数据仓库ETL工具

在数据仓库的建设中&#xff0c;选择合适的ETL&#xff08;数据提取、转换和加载&#xff09;工具至关重要。本文将以两款常见的ETL工具——Informatica和Kettle为例&#xff0c;分享如何选择适合的ETL工具来支持数据仓库的构建和管理。通过对比它们的特点和应用场景&#xff0…

Pytorch 9

softmax多分类问题 import torch from torchvision import datasets from torch.utils.data import DataLoader import torch.nn.functional as F# 激活函数 import torch.optim as optim # optim.SGD (随机梯度下降)&#xff1a;最基础的优化算法&#xff0c;通过沿着梯度的反…

初等数论精解【5】

文章目录 不定方程基础理论不定方程例子 1: 线性不定方程例子 2: 整数解的不定方程例子 3: 含有多个未知数的不定方程总结 参考文献 不定方程 基础 一元不定方程 1. a 1 x a 0 0 x − a 0 a 1 &#xff0c;但是不能保证有整数解 2. a n x n a n − 1 x n − 1 . . . a…

Springboot 启动时Bean的创建与注入(二)-面试热点-springboot源码解读-xunznux

Springboot 启动时Bean的创建与注入&#xff0c;以及对应的源码解读 文章目录 Springboot 启动时Bean的创建与注入&#xff0c;以及对应的源码解读11、getBean:200, AbstractBeanFactory (org.springframework.beans.factory.support)12、doGetBean:335, AbstractBeanFactory (…

Vue系列面试题

大家好&#xff0c;我是有用就扩散&#xff0c;有用就点赞。 1.Vue中组件间有哪些通信方式&#xff1f; 父子组件通信&#xff1a; &#xff08;1&#xff09;props | $emit &#xff08;接收父组件数据 | 传数据给父组件&#xff09; &#xff08;2&#xff09;ref | $refs&a…

基于Hutool实现自定义模板引擎,实现json个性化模板引擎转换

文章目录 前言编写引擎类&#xff1a;JsonTemplateEngine编写模板类&#xff1a;CustomTemplate编写测试代码测试json文件测试类 前言 由于百度搜索json模板引擎&#xff0c;推荐的都是一些freemarker之类的&#xff0c;需要引入其他的依赖&#xff0c;而且在编写json模板的时…

学习在测试时学习(Learning at Test Time)_ 具有表达性隐藏状态的循环神经网络(RNNs)

摘要 https://arxiv.org/pdf/2407.04620 自注意力机制在长文本语境中表现良好&#xff0c;但其复杂度为二次方。现有的循环神经网络&#xff08;RNN&#xff09;层具有线性复杂度&#xff0c;但其在长文本语境中的性能受到隐藏状态表达能力的限制。我们提出了一种新的序列建模…

LabVIEW多线圈电磁式振动发电机测试

开发了一种基于LabVIEW设计的多线圈电磁式振动发电机测试系统。系统通过高效的数据采集、波峰检测及相位差计算&#xff0c;优化了传统振动发电机的测试流程&#xff0c;提升了电压波形分析的精度和效率&#xff0c;具有较好的应用前景和推广价值。 项目背景 随着可再生能源技…

跨越数据边界:域适应提升目标检测的泛化之舞

跨越数据边界&#xff1a;域适应提升目标检测的泛化之舞 目标检测模型在实际应用中常常面临泛化能力不足的问题&#xff0c;尤其是在数据源和部署环境不一致的情况下。域适应&#xff08;Domain Adaptation&#xff09;技术通过减少源域&#xff08;有标签数据&#xff09;和目…