【golang】关于指针的有限操作
传统意义上来说,指针是一个指向某个确切的内存地址的值。这个内存地址可以是任何数据或代码的起始地址。在Go语言中有几种东西可以代表"指针"。其中最贴切传统意义的当属uintptr
类型的了。该类型实际上是一个数值类型,也是Go语言内建的数据类型之一。
根据当前计算机的计算架构的不同,它可以存储32位或64位的无符号整数,可以代表任何指针的位(bit)模式,也就是原始的内存地址。
Go语言标准库中的unsafe
包,unsafe
包中有一个类型叫做Pointer
,也代表任何指针的位(bit)模式,也就是原始的内存地址。
unsafe.Pointer
可以表示任何指向可寻址的值的指针,同时它也是前面提到的指针值和uintptr
值之间的桥梁。通过它,我们可以在这两种值之上进行双向的转换。这里有一个很关键的词——可寻址的(addressable)
。
在我们继续说unsafe.Pointer
之前,需要先搞清楚这个词的确切含义。
Go语言中的哪些值是不可寻址的?
- 常量的值。
- 基本类型值的字面量。
- 算术操作的结果值。
- 对各种字面量的索引表达式和切片表达式的结果值。
不过有一个例外,对切片字面量的索引结果值却是可寻址的。
- 对字符串变量的索引表达式和切片表达式的结果值。
- 对字典变量的索引表达式的结果值。
- 函数字面量和方法字面量,以及对它们的调用表达式的结果值。
- 结构体字面量的字段值,也就是对结构体字面量的选择表达式的结果值。
- 类型转换表达式的结果值。
- 类型断言表达式的结果值。
- 接收表达式的结果值。
// 示例1。const num = 123//_ = &num // 常量不可寻址。//_ = &(123) // 基本类型值的字面量不可寻址。var str = "abc"_ = str//_ = &(str[0]) // 对字符串变量的索引结果值不可寻址。//_ = &(str[0:2]) // 对字符串变量的切片结果值不可寻址。str2 := str[0]_ = &str2 // 但这样的寻址就是合法的。//_ = &(123 + 456) // 算术操作的结果值不可寻址。num2 := 456_ = num2//_ = &(num + num2) // 算术操作的结果值不可寻址。//_ = &([3]int{1, 2, 3}[0]) // 对数组字面量的索引结果值不可寻址。//_ = &([3]int{1, 2, 3}[0:2]) // 对数组字面量的切片结果值不可寻址。_ = &([]int{1, 2, 3}[0]) // 对切片字面量的索引结果值却是可寻址的。//_ = &([]int{1, 2, 3}[0:2]) // 对切片字面量的切片结果值不可寻址。//_ = &(map[int]string{1: "a"}[0]) // 对字典字面量的索引结果值不可寻址。var map1 = map[int]string{1: "a", 2: "b", 3: "c"}_ = map1//_ = &(map1[2]) // 对字典变量的索引结果值不可寻址。//_ = &(func(x, y int) int {// return x + y//}) // 字面量代表的函数不可寻址。//_ = &(fmt.Sprintf) // 标识符代表的函数不可寻址。//_ = &(fmt.Sprintln("abc")) // 对函数的调用结果值不可寻址。dog := Dog{"little pig"}_ = dog//_ = &(dog.Name) // 标识符代表的函数不可寻址。//_ = &(dog.Name()) // 对方法的调用结果值不可寻址。//_ = &(Dog{"little pig"}.name) // 结构体字面量的字段不可寻址。//_ = &(interface{}(dog)) // 类型转换表达式的结果值不可寻址。dogI := interface{}(dog)_ = dogI//_ = &(dogI.(Named)) // 类型断言表达式的结果值不可寻址。named := dogI.(Named)_ = named//_ = &(named.(Dog)) // 类型断言表达式的结果值不可寻址。var chan1 = make(chan int, 1)chan1 <- 1//_ = &(<-chan1) // 接收表达式的结果值不可寻址。
常量的值总是会被存储到一个确切的内存区域中,并且这种值肯定是不可变的
。基本类型值的字面量也是一样,其实它们本就可以被视为常量,只不过没有任何标识符可以代表它们罢了。
第一个关键词:不可变的。由于 Go 语言中的字符串值也是不可变的,所以对于一个字符串类型的变量来说,基于它的索引或切片的结果值也都是不可寻址的,因为即使拿到了这种值的内存地址也改变不了什么。
算术操作的结果值属于一种临时结果
。在我们把这种结果值赋给任何变量或常量之前,即使能拿到它的内存地址也是没有任何意义的。
第二个关键词:临时结果。这个关键词能被用来解释很多现象。我们可以把各种对值字面量施加的表达式的求值结果都看做是临时结果。
我们都知道,Go 语言中的表达式有很多种,其中常用的包括以下几种。
- 用于获得某个元素的索引表达式。
- 用于获得某个切片(片段)的切片表达式。
- 用于访问某个字段的选择表达式。
- 用于调用某个函数或方法的调用表达式。
- 用于转换值的类型的类型转换表达式。
- 用于判断值的类型的类型断言表达式。
- 向通道发送元素值或从通道那里接收元素值的接收表达式。
我们把以上这些表达式施加在某个值字面量上一般都会得到一个临时结果。比如,对数组字面量和字典字面量的索引结果值,又比如,对数组字面量和切片字面量的切片结果值。它们都属于临时结果,都是不可寻址的。
一个需要特别注意的例外是,对切片字面量的索引结果值是可寻址的。因为不论怎样,每个切片值都会持有一个底层数组,而这个底层数组中的每个元素值都是有一个确切的内存地址的。
那么对切片字面量的切片结果值为什么却是不可寻址的?这是因为切片表达式总会返回一个新的切片值,而这个新的切片值在被赋给变量之前属于临时结果。
如果针对的是数组类型或切片类型的变量,那么索引或切片的结果值就都不属于临时结果了,是可寻址的。
这主要因为变量
的值本身就不是“临时的”。对比而言,值字面量
在还没有与任何变量(或者说任何标识符)绑定之前是没有落脚点的,我们无法以任何方式引用到它们。这样的值就是“临时的”。
我们通过对字典类型的变量施加索引表达式,得到的结果值不属于临时结果,可是,这样的值却是不可寻址的。
原因是,字典中的每个键 - 元素对的存储位置都可能会变化,而且这种变化外界是无法感知的。
字典中总会有若干个哈希桶用于均匀地储存键 - 元素对。当满足一定条件时,字典可能会改变哈希桶的数量,并适时地把其中的键 - 元素对搬运到对应的新的哈希桶中。在这种情况下,获取字典中任何元素值的指针都是无意义的,也是不安全的。我们不知道什么时候那个元素值会被搬运到何处,也不知道原先的那个内存地址上还会被存放什么别的东
西。所以,这样的值就应该是不可寻址的。
第三个关键词:不安全的。“不安全的”操作很可能会破坏程序的一致性,引发不可预知的错误,从而严重影响程序的功能和稳定性。
函数在 Go 语言中是一等公民,所以我们可以把代表函数或方法的字面量或标识符赋给某个变量、传给某个函数或者从某个函数传出。但是,这样的函数和方法都是不可寻址的。一个原因是函数就是代码,是不可变的。
另一个原因是,拿到指向一段代码的指针是不安全的。此外,对函数或方法的调用结果值也是不可寻址的,这是因为它们都属于临时结果。至于典型回答中最后列出的那几种值,由于都是针对值字面量的某种表达式的结果值,所以都属于临时结果,都不可寻址。
- 不可变的值不可寻址。常量、基本类型的值字面量、字符串变量的值、函数以及方法的字面量都是如此。其实这样规定也有安全性方面的考虑。
- 绝大多数被视为临时结果的值都是不可寻址的。算术操作的结果值属于临时结果,针对值字面量的表达式结果值也属于临时结果。但有一个例外,对切片字面量的索引结果值,虽然也属于临时结果,但却是可寻址的。
- 若拿到某值的指针可能会破坏程序的一致性,那么就是不安全的,该值就不可寻址。由于字典的内部机制,对字典的索引结果值的取址操作都是不安全的。另外,获取由字面量或标识符代表的函数或方法的地址显然也是不安全的。
不可寻址的值在使用上有哪些限制?
首当其冲的当然是无法使用取址操作符&
获取它们的指针了。不过,对不可寻址的值施加取址操作都会使编译器报错,所以倒是不用太担心,你只要记住我在前面讲述的那几条规律,并在编码的时候提前注意一下就好了。
func New(name string) Dog {return Dog{name}
}
我们再为它编写一个函数New
。这个函数会接受一个名为name
的string
类型的参数,并会用这个参数初始化一个Dog
类型的值,最后返回该值。我现在要问的是:如果我调用该函数,并直接以链式的手法调用其结果值的指针方法SetName
,那么可以达到预期的效果吗?
New("little pig").SetName("monster")
由于New
函数的调用结果的值是不可寻址的,所以无法对它进行取址操作。因此,上边这行链式调用会让编译器报告两个错误,一个是果,即:不能在New{"little pig"}
的结果值上调用指针方法。一个是因,即:不能取得New{"little pig"}
的地址。
除此之外,我们都知道,Go 语言中的++和–并不属于操作符,而分别是自增语句和自减语句的重要组成部分。
虽然 Go 语言规范中的语法定义是,只要在++或–的左边添加一个表达式,就可以组成一个自增语句或自减语句,但是,它还明确了一个很重要的限制,那就是这个表达式的结果值必须是可寻址的。这就使得针对值字面量的表达式几乎都无法被用在这里。
不过这有一个例外,虽然对字典字面量和字典变量索引表达式的结果值都是不可寻址的,但是这样的表达式却可以被用在自增语句和自减语句中。
与之类似的规则还有两个。一个是,在赋值语句中,赋值操作符左边的表达式的结果值必须可寻址的,但是对字典的索引结果值也是可以的。
另一个是,在带有range子句的for语句中,在range关键字左边的表达式的结果值也都必须是可寻址的,不过对字典的索引结果值同样可以被用在这里。
怎样通过unsafe.Pointer操纵可寻址的值?
unsafe.Pointer
是像 * Dog
类型的值这样的指针值和uintptr
值之间的桥梁,那么我们怎样利用unsafe.Pointer
的中转和uintptr
的底层操作来操纵像dog
这样的值呢?
首先说明,这是一项黑科技。它可以绕过 Go 语言的编译器和其他工具的重重检查,并达到潜入内存修改数据的目的。这并不是一种正常的编程手段,使用它会很危险,很有可能造成安全隐患。
我们总是应该优先使用常规代码包中提供的 API 去编写程序,当然也可以把像reflect
以及go/ast
这样的代码包作为备选项。作为上层应用的开发者,请谨慎地使用unsafe
包中的任何程序实体。
dog := Dog{"little pig"}
dogP := &dog
dogPtr := uintptr(unsafe.Pointer(dogP))
我先声明了一个Dog类型的变量dog,然后用取址操作符&
,取出了它的指针值,并把它赋给了变量dogP。
最后,我使用了两个类型转换,先把dogP
转换成了一个unsafe.Pointer
类型的值,然后紧接着又把后者转换成了一个uintptr
的值,并把它赋给了变量dogPtr
。这背后隐藏着一些转换规则,如下:
- 一个指针值(比如
* Dog
类型的值)可以被转换为一个unsafe.Pointer
类型的值,反之
亦然。 - 一个
uintptr
类型的值也可以被转换为一个unsafe.Pointer
类型的值,反之亦然。 - 一个指针值无法被直接转换成一个
uintptr
类型的值,反过来也是如此。
所以,对于指针值和uintptr
类型值之间的转换,必须使用unsafe.Pointer
类型的值作为中转。那么,我们把指针值转换成uintptr
类型的值有什么意义吗?
namePtr := dogPtr + unsafe.Offsetof(dogP.name)
nameP := (*string)(unsafe.Pointer(namePtr))
这里需要与unsafe.Offsetof
函数搭配使用才能看出端倪。unsafe.Offsetof
函数用于获取两个值在内存中的起始存储地址之间的偏移量,以字节为单位。
这两个值一个是某个字段的值,另一个是该字段值所属的那个结构体值。我们在调用这个函数的时候,需要把针对字段的选择表达式传给它,比如dogP.name
。
有了这个偏移量,又有了结构体值在内存中的起始存储地址(这里由dogPtr
变量代表),把它们相加我们就可以得到dogP
的name
字段值的起始存储地址了。这个地址由变量namePtr
代表。
此后,我们可以再通过两次类型转换把namePtr
的值转换成一个* string
类型的值,这样就得到了指向dogP
的name
字段值的指针值。
你可能会问,我直接用取址表达式&(dogP.name)不就能拿到这个指针值了吗?干嘛绕这么大一圈呢?你可以想象一下,如果我们根本就不知道这个结构体类型是什么,也拿不到dogP这个变量,那么还能去访问它的name字段吗?
答案是,只要有namePtr
就可以。它就是一个无符号整数,但同时也是一个指向了程序内部数据的内存地址。它可能会给我们带来一些好处,比如可以直接修改埋藏得很深的内部数据。
但是,一旦我们有意或无意地把这个内存地址泄露出去,那么其他人就能够肆意地改动dogP.name
的值,以及周围的内存地址上存储的任何数据了。
文章学习自郝林老师的《Go语言36讲》
相关文章:
【golang】关于指针的有限操作
传统意义上来说,指针是一个指向某个确切的内存地址的值。这个内存地址可以是任何数据或代码的起始地址。在Go语言中有几种东西可以代表"指针"。其中最贴切传统意义的当属uintptr类型的了。该类型实际上是一个数值类型,也是Go语言内建的数据类型…...
ProGuard + SpringBoot3 + JDK17
1、pom依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/POM/4.…...
Kafka面试
文章内容转自: 华仔聊技术(Kafka 面试连环炮) 目录 一.初级 1.Kafka核心组件图 2.在 Kafka 中 Zookeeper 作用是什么? 3.生产者有哪些发消息的模式? 4.Kafka 如何合理设置分区数,越多越好吗? Kafka 如何合理设置分区数 分区设置越多越好吗? 5.如何保证 Kafka 中的…...
Amazon CloudFront 部署小指南(六)- Lambda@Edge 基础与诊断
内容简介 本文适用于希望使用 Amazon CloudFront LambdaEdge 提升 Amazon CloudFront 边缘计算能力的用户,旨在帮助您更好的进行 CloudFront LambdaEdge 的开发、调试、测试、部署等工作。 首先我们会对 CloudFront LambdaEdge 做个简单的介绍,然后分七个…...
centos7安装hadoop 单机版
1.解压 (1)将hadoop压缩包复制到/opt/software路径下 (2)解压hadoop到/opt/module目录下 [rootkb135 software]# tar -zxvf hadoop-3.1.3.tar.gz -C /opt/module/ (3)修改hadoop属主和属组 [rootkb135 m…...
村口的人家排放污水,污水浸染了整个村子,怎么办
从前有一个很不错的村子里,村子里有很多户人家,随着生活水平越来越好,房子也修起来了,柏油马路也宽敞了,大家进出村子,都要走那条马路,要不就出不去。 目录 1. 修厕所 2. 村口的日家 3. 告诉…...
算法leetcode|72. 编辑距离(rust重拳出击)
文章目录 72. 编辑距离:样例 1:样例 2:提示: 分析:题解:rust:二维数组(易懂)滚动数组(更加优化的内存空间) go:c:python&a…...
实训笔记8.21
8.21笔记 8.21笔记一、Hive数据仓库技术的基本概念和组成1.1 Hive的组成架构1.1.1 Hive的客户端(1)Hive的命令行客户端 hive命令(2)Hive的JDBC的客户端(Java API)hive的JDBC客户端又有多种使用方式 &#x…...
robust distortion-free watermarks for language models
本文是LLM系列文章,针对《robust distortion-free watermarks for language models》的翻译。 语言模的鲁棒无失真水印 摘要1 引言2 方法和理论分析3 实验结果4 讨论 摘要 我们提出了一种从自回归语言模型中在文本中植入水印的方法,该方法对扰动具有鲁…...
PTS性能测试工具-使用记录
因为PTS使用是要收费的,所以文中会有大量图片记录,为我自己以后工作中,可能会再次使用PTS做个参照,以免时间长,容易忘记~ 目录 一、创建场景 二、填写一个压测节点 1、填写节点基本信息 2、Body / Header填写 …...
【boost网络库从青铜到王者】第六篇:asio网络编程中的socket异步读(接收)写(发送)
文章目录 1、简介2、异步写 void AsyncWriteSomeToSocketErr(const std::string& buffer)3、异步写void AsyncWriteSomeToSocket(const std::string& buffer)4、异步写void AsyncSendToSocket(const std::string& buffer)5、异步读void AsyncReadSomeToSocket(cons…...
django sqlite3操作和manage.py功能介绍
参考链接:https://www.cnblogs.com/csd97/p/8432715.html manage.py 常用命令_python manage.py_追逐&梦想的博客-CSDN博客 python django操作sqlite3_django sqlite_浪子仙迹的博客-CSDN博客...
【SQL语句】SQL编写规范
简介 本文编写原因主要来于XC迁移过程中修改SQL语句时,发现大部分修改均源自于项目SQL编写不规范,以此文档做以总结。 注:此文档覆盖不甚全面,大体只围绕迁移遇到的修改而展开。 正文 1、【字段引号】 列名、表名如无特殊情况…...
后端项目开发:工具类封装(序列化、反射)
1.整合Jackson 根据《阿里巴巴开发规范》,包名使用单数,类名可以使用复数。 所以generic-common创建util包和utils工具类 很多时候我们需要将接收到的json数据转换为对象,或者将对象转为json存储。这时候我们需要编写用于json转换的工具类。…...
软件测试技术分享丨遇到bug怎么分析?
为什么定位问题如此重要? 可以明确一个问题是不是真的“bug” 很多时候,我们找到了问题的原因,结果发现这根本不是bug。原因明确,误报就会降低 多个系统交互,可以明确指出是哪个系统的缺陷,防止“踢皮球…...
LeetCode无重复字符的最长子串
给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。 示例 1: 输入: s “abcabcbb” 输出: 3 解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。 示例 2: 输入: s “bbbbb” 输出: 1 解释: 因为无重复字符的最长子串是 “…...
17.2.2 【Linux】通过systemctl观察系统上所有的服务
使用 systemctl list-unit-files 会将系统上所有的服务通通列出来~而不像 list-units 仅以 unit 分类作大致的说明。 至于 STATE 状态就是前两个小节谈到的开机是否会载入的那个状态项目。主要有 enabled / disabled / mask / static 等等。 假设我不想要知道这么多…...
Redis扩容机制与一致性哈希算法解析
在分布式系统设计中,Redis是一个备受欢迎的内存数据库,而一致性哈希算法则是分布式系统中常用的数据分片和负载均衡技术。本文将深入探讨Redis的扩容机制以及一致性哈希算法的原理,同时提供示例代码以帮助读者更好地理解这两个重要概念。 推…...
BDA初级分析——可视化基础
一、可视化的作用 数据可视化——利用各种图形方式更加直观地呈现数据的过程 可视化的作用 1、更快地理解数据,找出数据的规律和异常 2、讲出数据背后的故事,辅助做出业务决策 3、给非专业人士提供数据探索的能力 数据分析问题如何通过可视化呈现&am…...
边缘计算节点BEC典型实践:如何快速上手PC-Farm服务器?
百度智能云边缘计算节点BEC(Baidu Edge Computing)基于运营商边缘节点和网络构建,一站式提供靠近终端用户的弹性计算资源。边缘计算节点在海外覆盖五大洲,在国内覆盖全国七大区、三大运营商。BEC通过就近计算和处理,大…...
python自动把内容发表到wordpress完整示例及错误解答
要实现 Python 自动将内容发布到 WordPress,可以使用 Python 的 wordpress_xmlrpc 库,该库提供了使用 WordPress XML-RPC API 进行内容发布和管理的功能。 需要安装一下第三方库:wordpress_xmlrpc! pip install python_wordpress_xmlrpc 下面是一个简单的示例代码,可以实…...
【javaweb】学习日记Day6 - Mysql 数据库 DDL DML DQL
之前学习过的SQL语句笔记总结戳这里→【数据库原理与应用 - 第六章】T-SQL 在SQL Server的使用_Roye_ack的博客-CSDN博客 目录 一、概述 1、如何安装及配置路径Mysql? 2、SQL分类 二、DDL 数据定义 1、数据库操作 2、IDEA内置数据库使用 (1&…...
如何利用SFTP如何实现更安全的远程文件传输 ——【内网穿透】
🎬 鸽芷咕:个人主页 🔥 个人专栏: 《高效编程技巧》《cpolar》 ⛺️生活的理想,就是为了理想的生活! 文章目录 1. 安装openSSH1.1 安装SSH1.2 启动ssh 2. 安装cpolar2.1 配置termux服务 3. 远程SFTP连接配置3.1 查看生成的随机公…...
枚举和反射
枚举 枚举 枚举是一种特殊的类,它可以有自己的属性、方法和构造方法。 两种枚举的方法 自定义枚举 a.将构造器私有化,防止外部直接new b.去掉set方法,防止属性被修改 c.在内部直接创建固定的对象 通过类名直接去访问 关键字枚举 用…...
MinIO【部署 01】MinIO安装及SpringBoot集成简单测试
MinIO安装及SpringBoot集成测试 1.下载安装1.1 Install the MinIO Server1.2 Launch the MinIO Server1.3 Connect Your Browser to the MinIO Server 2.SpringBoot集成2.1 依赖及配置2.2 代码2.3 测试结果 1.下载安装 下载 https://min.io/download#/linux; 安装文…...
问道管理:证券代码是什么?有什么用?
交流炒股经历时,有些股民一时忘了股票发行公司的全称,会直接报一串数字来代替,这串数字的内容是证券代码,那么,证券代码是什么?它又起什么作用?关于这些,为大家准备了以下参考内容。…...
中文医学知识语言模型:BenTsao
介绍 BenTsao:[原名:华驼(HuaTuo)]: 基于中文医学知识的大语言模型指令微调 本项目开源了经过中文医学指令精调/指令微调(Instruction-tuning) 的大语言模型集,包括LLaMA、Alpaca-Chinese、Bloom、活字模型等。 我们基于医学知识图谱以及医…...
Java基础十四(字符串)
1. 判断邮箱 输入一个电子邮箱,判断是否是正确电子邮箱地址。 正确的邮箱地址: 必须包含 字符,不能是开头或结尾必须以 .com结尾和.com之间必须有其他字符 public class Mailbox {/*** 判断邮箱* param str* return boolean*/public stat…...
vue3 基础知识 (动态组件 keep-alive 等) 04
嘿,happy 文章目录 一、动态组件二、keep-alive 一、动态组件 动态组件是使用 component 组件,通过一个特殊的属性 is 来实现 一定是注册好的组件我们需要将属性和监听事件放到 component 上来使用 <template><div><button v-for"t…...
【C# Programming】编程入门:数组、操作符、控制流
目录 一、数组 1、数组的声明 1.1 一维数组声明: 1.2 多维数组声明: 2、数组的实例化和赋值 2.1 数组在声明时通过在花括号中使用以逗号分隔的数据项对数组赋值, 例如: 2.2 如果在声明后赋值,则需…...
网站用什么字体做正文/如何快速提升自己
PHP类的属性不能被赋值为变量的原因 ps:本人亲测,阿里云2核4G5M的服务器性价比很高,新用户一块多一天,老用户三块多一天,最高可以买三年,感兴趣的可以戳一下:阿里云折扣服务器 成员属性声明时…...
做网上水果网站的调查/优化课程
0背景之前学习了USB虚拟成串口VCP和自定义HID设备的两种模式,本次来学习STM32的USB模拟成大容量存储设备模式,即将STM32板子模拟成U盘。1cube MX配置步骤首先选择USB使用模式,我们选择MSC模式,如下图所示然后配置系统和USB外设所使…...
旅游建设网站/百度关键词怎么做
我们在开发时,经常需要重复编写很多和业务无关的代码,比如获取指定日期对象、获取本机 IP 地址、校验身份证号、数据加密等。通常我们会把这些代码独立出来,放到 util 包下,作为 工具类 供其他代码调用。相信每位 Java 开发者都自…...
修改wordpress主题/本周新闻热点
这一讲来到了应用层,同时也是协议体系中的顶层。 Application Layer 其实从之前的课程中可以发现,每一个层次都会依赖它的下一层,应用层也一样,它将会应用TCP层的服务,TCP层的服务为应用层提供了可靠、有序的端到端数据…...
网站开发专业就业前系军/南京seo域名
处理JavaScript函数上下文时可能遇到一些问题。在回调函数中(例如事件处理器),函数上下文与预期不符,但可以使用call或apply方法绕过。在本节中,我们看看另外两个选择:箭头函数和bind方法,在一些…...
桂林网站建设公司/孔宇seo
这篇文章记录文件描述符,下一篇文章记录文件描述表。 1 文件描述符 先说说什么是文件IO。文件的IO就是文件的输入输出,也就是文件的读写。读和写是以CPU为参考的,从CPU向文件中写入数据,就是写操作;从文件中读取数据…...