1.Go看源码必会知识之unsafe包
2.python小白必背100源代码_送给小白
3.flv.js源码知识点(下) FLV格式解析
4.Python入门必备知识点有哪些?
5.flv.js源码知识点(中)
6.Excel2007VBA应用程序开发文摘
Go看源码必会知识之unsafe包
前言
有看源码的程序朋友应该会发现,Go标准库中大量使用了unsafe.pointer,源码要想更好的知识理解源码实现,就要知道unsafe.pointer到底是点程什么?所以今天就与大家来聊一聊unsafe包。
什么是序源unsafe众所周知,Go语言被设计成一门强类型的码知下载网页APP源码静态语言,那么他的识点类型就不能改变了,静态也是程序意味着类型检查在运行前就做了。所以在Go语言中是源码不允许两个指针类型进行转换的,使用过C语言的知识朋友应该知道这在C语言中是可以实现的,Go中不允许这么使用是点程处于安全考虑,毕竟强制转型会引起各种各样的序源麻烦,有时这些麻烦很容易被察觉,码知有时他们却又隐藏极深,识点难以察觉。程序大多数读者可能不明白为什么类型转换是不安全的,这里用C语言举一个简单的例子:
int main(){ double pi = 3.;double *pv = πvoid *temp = pd;int *p = temp;}在标准C语言中,任何非void类型的指针都可以和void类型的指针相互指派,也可以通过void类型指针作为中介,实现不同类型的指针间接相互转换。上面示例中,指针pv指向的空间本是一个双精度数据,占8个字节,但是经过转换后,p指向的是一个4字节的int类型。这种发生内存截断的设计缺陷会在转换后进行内存访问是存在安全隐患。我想这就是Go语言被设计成强类型语言的原因之一吧。
虽然类型转换是不安全的,但是在一些特殊场景下,使用了它,可以打破Go的类型和内存安全机制,可以绕过类型系统低效,提高运行效率。所以Go标准库中提供了一个unsafe包,之所以叫这个名字,就是不推荐大家使用,但是不是不能用,如果你掌握的特别好,还是可以实践的。
unsafe 实现原理在使用之前我们先来看一下unsafe的源码部分,标准库unsafe包中只提供了3``种方法,分别是:
func Sizeof(x ArbitraryType) uintptrfunc Offsetof(x ArbitraryType) uintptrfunc Alignof(x ArbitraryType) uintptrSizeof(x ArbitrayType)方法主要作用是用返回类型x所占据的字节数,但并不包含x所指向的内容的大小,与C语言标准库中的Sizeof()方法功能一样,比如在位机器上,一个指针返回大小就是4字节。
Offsetof(x ArbitraryType)方法主要作用是返回结构体成员在内存中的位置离结构体起始处(结构体的第一个字段的偏移量都是0)的字节数,即偏移量,我们在注释中看一看到其入参必须是一个结构体,其返回值是一个常量。
Alignof(x ArbitratyType)的主要作用是返回一个类型的对齐值,也可以叫做对齐系数或者对齐倍数。对齐值是一个和内存对齐有关的值,合理的内存对齐可以提高内存读写的性能。一般对齐值是2^n,最大不会超过8(受内存对齐影响).获取对齐值还可以使用反射包的函数,也就是说:unsafe.Alignof(x)等价于reflect.TypeOf(x).Align()。对于任意类型的变量x,unsafe.Alignof(x)至少为1。对于struct结构体类型的变量x,计算x每一个字段f的unsafe.Alignof(x,f),unsafe.Alignof(x)等于其中的最大值。对于array数组类型的变量x,unsafe.Alignof(x)等于构成数组的元素类型的对齐倍数。没有任何字段的空struct{ }和没有任何元素的array占据的内存空间大小为0,不同大小为0的短线超强波段指标源码变量可能指向同一块地址。
细心的朋友会发发现这三个方法返回的都是uintptr类型,这个目的就是可以和unsafe.poniter类型相互转换,因为*T是不能计算偏移量的,也不能进行计算,但是uintptr是可以的,所以可以使用uintptr类型进行计算,这样就可以可以访问特定的内存了,达到对不同的内存读写的目的。三个方法的入参都是ArbitraryType类型,代表着任意类型的意思,同时还提供了一个Pointer指针类型,即像void *一样的通用型指针。
type ArbitraryType inttype Pointer *ArbitraryType// uintptr 是一个整数类型,它足够大,可以存储type uintptr uintptr上面说了这么多,可能会有点懵,在这里对三种指针类型做一个总结:
*T:普通类型指针类型,用于传递对象地址,不能进行指针运算。
unsafe.poniter:通用指针类型,用于转换不同类型的指针,不能进行指针运算,不能读取内存存储的值(需转换到某一类型的普通指针)
uintptr:用于指针运算,GC不把uintptr当指针,uintptr无法持有对象。uintptr类型的目标会被回收。
三者关系就是:unsafe.Pointer是桥梁,可以让任意类型的指针实现相互转换,也可以将任意类型的指针转换为uintptr进行指针运算,也就说uintptr是用来与unsafe.Pointer打配合,用于指针运算。画个图表示一下:
基本原理就说到这里啦,接下来我们一起来看看如何使用~
unsafe.Pointer基本使用我们在上一篇分析atomic.Value源码时,看到atomic/value.go中定义了一个ifaceWords结构,其中typ和data字段类型就是unsafe.Poniter,这里使用unsafe.Poniter类型的原因是传入的值就是interface{ }类型,使用unsafe.Pointer强转成ifaceWords类型,这样可以把类型和值都保存了下来,方便后面的写入类型检查。截取部分代码如下:
// ifaceWords is interface{ } internal representation.type ifaceWords struct { typunsafe.Pointer data unsafe.Pointer}// Load returns the value set by the most recent Store.// It returns nil if there has been no call to Store for this Value.func (v *Value) Load() (x interface{ }) { vp := (*ifaceWords)(unsafe.Pointer(v))for { typ := LoadPointer(&vp.typ) // 读取已经存在值的类型/**..... 中间省略**/// First store completed. Check type and overwrite data.if typ != xp.typ { //当前类型与要存入的类型做对比 panic("sync/atomic: store of inconsistently typed value into Value")}}上面就是源码中使用unsafe.Pointer的一个例子,有一天当你准备读源码时,unsafe.pointer的使用到处可见。好啦,接下来我们写一个简单的例子,看看unsafe.Pointer是如何使用的。
func main(){ number := 5 pointer := &number fmt.Printf("number:addr:%p, value:%d\n",pointer,*pointer) floatNumber := (*float)(unsafe.Pointer(pointer)) *floatNumber = *floatNumber + 3 fmt.Printf("float:addr:%p, value:%f\n",floatNumber,*floatNumber)}运行结果:
number:addr:0xc, value:5float:addr:0xc, value:3.由运行可知使用unsafe.Pointer强制类型转换后指针指向的地址是没有改变,只是类型发生了改变。这个例子本身没什么意义,正常项目中也不会这样使用。
总结一下基本使用:先把*T类型转换成unsafe.Pointer类型,然后在进行强制转换转成你需要的指针类型即可。
Sizeof、Alignof、Offsetof三个函数的基本使用先看一个例子:
type User struct { Name string Age uint Gender bool // 男:true 女:false 就是举个例子别吐槽我这么用。。。。}func func_example(){ // sizeof fmt.Println(unsafe.Sizeof(true)) fmt.Println(unsafe.Sizeof(int8(0))) fmt.Println(unsafe.Sizeof(int())) fmt.Println(unsafe.Sizeof(int())) fmt.Println(unsafe.Sizeof(int())) fmt.Println(unsafe.Sizeof("asong")) fmt.Println(unsafe.Sizeof([]int{ 1,3,4})) // Offsetof user := User{ Name: "Asong", Age: ,Gender: true} userNamePointer := unsafe.Pointer(&user) nNamePointer := (*string)(unsafe.Pointer(userNamePointer)) *nNamePointer = "Golang梦工厂" nAgePointer := (*uint)(unsafe.Pointer(uintptr(userNamePointer) + unsafe.Offsetof(user.Age))) *nAgePointer = nGender := (*bool)(unsafe.Pointer(uintptr(userNamePointer)+unsafe.Offsetof(user.Gender))) *nGender = false fmt.Printf("u.Name: %s, u.Age: %d,u.Gender: %v\n", user.Name, user.Age,user.Gender) // Alignof var b bool var i8 int8 var i int var i int var f float var s string var m map[string]string var p *int fmt.Println(unsafe.Alignof(b)) fmt.Println(unsafe.Alignof(i8)) fmt.Println(unsafe.Alignof(i)) fmt.Println(unsafe.Alignof(i)) fmt.Println(unsafe.Alignof(f)) fmt.Println(unsafe.Alignof(s)) fmt.Println(unsafe.Alignof(m)) fmt.Println(unsafe.Alignof(p))}为了省事,把三个函数的使用示例放到了一起,首先看sizeof方法,我们可以知道各个类型所占字节大小,这里重点说一下int类型,Go语言中的int类型的具体大小是跟机器的 CPU位数相关的。如果 CPU 是 位的,那么int就占4字节,如果 CPU是django工作流源码位的,那么 int 就占8 字节,这里我的电脑是位的,所以结果就是8字节。
然后我们在看Offsetof函数,我想要修改结构体中成员变量,第一个成员变量是不需要进行偏移量计算的,直接取出指针后转换为unsafe.pointer,在强制给他转换成字符串类型的指针值即可。如果要修改其他成员变量,需要进行偏移量计算,才可以对其内存地址修改,所以Offsetof方法就可返回成员变量在结构体中的偏移量,也就是返回结构体初始位置到成员变量之间的字节数。看代码时大家应该要住uintptr的使用,不可以用一个临时变量存储uintptr类型,前面我们提到过用于指针运算,GC不把uintptr当指针,uintptr无法持有对象。uintptr类型的目标会被回收,所以你不知道他什么时候会被GC掉,那样接下来的内存操作会发生什么样的错误,咱也不知道。比如这样一个例子:
// 切记不要这样使用p1 := uintptr(userNamePointer)nAgePointer := (*uint)(unsafe.Pointer(p1 + unsafe.Offsetof(user.Age)))最后看一下Alignof函数,主要是获取变量的对齐值,除了int、uintptr这些依赖CPU位数的类型,基本类型的对齐值都是固定的,结构体中对齐值取他的成员对齐值的最大值,结构体的对齐涉及到内存对齐,我们在下面详细介绍。
经典应用:string与[]byte的相互转换实现string与byte的转换,正常情况下,我们可能会写出这样的标准转换:
// string to []bytestr1 := "Golang梦工厂"by := []byte(s1)// []byte to stringstr2 := string(by)使用这种方式进行转换都会涉及底层数值的拷贝,所以想要实现零拷贝,我们可以使用unsafe.Pointer来实现,通过强转换直接完成指针的指向,从而使string和[]byte指向同一个底层数据。在reflect包中有·string和slice对应的结构体,他们的分别是:
type StringHeader struct { Data uintptr Lenint}type SliceHeader struct { Data uintptr Lenint Capint}StringHeader代表的是string运行时的表现形式(SliceHeader同理),通过对比string和slice运行时的表达可以看出,他们只有一个Cap字段不同,所以他们的内存布局是对齐的,所以可以通过unsafe.Pointer进行转换,因为可以写出如下代码:
func Sizeof(x ArbitraryType) uintptrfunc Offsetof(x ArbitraryType) uintptrfunc Alignof(x ArbitraryType) uintptr0上面的代码我们通过重新构造slice header和string header完成了类型转换,其实[]byte转换成string可以省略掉自己构造StringHeader的方式,直接使用强转就可以,因为string的底层也是[]byte,强转会自动构造,省略后的代码如下:
func Sizeof(x ArbitraryType) uintptrfunc Offsetof(x ArbitraryType) uintptrfunc Alignof(x ArbitraryType) uintptr1虽然这种方式更高效率,但是不推荐大家使用,前面也提高到了,这要是不安全的,使用当不当会出现极大的隐患,一些严重的情况recover也不能捕获。
内存对齐现在计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但是实际情况是在访问特定类型变量的时候经常在特定的内存地址访问,这就需要各种类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就对齐。
对齐的作用和原因:CPU访问内存时,并不是逐个字节访问,而是以字长(word size)单位访问。比如位的CPU,字长为4字节,那么CPU访问内存的庄家主买指标源码单位也是4字节。这样设计可以减少CPU访问内存的次数,加大CPU访问内存的吞吐量。假设我们需要读取8个字节的数据,一次读取4个字节那么就只需读取2次就可以。内存对齐对实现变量的原子性操作也是有好处的,每次内存访问都是原子的,如果变量的大小不超过字长,那么内存对齐后,对该变量的访问就是原子的,这个特性在并发场景下至关重要。
我们来看这样一个例子:
func Sizeof(x ArbitraryType) uintptrfunc Offsetof(x ArbitraryType) uintptrfunc Alignof(x ArbitraryType) uintptr2从结果可以看出,字段放置不同的顺序,占用内存也不一样,这就是因为内存对齐影响了struct的大小,所以有时候合理的字段可以减少内存的开销。下面我们就一起来分析一下内存对齐,首先要明白什么是内存对齐的规则,C语言的对齐规则与Go语言一样,所以C语言的对齐规则对Go同样适用:
对于结构的各个成员,第一个成员位于偏移为0的位置,结构体第一个成员的偏移量(offset)为0,以后每个成员相对于结构体首地址的 offset 都是该成员大小与有效对齐值中较小那个的整数倍,如有需要编译器会在成员之间加上填充字节。
除了结构成员需要对齐,结构本身也需要对齐,结构的长度必须是编译器默认的对齐长度和成员中最长类型中最小的数据大小的倍数对齐。
好啦,知道规则了,我们现在来分析一下上面的例子,根据我的mac使用的位CPU,对齐参数是8来分析,int、[]int、string、bool对齐值分别是4、8、8、1,占用内存大小分别是4、、、1,我们先根据第一条对齐规则分析User1:
第一个字段类型是int,对齐值是4,大小为4,所以放在内存布局中的第一位.
第二个字段类型是[]int,对齐值是8,大小为,所以他的内存偏移值必须是8的倍数,所以在当前user1中,就不能从第4位开始了,必须从第5位开始,也就偏移量为8。第4,5,6,7位由编译器进行填充,一般为0值,也称之为空洞。第9位到第位为第二个字段B.
第三个字段类型是string,对齐值是8,大小为,所以他的内存偏移值必须是8的倍数,因为user1前两个字段就已经排到了第位,所以下一位的偏移量正好是,正好是字段C的对齐值的倍数,不用填充,可以直接排列第三个字段,也就是从第位到位第三个字段C.
第三个字段类型是bool,对齐值是大闸蟹溯源码可查几次1,大小为1,所以他的内存偏移值必须是1的倍数,因为user1前两个字段就已经排到了第位,所以下一位的偏移量正好是。正好是字段D的对齐值的倍数,不用填充,可以直接排列到第四个字段,也就是从到第位是第三个字段D.
好了现在第一条内存对齐规则后,内存长度已经为字节,我们开始使用内存的第2条规则进行对齐。根据第二条规则,默认对齐值是8,字段中最大类型程度是,取最小的那一个,所以求出结构体的对齐值是8,我们目前的内存长度是,不是8的倍数,所以需要补齐,所以最终的结果就是,补了7位。
说了这么多,画个图看一下吧:
现在你们应该懂了吧,按照这个思路再去分析其他两个struct吧,这里就不再分析了。
对于内存对齐这里还有一最后需要注意的知识点,空struct不占用任何存储空间,空 struct{ } 大小为 0,作为其他 struct 的字段时,一般不需要内存对齐。但是有一种情况除外:即当 struct{ } 作为结构体最后一个字段时,需要内存对齐。因为如果有指针指向该字段, 返回的地址将在结构体之外,如果此指针一直存活不释放对应的内存,就会有内存泄露的问题(该内存不因结构体释放而释放)。来看一个例子:
func Sizeof(x ArbitraryType) uintptrfunc Offsetof(x ArbitraryType) uintptrfunc Alignof(x ArbitraryType) uintptr3简单来说,对于任何占用0大小空间的类型,像struct { }或者[0]byte这些,如果该类型出现在结构体末尾,那么我们就假设它占用1个字节的大小。因此对于test1结构体,他看起来就是这样:`
func Sizeof(x ArbitraryType) uintptrfunc Offsetof(x ArbitraryType) uintptrfunc Alignof(x ArbitraryType) uintptr4因此在内存对齐时,最后结构体占用的字节就是8了。
重点要注意的问题:不要在结构体定义的最后添加零大小的类型
总结好啦,终于又到文章的末尾了,我们来简单的总结一下,unsafe 包绕过了 Go 的类型系统,达到直接操作内存的目的,使用它有一定的风险性。但是在某些场景下,使用 unsafe 包提供的函数会提升代码的效率,Go 源码中也是大量使用 unsafe 包。
unsafe 包定义了 Pointer 和三个函数:
type ArbitraryType inttype Pointer *ArbitraryTypefunc Sizeof(x ArbitraryType) uintptrfunc Offsetof(x ArbitraryType) uintptrfunc Alignof(x ArbitraryType) uintptruintptr 可以和 unsafe.Pointer 进行相互转换,uintptr 可以进行数学运算。这样,通过 uintptr 和 unsafe.Pointer 的结合就解决了 Go 指针不能进行数学运算的限制。通过 unsafe 相关函数,可以获取结构体私有成员的地址,进而对其做进一步的读写操作,突破 Go 的类型安全限制。
最后我们又学习了内存对齐的知识,这样设计可以减少CPU访问内存的次数,加大CPU访问内存的吞吐量,所以结构体中字段合理的排序可以更节省内存,注意:不要在结构体定义的最后添加零大小的类型。
原文:/post/好啦,这篇文章就到这里啦,素质三连(分享、点赞、在看)都是笔者持续创作更多优质内容的动力!
创建了一个Golang学习交流群,欢迎各位大佬们踊跃入群,我们一起学习交流。入群方式:加我vx拉你入群,或者公众号获取入群二维码
结尾给大家发一个小福利吧,最近我在看[微服务架构设计模式]这一本书,讲的很好,自己也收集了一本PDF,有需要的小伙可以到自行下载。获取方式:关注公众号:[Golang梦工厂],后台回复:[微服务],即可获取。
我翻译了一份GIN中文文档,会定期进行维护,有需要的小伙伴后台回复[gin
python小白必背源代码_送给小白
Python编程入门时,掌握基本的代码规范至关重要。首先,理解缩进规则是关键,Python依赖缩进来组织代码结构,如类定义和条件语句,不正确的缩进可能导致语法错误,如例子中的IF语句需要正确使用冒号和缩进来确保其可执行性。
其次,新手要避免错误地使用类变量。类变量在Python中存储在类的命名空间,而不是每个实例中,理解命名空间和方法解析顺序(MRO)有助于避免混淆,如改变A.x的值,并不会影响继承自A的其他类的x值。
Python的范围规则也很重要,LEGB规则规定了变量查找的顺序,理解这一点可以避免在函数内部访问变量时出现未定义变量的错误。例如,局部变量的定义会影响全局作用域中的变量访问。
闭包变量绑定问题也是易混淆点,Python的闭包使用时要关注变量绑定的时间,理解迟绑定机制有助于正确处理匿名函数中的变量引用。
避免与Python标准库模块名称冲突,以及清晰区分is、==和=的含义,能帮助避免常见的编程陷阱。is检查对象引用,==比较内容,而=是赋值操作。
最后,理解构造函数__init__的作用和用法,特别是当重写它时如何正确调用父类的初始化方法,是提高代码可维护性的基础。
对于所有这些关键知识点,有兴趣的朋友可通过链接获取Python、Java、大数据、web前端和人工智能的教程,或关注程序员子木公众号获取更多资源。
flv.js源码知识点(下) FLV格式解析
flv.js系列三:FLV格式解析
此篇文章为flv.js源码知识点系列的终篇,旨在深入解析FLV文件的格式。在理解FLV文件数据结构及如何在JavaScript中读取特定二进制数据的基础上,文章将引导读者逐步构建对FLV文件解析的全面认知。
FLV格式解析主要涉及两个关键部分:FLVHeader和FLVBody。FLVHeader为文件的前导部分,固定长度为9字节,其结构定义了文件的后续部分。FLVBody包含多个Tag,每个Tag由TagHeader和TagData组成,Tag的结构为字节,体现了FLV文件的层次化和可扩展性。
在进行FLV文件解析时,二进制数据读取API显得尤为重要,特别是DateView类的使用。DateView允许以位级别访问ArrayBuffer中的数据,提供了读取、写入以及转换数据类型的能力,极大地简化了二进制数据的处理流程。
具体而言,DateView提供了构造函数new DataView,用于指定数组缓冲区、偏移量和长度。获取数据时,可以通过getUint8、getUint等方法,灵活地读取不同长度的整数。此外,了解字节序(大字节序与小字节序)的概念及其对数据读取的影响,对于正确解析FLV文件至关重要。
位操作是二进制数据处理的另一大利器,包括按位非、按位与、按位或、按位异或以及位移操作等。这些操作允许在位级别上进行复杂的数据提取和重组,对于处理如FLV标签中的时间戳拼接等特定场景尤为关键。
最后,文章强调了结合FLV格式文档和二进制数据读取技术进行解析的重要性。通过解析每个字段,开发者可以有效地理解和处理FLV文件中的音视频数据,为后续的音视频解码、传输和播放提供坚实基础。
通过本系列文章的学习,读者不仅掌握了flv.js源码的解析原理,还深入理解了FLV文件格式的内在结构与处理方法,为音视频开发工作打下坚实的技术基础。
Python入门必备知识点有哪些?
Python入门必备知识点1、什么是Python? 使用Pvthon的优缺点有哪些?
Python是一种面向对象的解释性的交互式语言,带有对象、模块、线程、异常和内存自动管理的机制。使用Python的优点有:简单、易学、轻便可移植、可扩展、可读性具有多种内建数据类型、开源等等。使用Python的缺点有:运行速度慢,代码不能加密(解释性语言,发布python应用只能发布源代码,不像C发布编译后的应用文件)
2、什么是PEP 8?
PEP8是一种编码规范,是一系列关于如何让Python代码更加具有可读性的编码建议。
3、什么是序列化和反序列化?
序列化:将在内存中的变量转为可存储或传输的过程。应用JSON传输,序列化为统一格式ison反序列化:与序列化的过程相反Pickle模块允许我们将Python对象转换成一个string表示的信息,并且可以使用dump函数将其保存到一个文件中去,这样的过程称为序列化。而使用文件中保存的对象信息重构Python对象的过程称为反序列化。
4、Python是如何被解释执行的?
Pvthon是一种解释型语言,它可以直接从源代码运行程序。程序员编写的源代码首先转变成一种中间语言代码,然后再被转换成能够直接执行的机器语言代码。
5.Python是如何进行内存管理的?
Python内存空间是以Python私有堆的形式进行管理的。所有的Python对象和数据结构都存放在一个私有堆中。解释器可以访问私有堆,而程序员不可以。将Pvthon堆空间中的内存分配给Pvthon对象的工作是由Python内存管理器完成的。而内核API则会提供给程序员一些相关的工具来完成涉及到内存的编码工作。
6.能够找到程序中的bug进行静态分析的工具有哪些?
Python还内置垃圾回收器,从而进行回收释放内存到堆空间能够找到程序中的bug进行静态分析的工具有哪些?6、PyChecker是一种能够发现Python源代码中的bug并对其中的代码风格和复杂度进行警告的静态分析工具。另外一种工具是Pylint,它能够验证模块是否满足编码标准。
7、什么是Python装饰器?
用来装饰一个函数从而使这个函数在不修改本身的函数定义外,动态产生额外的功能;装饰函数的入参为需要被装饰的函数。装饰器本质上是一个返回函数的高阶函数一个Python装饰器是我们在符合Python语法的同时又方便地对函数做的一些特定的修改。
8、列表、元组、集合、字典的区别是什么?
列表:元素可变(任何数据类型),有序(可索引)append/insert/pop;元组:元素不可变但元素中的可变元素是可变的;有序(可索引);而且组可以被散列,例如作为字典的键。集合:无序(不可被索引)、互异字典:无序,键值对(key: value),key唯一不可重复
9、怎样理解字典和列表?
它们是一种用来创建可变对象的语法结构,数据容器dic插入、查找速度比list快,不会随key的增加而增加,但list会随着元素的增加而增加(偏移量)
、参数是如何通过值或者引用传递的?
python中确切说应该是“传对象引用”的方式,引用对象传递。一切皆对象,参数皆引用如果函数收到的是一个可变对象(比如列表、字典)的引用,则可修改对象的原始值==相当于“引用传递”方式如果函数收到的是一个不可变对象(比如数字、字符、元组),就不能直接修改原始对象指向的值==相当于“值传递”方式。
flv.js源码知识点(中)
本文续接上篇,深入探讨flv.js的内部机制,特别是网速计算和数据缓存处理。在播放过程中,用户网速的稳定性对体验至关重要。flv.js通过statistics_info事件实时反馈当前网速,speed字段以KB/s为单位。计算过程巧妙地利用时间差值,确保准确反映最近一秒的数据传输速率,而非上一秒。
网速计算的关键在于addBytes方法,它通过比较当前时间和上一次计算时间的差异,来动态调整网速估计。currentKBps属性仅在durationSeconds大于0.5时使用,以减少误差。平均网速averageKBps会在网络中断或暂停时受到影响。
数据缓存处理涉及loader获取数据后至FLVDemuxer的中间环节。FLV格式数据以TAG为单位,因此需要将连续字节缓存起来,直到遇到完整的TAG。这个过程在IOController中实现,涉及ArrayBuffer的二进制缓存操作,如使用Uint8Array存储和处理数据。文章详细介绍了缓存扩展和消费的方法,以及如何根据网速动态调整缓存大小。
后续内容将涉及FLV格式的解析和位操作。请继续关注,以了解更多flv.js源码的深层次知识。
原文链接已删除,如果你对C++音视频开发感兴趣,可以搜索相关资源进行学习。
ExcelVBA应用程序开发文摘
Excel是Microsoft Office不可或缺的组件,Office系列还包括Word、PowerPoint等。Office内建的开发平台和工具允许用户进行深度定制,即二次开发。以下是关于Excel VBA应用程序开发的一些关键知识点: 1.1 VBA基础与Excel应用 VBA,全称Visual Basic for Application,是由微软为在其桌面应用中实现自动化任务而设计的编程语言。Excel作为最早支持VBA的Office组件,为用户提供了丰富的定制潜力。VBA应用程序,如"企业人事管理系统"和"股票理财系统"(如图1-1所示),正是利用Excel VBA进行开发,以提升工作效率和实用性。 1.1.1 开发环境入门 要开始Excel VBA开发,首先打开VBE(Visual Basic Editor)窗口,这是一个用于编写、调试和修改Excel VBA代码的集成环境。VBE界面直观,概述了主要功能和工具,帮助开发者快速上手。 1.2 VBA编程基础 在VBA中,数据类型、常量和变量的设置至关重要。理解运算符和程序控制语句,如顺序执行、条件判断和循环,是编写高效代码的基础。此外,VBA数组、Sub过程(用户自定义函数)和Function函数(返回值的函数)的运用,以及数据输入输出对话框函数的掌握,是VBA编程的重要组成部分。 1.3 Excel对象和应用流程 Excel VBA应用广泛,涵盖Application对象(管理整个Excel实例)、Workbook对象(操作工作簿)、Worksheet对象(操作工作表)、Range对象(处理单元格区域)和Chart对象(图形处理)。开发流程通常包括需求分析、设计、编写代码、测试和调试,直到完成一个功能完备的Excel VBA应用程序。扩展资料
本书通过9个综合案例(福利**系统、股票理财系统、员工考勤管理系统、员工工资条管理系统、销售数据图表分析系统、企业人事管理系统、文件管理系统、企业库存管理系统和企业员工信息管理系统)来讲解如何利用Excel VBA解决办公应用中复杂问题的相应方法与技巧。本书案例经典、内容全面、技术实用、资源丰富,读者在深入研究本书后,能够提高自己的编程水平,并且可以使开发出的系统符合实际商业开发的要求。在华信教育资源网上可免费下载本书案例的源代码。初用编译器必备基础知识丨编译器相关的几个知识点!
在编程过程中,理解一些基本概念对于使用IDE至关重要。本文将简要介绍几个关键点,帮助你更好地掌握。源文件(Source File)
开发软件时,我们将代码存储在特定的文件中,称为源文件。每种编程语言有自己的后缀标识,如C语言的.c,C++的.cpp,Java的.java,Python的.py,JavaScript的.js。源文件本质上是文本文件,即使用记事本创建的txt文件改名后缀,编译器也能识别其代码。工程/项目(Project)
一个大型程序包含多个功能,需要分散在不同源文件中管理。为了便于管理,程序员会将代码、资源文件归类到一个特定的目录,即IDE中的“项目”或“工程”。IDE会为每个项目创建一个专属文件夹,提供编辑、重命名等操作。工程类型/项目类型
程序类型有控制台应用、GUI应用和库等。选择正确的工程类型决定了程序的外观和功能。例如,控制台应用适合初学者,GUI应用则更复杂。IDE支持多种工程类型,允许自定义设置。链接(Link)
源代码经过编译后通常会产生中间文件,这些文件还需与系统库结合,通过链接器形成可执行文件。即使只有一个源文件,也需要编译和链接步骤来生成最终可执行文件。 无论你是编程新手还是进阶者,理解这些基础概念将有助于你的编程旅程。进一步学习和实践,可以从我的编程学习交流专栏获取更多资源。Pythonå¼åè¦ç¨å°å¤å°ç¥è¯ç¹
导读ï¼æ¬ç¯æç« é¦å¸CTOç¬è®°æ¥ç»å¤§å®¶ä»ç»æå ³Pythonå¼åè¦ç¨å°å¤å°ç¥è¯ç¹çç¸å ³å 容ï¼å¸æ对大家ææ帮å©ï¼ä¸èµ·æ¥ççå§ãæ¬æç®å½ä¸è§ï¼
1ãpython webå¼åéè¦å¦åªäºä¸è¥¿2ãpythonå端å¼åéè¦å¦åªäºå 容?3ãpython主è¦å¦ä¹ åªäºç¥è¯ç¹ï¼4ãå¦pythonéè¦ä»ä¹åºç¡ç¥è¯5ãPythoné½éè¦é£äºææ¯ï¼6ãpythonå·¥ç¨å¸éè¦ææ¡ä»ä¹ç¥è¯python webå¼åéè¦å¦åªäºä¸è¥¿ä»ä¹æ¯webå¼åå¢ï¼å ¶å®å°±æ¯å¼åä¸ä¸ªç½ç«äºãé£å¼åç½ç«éè¦ç¨å°åªäºç¥è¯å¢
1ãpythonåºç¡ï¼å 为ç¨pythonå¼åçï¼æ以pythonæå®è¦ä¼ï¼æèµ·ç ä½ ä¹å¾ä¼æ¡ä»¶å¤æï¼å¾ªç¯ï¼å½æ°ï¼ç±»è¿äºç¥è¯ï¼
2ãhtmlãcssçåºç¡ç¥è¯ï¼å 为è¦å¼åç½ç«ï¼ç½é¡µé½htmlåcssåçï¼æèµ·ç è¿äºç¥è¯ä½ å¾ä¼ï¼å°±ç®ä¸ä¼åå端ï¼å¼åä¸åºæ¥ç¹å«æ¼äº®ç页é¢ï¼ç½ç«ï¼æèµ·ç è¦è½çæhtmlæ ç¾æ¯ï¼
3ãæ°æ®åºåºç¡ç¥è¯ï¼å 为å¼åä¸ä¸ªç½ç«çè¯ï¼æ°æ®åå¨åªéï¼å°±æ¯å¨æ°æ®åºéï¼é£ä½ æèµ·ç è¦ä¼æ°æ®åºçå¢å æ¹æ¥å§ï¼è¦ä¸ç¶æä¹åæ°æ®ï¼åæ°æ®å¢
ä¸é¢è¿äºç¥è¯ä¼çè¯ï¼å°±åºæ¬ä¸å¯ä»¥äºï¼å¯ä»¥å¼åä¸ä¸ªç®åçå°ç«å°±æ²¡æé®é¢äºï¼å¦ææ³å¼åæ¯è¾å¤§åçç½ç«ï¼ä¸å¡é»è¾æ¯è¾å¤æçï¼é£å°±å¾ç¨å°å ¶ä»çç¥è¯äºï¼æ¯å¦è¯´redisãMQççã
å¼ååçåå¤ï¼
1ãå®è£ 好 python3.5çæ¬
2ãå®è£ 好pycharmï¼æ¯å±ä»¬çå¼åå·¥å ·
3ãå®è£ 好djangoï¼çæ¬1.9ï¼ä½¿ç¨pip install django==1.9å½ä»¤å®è£ å³å¯
4ãå®è£ 好navicatæè å ¶ä»æä½æ°æ®åºçå¯è§åå·¥å ·ï¼å 为å±ä»¬è¦ç¨å°æ°æ®åºï¼è¦çæ°æ®åºãæ¹æ°æ®
åé¢å±ä»¬å®è£ äºdjangoï¼djangoæ¯ä¸ä¸ªpythonå¼åçwebæ¡æ¶ï¼å¤§èå ¨ï¼å ä¹ä½ ç¨å°çæ¹æ³ï¼å®å ¨é½æï¼å®éé¢å·²ç»å®ç°äºæåæ说çææçåè½ï¼å±ä»¬åªéè¦ç¨æå®æ¿è¿æ¥ç¨å°±å¯ä»¥äºãå½ç¶pythonè¿æå¾å¤webå¼åæ¡æ¶ï¼æ¯å¦è¯´web.pyãflaskãtornadoãbottleççï¼ä»ä»¬å¨åçä¸ådjangoä¸æ ·ï¼ä½æ¯ä»ä»¬çåè½é½æ²¡ædjangoå¤ï¼djangoä¹æ¯ç¤¾åºæ大ï¼æ´»è·ç¨æ·æå¤çä¸ä¸ªæ¡æ¶ã
pythonå端å¼åéè¦å¦åªäºå 容?
Pythonçå¦ä¹ å 容è¿æ¯æ¯è¾å¤çï¼æ们å°å¦ä¹ çè¿ç¨åå为4个é¶æ®µï¼æ¯ä¸ªé¶æ®µå¦ä¹ 对åºçå 容ï¼å ·ä½çå¦ä¹ 顺åºå¦ä¸ï¼
Pythonå¦ä¹ 顺åºï¼
â Python软件å¼ååºç¡
ææ¡è®¡ç®æºçææåå·¥ä½åç
ä¼ä½¿ç¨Linux常ç¨å·¥å ·
çç»ä½¿ç¨Dockerçåºæ¬å½ä»¤
建ç«Pythonå¼åç¯å¢ï¼å¹¶ä½¿ç¨printè¾åº
使ç¨Pythonå®æå符串çåç§æä½
使ç¨Python re模åè¿è¡ç¨åºè®¾è®¡
使ç¨Pythonå建æ件ã访é®ãå é¤æ件
ææ¡import è¯å¥ãFromâ¦import è¯å¥ãFromâ¦import* è¯å¥ãæ¹æ³çå¼ç¨ãPythonä¸çå
â¡Python软件å¼åè¿é¶
è½å¤ä½¿ç¨Pythoné¢å对象æ¹æ³å¼å软件
è½å¤èªå·±å»ºç«æ°æ®åºï¼è¡¨ï¼å¹¶è¿è¡åºæ¬æ°æ®åºæä½
ææ¡éå ³ç³»æ°æ®åºMongoDBç使ç¨ï¼ææ¡Rediså¼å
è½å¤ç¬ç«å®æTCP/UDPæå¡ç«¯å®¢æ·ç«¯è½¯ä»¶å¼åï¼è½å¤å®ç°ftpãpiler\build.js
AST抽象语法树的知识点作为JavaScript中(任何编程语言中都有ast这个概念,这里就不过多赘述)相对基础的,也是最不可忽略的知识,带给我们的启发是无限可能的,它就像一把螺丝刀,能够拆解javascript这台庞大的机器,让我们能够看到一些本质的东西,同时也能通过它批量构建任何javascript代码。 小时候梦想改变世界,如今我们可以用自己写的程序,构建出我们所生活的网络世界,丰富多姿。 借用一句歌词: 我还是从前那个少年,没有一丝丝改变。时间只不过是考验,种在心中信念丝毫未减 。希望大家能够在软件开发的路途上坚定信念,越走越远.....