十年网站开发经验 + 多家企业客户 + 靠谱的建站团队
量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决
/*Note:YourchoiceisCIDE*/#include"stdio.h"#include"stdlib.h"#include"ctype.h"intn=0;charrecord[20];floatproduct();floatchange();floatmuli(){floatsumm;summ=product();while(record[n]=='-'||record[n]=='+'){switch(record[n]){case'+':n++;summ+=product();break;case'-':n++;summ-=product();break;}}returnsumm;}floatproduct(){floatsump;sump=change();while(record[n]=='*'||record[n]=='/'){switch(record[n]){case'*':n++;sump*=change();break;case'/':n++;sump/=change();break;}}returnsump;}floatchange(){floatsumc;charrec[20],i=0;if(record[n]=='('){n++;sumc=muli();}if(record[n]==')')n++;while(isdigit(record[n])||record[n]=='.'){while(isdigit(record[n])||record[n]=='.')rec[i++]=record[n++];rec[i]='\0';sumc=atof(rec);}returnsumc;}voidmain(){while(1){n=0;scanf("%s",record);printf("%s=%g\n",record,muli());}}这个是比较简单的。。。但是有点难理解。。。不过运行绝对正确。。还支持括号。。。但是在这个程序里面我加没有出错处理。。。另一种方法是用栈写。。。这个好理解。。但是麻烦。。。
成都网站建设、网站建设的关注点不是能为您做些什么网站,而是怎么做网站,有没有做好网站,给成都创新互联公司一个展示的机会来证明自己,这并不会花费您太多时间,或许会给您带来新的灵感和惊喜。面向用户友好,注重用户体验,一切以用户为中心。
为了确保一致性构建,Go引入了go.mod文件来标记每个依赖包的版本,在构建过程中go命令会下载go.mod中的依赖包,下载的依赖包会缓存在本地,以便下次构建。 考虑到下载的依赖包有可能是被黑客恶意篡改的,以及缓存在本地的依赖包也有被篡改的可能,单单一个go.mod文件并不能保证一致性构建。
为了解决Go module的这一安全隐患,Go开发团队在引入go.mod的同时也引入了go.sum文件,用于记录每个依赖包的哈希值,在构建时,如果本地的依赖包hash值与go.sum文件中记录得不一致,则会拒绝构建。
go.sum文件记录
go.sum文件中每行记录由module名、版本和哈希组成,并由空格分开:
比如,某个go.sum文件中记录了github点抗 /google/uuid 这个依赖包的v1.1.2版本的哈希值:
正常情况下,每个依赖包版本会包含两条记录,第一条记录为该依赖包版本整体(所有文件)的哈希值,第二条记录表示该依赖包版本中go.mod文件的哈希值,如果该依赖包版本没有go.mod文件,则只有第一条记录。如上面的例子中,v1.1.2表示该依赖包版本整体,而v1.1.2/go.mod表示该依赖包版本中go.mod文件。
依赖包版本中任何一个文件(包括go.mod)改动,都会改变其整体哈希值,此处再额外记录依赖包版本的go.mod文件主要用于计算依赖树时不必下载完整的依赖包版本,只根据go.mod即可计算依赖树。
每条记录中的哈希值前均有一个表示哈希算法的h1:,表示后面的哈希值是由算法SHA-256计算出来的
go.sum文件中记录的依赖包版本数量往往比go.mod文件中要多,这是因为二者记录的粒度不同导致的。go.mod只需要记录直接依赖的依赖包版本,只在依赖包版本不包含go.mod文件时候才会记录间接依赖包版本,而go.sum则是要记录构建用到的所有依赖包版本。
生成
当我们在GOMODULE模式下引入一个新的依赖时,通常会使用go get命令获取该依赖,比如:
go get命令首先会将该依赖包下载到本地缓存目录$GOPATH/pkg/mod/cache/download,该依赖包为一个后缀为.zip的压缩包,如v1.0.0.zip。go get下载完成后会对该.zip包做哈希运算,并将结果存放在后缀为.ziphash的文件中,如v1.0.0.ziphash。如果在项目的根目录中执行go get命令的话,go get会同步更新go.mod和go.sum文件,go.mod中记录的是依赖名及其版本,如:
go.sum文件中则会记录依赖包的哈希值(同时还有依赖包中go.mod的哈希值),如:
在更新go.sum之前,为了确保下载的依赖包是真实可靠的,go命令在下载完依赖包后还会查询GOSUMDB环境变量所指示的服务器,以得到一个权威的依赖包版本哈希值。如果go命令计算出的依赖包版本哈希值与GOSUMDB服务器给出的哈希值不一致,go命令将拒绝向下执行,也不会更新go.sum文件。
go.sum存在的意义在于,希望别人或者在别的环境中构建当前项目时所使用依赖包跟go.sum中记录的是完全一致的,从而达到一致构建的目的。
校验
假设我们拿到某项目的源代码并尝试在本地构建,go命令会从本地缓存中查找所有go.mod中记录的依赖包,并计算本地依赖包的哈希值,然后与go.sum中的记录进行对比,即检测本地缓存中使用的依赖包版本是否满足项目go.sum文件的期望。
如果校验失败,说明本地缓存目录中依赖包版本的哈希值和项目中go.sum中记录的哈希值不一致,go命令将拒绝构建。 这就是go.sum存在的意义,即如果不使用期望的版本,就不能构建。
校验和数据库
环境变量GOSUMDB标识一个checksum database,即校验和数据库,实际上是一个web服务器,该服务器提供查询依赖包版本哈希值的服务。
该数据库中记录了很多依赖包版本的哈希值,比如Google官方的sum.golang.org则记录了所有的可公开获得的依赖包版本。除了使用官方的数据库,还可以指定自行搭建的数据库,甚至干脆禁用它(export GOSUMDB=off)。
如果系统配置了GOSUMDB,在依赖包版本被写入go.sum之前会向该数据库查询该依赖包版本的哈希值进行二次校验,校验无误后再写入go.sum。
如果系统禁用了GOSUMDB,在依赖包版本被写入go.sum之前则不会进行二次校验,go命令会相信所有下载到的依赖包,并把其哈希值记录到go.sum中。
在本节中,您将添加通用函数调用的修改版本,进行小的更改以简化调用代码。您将删除在这种情况下不需要的类型参数。
当 Go 编译器可以推断您要使用的类型时,您可以在调用代码中省略类型参数。编译器从函数参数的类型推断类型参数。
请注意,这并不总是可能的。例如,如果您需要调用没有参数的泛型函数,则需要在函数调用中包含类型参数。
在 main.go 中,在您已有的代码下方,粘贴以下代码。
在此代码中:
(1)调用泛型函数,省略类型参数。
从包含 main.go 的目录中的命令行,运行代码。
接下来,您将通过将整数和浮点数的并集捕获到您可以重用的类型约束(例如从其他代码中)来进一步简化函数。
正如您将在本节中看到的,约束接口也可以引用特定类型。
1、编写代码
在此代码中:
b.在您已有的函数下方,粘贴以下通用 SumNumbers函数。
在此代码中:
c.在 main.go 中,在您已有的代码下方,粘贴以下代码。
在此代码中:
(1)调用SumNumbers打印每个map的总和。
与上一节一样,在调用泛型函数时省略了类型参数(方括号中的类型名称)。Go 编译器可以从其他参数推断类型参数。
从包含 main.go 的目录中的命令行,运行代码。
做得很好!您刚刚学习了 Go 中的泛型。
1、值接收者和指针接收者
所谓指针接收者和值接收者这两个概念,用GO写了一阵子代码的人都了解了,这里只做简要说明一下,也就是对于一个给定结构,咱们对结构进行方法包装的时候,固定必传的参数,用来指向这个对象结构自身的一个参数,在go中也就是形式如下:
我们对结构体testStruct进行了包装,提供了两个方法,sum和modify,其中sum的方法接收者为a testStruct,这个就是值接收者,而modify的接收者为a *testStruct就是指针接收者,也就是说固定对象指针,一个传递的是指针地址,而另外一个直接传递的是结构值拷贝了
对指针有一定了解的,都可以知道,指针传递过去的,可以直接修改结构内部内容,而值传递过去的,无论如何修改这个接收者的数据,不会对原对象结构产生影响。而对于咱们包装结构对象的时候,到底是使用指针还是使用值接收者,这个实际上没有太大的定论,就我个人的观点来说,如果结构体占有的内存空间不大(kb级别),而又不需要修改内部的,同时结构对象内部没有同步对象比如(sync包中的mutex,rwlock,waitgroup等之类的结构的话,可以直接值传递,实际上值copy也没有咱们想象的那么慢,很多时候,都用指针,最后的gc回收扫描可能都比咱们这个传递copy的消耗大) p="" /kb级别),而又不需要修改内部的,同时结构对象内部没有同步对象比如(sync包中的mutex,rwlock,waitgroup等之类的结构的话,可以直接值传递,实际上值copy也没有咱们想象的那么慢,很多时候,都用指针,最后的gc回收扫描可能都比咱们这个传递copy的消耗大)
2、实现接口的值接收者和指针接收者有啥区别
也就是比如定义如下
这里面的值接收者和指针接收者有什么区别,这里咱来写一个测试
通过这个测试用例可以发现,指针接收者实现的接口可以同时支持转移到值接收者接口和指针接收者接口,而用值接收者实现的接口,则无法转移到使用指针接收者实现的接口,为啥子呢?目前网上或者各类资料上都是给的一个很官方很官方,而且很书面话难以理解的说明,大致意思如下:
这是目前网络或者各种资料上都是差不多是这样说的,看似讲了,实际上就说了一个结果,根本就没说出来一个为什么。这样的总结出来,一个初学者的角度来看,是很不好理解的,初学者要么就是死记硬背,要么就是生搬硬套,甚至直到写了好多好多代码了,都还没有搞明白一个为啥子,只是会用了而已,从长远来说这是不利于自身提高的。
有这两个本质点,咱们自己来思考一下,如果你来实现这个编译器的时候,用指针接收的时候,指针接收者,默认就能直接获取支持,而值接收者实现接口的咱们可以直接来一个解指针就变成了值,就能匹配上值接收者实现的接口了,反过来说,如果值接收者,此时要匹配指针接收者,如何匹配呢,取一个地址就变成了指针了,此时数据类型确实是匹配了,但是,地址指向的数据区不对了,因为我们刚刚说了值接收者拷贝了一个新值之后是完全的一个新的对象,这个新对象和原始对象一点关系都没有,咱们取地址,取的也是这个新对象地址,对这个地址进行操作,也是这个新对象的内部数据,和原始数据内部没有任何关系,所以由此就能推断出,这个是为啥子值接收者不能匹配上指针接收者,而指针接收者却可以匹配上值接收者了。
1、在某个作用域内部,所有定义的字符串的数据区相同
这个很好验证,代码如下:
2、字符串相加会产生一个新串
这个也很好验证
3、字符串真的是不可变的吗
实际上从字符串的结构
从这个结构,就能大致的推断出来,字符串设计成这样就不具备直接扩容+来增加新数据,而如果咱们直接使用string[index] = 'a',用这种方式,就不能编译通过,官方也确定说字符串是不可变的。那么真的是不可变的吗?
通过上面的结构,在加上go的slice切片的数据结构
由此可见,咱们可以将字符串通过指针方式强转为一个byte数组指针,然后通过byte切片来修改,试试
编译通过,运行报错
unexpected fault address 0xae2e27
fatal error: fault
这个错误,基本上就是一个内存的保护错误,是写异常,所以说明了,这个肯定做了内存写保护,那么直接修改一下内存区的属性,去掉他的写保护,就能写了
以下代码都是在Win平台,Go1.18,Win上修改内存权限属性,使用VirtualProtect,代码如下
此时运行,就能发现tstr的内容被咱们变了,这种情况实际上在实际开发中不具有实际意义,因为本身在语言层面,已经做了层层限制,咱们这是属于非法强制的操作方式,是流氓行为,那么是否有比较温和一点的操作方式呢?答案是有的,且往下看。
通过上面,我们已经用到了字符串结构,切片结构,要想字符串内容可变,那么咱们自己构造字符串的数据内容区域,且让这个数据区木有内存写保护不就行了,内容区可变,GO原生态的byte数组不就行嘛,所以咱们自己构造一下
此时我们直接修改buffer的内容,就是直接修改了str的数据内容了。而又不会像前面的一样遇到内存写保护
4、字符串转换优化时可能碰到的坑
通过前面讨论的字符串的可变性的方法,咱们可以知道,很多时候,[]byte到字符串的转变,可以直接构造其结构,而共享数据,从而达到减少数据内存copy的方式来进行优化,再使用这些优化的时候,一定需要注意,字符串或者数组的生命周期,是否会存在被改写的情况,从而导致前后不一致的问题。
比如下面这段代码:
大家可以猜想一下,这个最后里面的数据mmp中,"test"的value是多少,"abcd"的value是多少,然后想想为什么,且等端午之后,再来分解