Golang58个坑初级篇135
左大括号{不能单独放一行未使用的变量未使用的import简短声明的变量只能在函数内部使用使用简短声明来重复声明变量不能使用简短声明来设置字段的值不小心覆盖了变量显式类型的变量无法使用nil来初始化直接使用值为nil的slice、mapmap容量string类型的变量值不能为nilArray类型的值作为函数参数range遍历slice和array时混淆了返回值slice和array其实是一维数据访问map中不存在的keystring类型的值是常量,不可更改string与byteslice之间的转换string与索引操作符字符串并不都是UTF8文本字符串的长度在多行array、slice、map语句中缺少,号log。Fatal和log。Panic不只是log对内建数据结构的操作并不是同步的range迭代string得到的值range迭代mapswitch中的fallthrough语句自增和自减运算按位取反运算符的优先级不导出的struct字段无法被encode程序退出时还有goroutine在执行向无缓冲的channel发送数据,只要receiver准备好了就会立刻返回向已关闭的channel发送数据会造成panic使用了值为nil的channel若函数receiver传参是传值方式,则无法修改参数的原有值1。左大括号{不能单独放一行
在其他大多数语言中,{的位置你自行决定。Go比较特别,遵守分号注入规则(automaticsemicoloninjection):编译器会在每行代码尾部特定分隔符后加;来分隔多条语句,比如会在)后加分号:错误示例funcmain(){println(www。topgoer。com是个不错的go语言中文文档)}等效于funcmain();无函数体{println(helloworld)}。main。go:missingfunctionbody。main。go:syntaxerror:unexpectedsemicolonornewlinebefore{正确示例funcmain(){println(www。topgoer。com是个不错的go语言中文文档)}2。未使用的变量
如果在函数体代码中有未使用的变量,则无法通过编译,不过全局变量声明但不使用是可以的。即使变量声明后为变量赋值,依旧无法通过编译,需在某处使用它:错误示例vargvarint全局变量,声明不使用也可以funcmain(){varoneinterror:onedeclaredandnotusedtwo:2error:twodeclaredandnotusedvarthreeinterror:threedeclaredandnotusedthree3}正确示例可以直接注释或移除未使用的变量funcmain(){varoneintonetwo:2println(two)varthreeintonethreevarfourintfourfour}3。未使用的import
如果你import一个包,但包中的变量、函数、接口和结构体一个都没有用到的话,将编译失败。可以使用下划线符号作为别名来忽略导入的包,从而避免编译错误,这只会执行package的init()错误示例import(fmtimportedandnotused:fmtlogimportedandnotused:logtimeimportedandnotused:time)funcmain(){}正确示例可以使用goimports工具来注释或移除未使用到的包import(fmtlogtime)funcmain(){log。Printlntime。Now}4。简短声明的变量只能在函数内部使用错误示例myvar:1funcmain(){}正确示例varmyvar1funcmain(){}5。使用简短声明来重复声明变量
不能用简短声明方式来单独为一个变量重复声明,:左侧至少有一个新变量,才允许多变量的重复声明:错误示例funcmain(){one:0one:1error:nonewvariablesonleftsideof:}正确示例funcmain(){one:0one,two:1,2two是新变量,允许one的重复声明。比如error处理经常用同名变量errone,twotwo,one交换两个变量值的简写}6。不能使用简短声明来设置字段的值
struct的变量字段不能使用:来赋值以使用预定义的变量来避免解决:错误示例typeinfostruct{resultint}funcwork()(int,error){return3,nil}funcmain(){vardatainfodata。result,err:work()error:nonnamedata。resultonleftsideof:fmt。Printf(info:v,data)}正确示例funcmain(){vardatainfovarerrerrorerr需要预声明data。result,errwork()iferr!nil{fmt。Println(err)return}fmt。Printf(info:v,data)}7。不小心覆盖了变量
对从动态语言转过来的开发者来说,简短声明很好用,这可能会让人误会:是一个赋值操作符。如果你在新的代码块中像下边这样误用了:,编译不会报错,但是变量不会按你的预期工作:funcmain(){x:1println(x)1{println(x)1x:2println(x)2新的x变量的作用域只在代码块内部}println(x)1}
这是Go开发者常犯的错,而且不易被发现。可使用vet工具来诊断这种变量覆盖,Go默认不做覆盖检查,添加shadow选项来启用:gotoolvetshadowmain。gomain。go:9:declarationofxshadowsdeclarationatmain。go:5
注意vet不会报告全部被覆盖的变量,可以使用gonyet来做进一步的检测:GOPATHbingonyetmain。gomain。go:10:3:Shadowingvariablex8。显式类型的变量无法使用nil来初始化
nil是interface、function、pointer、map、slice和channel类型变量的默认初始值。但声明时不指定类型,编译器也无法推断出变量的具体类型。
参考:go语言nil:空值零值错误示例funcmain(){varxnilerror:useofuntypednilx}正确示例funcmain(){varxinterface{}nilx}9。直接使用值为nil的slice、map
允许对值为nil的slice添加元素,但对值为nil的map添加元素则会造成运行时panicmap错误示例funcmain(){varmmap〔string〕intm〔one〕1error:panic:assignmenttoentryinnilmapm:make(map〔string〕int)map的正确声明,分配了实际的内存}slice正确示例funcmain(){vars〔〕intsappend(s,1)}10。map容量
在创建map类型的变量时可以指定容量,但不能像slice一样使用cap()来检测分配空间的大小:错误示例funcmain(){m:make(map〔string〕int,99)println(cap(m))error:invalidargumentm1(typemap〔string〕int)forcap}11。string类型的变量值不能为nil
对那些喜欢用nil初始化字符串的人来说,这就是坑:错误示例funcmain(){varsstringnilcannotusenilastypestringinassignmentifsnil{invalidoperation:snil(mismatchedtypesstringandnil)sdefault}}正确示例funcmain(){varsstring字符串类型的零值是空串ifs{sdefault}}12。Array类型的值作为函数参数
在CC中,数组(名)是指针。将数组作为参数传进函数时,相当于传递了数组内存地址的引用,在函数内部会改变该数组的值。
在Go中,数组是值。作为参数传进函数时,传递的是数组的原始值拷贝,此时在函数内部是无法更新该数组的:数组使用值拷贝传参funcmain(){x:〔3〕int{1,2,3}func(arr〔3〕int){arr〔0〕7fmt。Println(arr)〔723〕}(x)fmt。Println(x)〔123〕并不是你以为的〔723〕}
如果想修改参数数组:直接传递指向这个数组的指针类型:传址会修改原数据funcmain(){x:〔3〕int{1,2,3}func(arr〔3〕int){(arr)〔0〕7fmt。Println(arr)〔723〕}(x)fmt。Println(x)〔723〕}直接使用slice:即使函数内部得到的是slice的值拷贝,但依旧会更新slice的原始数据(底层array)会修改slice的底层array,从而修改slicefuncmain(){x:〔〕int{1,2,3}func(arr〔〕int){arr〔0〕7fmt。Println(x)〔723〕}(x)fmt。Println(x)〔723〕}13。range遍历slice和array时混淆了返回值
与其他编程语言中的forin、foreach遍历语句不同,Go中的range在遍历时会生成2个值,第一个是元素索引,第二个是元素的值:错误示例funcmain(){x:〔〕string{a,b,c}forv:rangex{fmt。Println(v)123}}正确示例funcmain(){x:〔〕string{a,b,c}for,v:rangex{使用丢弃索引fmt。Println(v)}}14。slice和array其实是一维数据
看起来Go支持多维的array和slice,可以创建数组的数组、切片的切片,但其实并不是。
对依赖动态计算多维数组值的应用来说,就性能和复杂度而言,用Go实现的效果并不理想。
可以使用原始的一维数组、独立的切片、共享底层数组的切片来创建动态的多维数组。
1。使用原始的一维数组:要做好索引检查、溢出检测、以及当数组满时再添加值时要重新做内存分配。
2。使用独立的切片分两步:创建外部slice对每个内部slice进行内存分配注意内部的slice相互独立,使得任一内部slice增缩都不会影响到其他的slice使用各自独立的6个slice来创建〔2〕〔3〕的动态多维数组funcmain(){x:2y:4table:make(〔〕〔〕int,x)fori:rangetable{table〔i〕make(〔〕int,y)}}
1。使用共享底层数组的切片创建一个存放原始数据的容器slice创建其他的slice切割原始slice来初始化其他的slicefuncmain(){h,w:2,4raw:make(〔〕int,hw)fori:rangeraw{raw〔i〕i}初始化原始slicefmt。Println(raw,raw〔4〕)〔01234567〕0xc420012120table:make(〔〕〔〕int,h)fori:rangetable{等间距切割原始slice,创建动态多维数组table0:raw〔04:044〕1:raw〔14:144〕table〔i〕raw〔iw:iww〕}fmt。Println(table,table〔1〕〔0〕)〔〔0123〕〔4567〕〕0xc420012120}15。访问map中不存在的key
和其他编程语言类似,如果访问了map中不存在的key则希望能返回nil,比如在PHP中:phprv〔x1,y2〕;vardump(v〔z〕);NULL
Go则会返回元素对应数据类型的零值,比如nil、’’、false和0,取值操作总有值返回,故不能通过取出来的值来判断key是不是在map中。
检查key是否存在可以用map直接访问,检查返回的第二个参数即可:错误的key检测方式funcmain(){x:map〔string〕string{one:2,two:,three:3}ifv:x〔two〕;v{fmt。Println(keytwoisnoentry)键two存不存在都会返回的空字符串}}正确示例funcmain(){x:map〔string〕string{one:2,two:,three:3}if,ok:x〔two〕;!ok{fmt。Println(keytwoisnoentry)}}16。string类型的值是常量,不可更改
尝试使用索引遍历字符串,来更新字符串中的个别字符,是不允许的。
string类型的值是只读的二进制byteslice,如果真要修改字符串中的字符,将string转为〔〕byte修改后,再转为string即可:修改字符串的错误示例funcmain(){x:textx〔0〕Terror:cannotassigntox〔0〕fmt。Println(x)}修改示例funcmain(){x:textxBytes:〔〕byte(x)xBytes〔0〕T注意此时的T是rune类型xstring(xBytes)fmt。Println(x)Text}
注意:上边的示例并不是更新字符串的正确姿势,因为一个UTF8编码的字符可能会占多个字节,比如汉字就需要34个字节来存储,此时更新其中的一个字节是错误的。
更新字串的正确姿势:将string转为runeslice(此时1个rune可能占多个byte),直接更新rune中的字符funcmain(){x:textxRunes:〔〕rune(x)xRunes〔0〕我xstring(xRunes)fmt。Println(x)我ext}17。string与byteslice之间的转换
当进行string和byteslice相互转换时,参与转换的是拷贝的原始值。这种转换的过程,与其他编程语的强制类型转换操作不同,也和新slice与旧slice共享底层数组不同。
Go在string与byteslice相互转换上优化了两点,避免了额外的内存分配:在map〔string〕中查找key时,使用了对应的〔〕byte,避免做m〔string(key)〕的内存分配使用forrange迭代string转换为〔〕byte的迭代:fori,v:range〔〕byte(str){}18。string与索引操作符
对字符串用索引访问返回的不是字符,而是一个byte值。
这种处理方式和其他语言一样,比如PHP中:phprname中文;vardump(name);中文占用6个字节string(6)中文phprname中文;vardump(name〔0〕);把第一个字节当做Unicode字符读取,显示UFFFDstring(1)phprname中文;vardump(name〔0〕。name〔1〕。name〔2〕);string(3)中funcmain(){x:asciifmt。Println(x〔0〕)97fmt。Printf(T,x〔0〕)uint8}
如果需要使用forrange迭代访问字符串中的字符(unicodecodepointrune),标准库中有unicodeutf8包来做UTF8的相关解码编码。另外utf8string也有像func(sString)At(iint)rune等很方便的库函数。19。字符串并不都是UTF8文本
string的值不必是UTF8文本,可以包含任意的值。只有字符串是文字字面值时才是UTF8文本,字串可以通过转义来包含其他数据。
判断字符串是否是UTF8文本,可使用unicodeutf8包中的ValidString()函数:funcmain(){str1:ABCfmt。Println(utf8。ValidString(str1))truestr2:ACfmt。Println(utf8。ValidString(str2))falsestr3:AxfeCfmt。Println(utf8。ValidString(str3))true把转义字符转义成字面值}20。字符串的长度
在Python中:datauprint(len(data))1
然而在Go中:funcmain(){char:fmt。Println(len(char))3}
Go的内建函数len()返回的是字符串的byte数量,而不是像Python中那样是计算Unicode字符数。
如果要得到字符串的字符数,可使用unicodeutf8包中的RuneCountInString(strstring)(nint)funcmain(){char:fmt。Println(utf8。RuneCountInString(char))1}
注意:RuneCountInString并不总是返回我们看到的字符数,因为有的字符会占用2个rune:funcmain(){char:fmt。Println(len(char))3fmt。Println(utf8。RuneCountInString(char))2fmt。Println(cafe)caf法文的cafe,实际上是两个rune的组合}21。在多行array、slice、map语句中缺少,号funcmain(){x:〔〕int{1,2syntaxerror:unexpectednewline,expectingcommaor}}y:〔〕int{1,2,}z:〔〕int{1,2}。。。}
声明语句中}折叠到单行后,尾部的,不是必需的。22。log。Fatal和log。Panic不只是log
log标准库提供了不同的日志记录等级,与其他语言的日志库不同,Go的log包在调用Fatal()、Panic()时能做更多日志外的事,如中断程序的执行等:funcmain(){log。Fatal(Fatallevellog:logentry)输出信息后,程序终止执行log。Println(Nomallevellog:logentry)}23。对内建数据结构的操作并不是同步的
尽管Go本身有大量的特性来支持并发,但并不保证并发的数据安全,用户需自己保证变量等数据以原子操作更新。
goroutine和channel是进行原子操作的好方法,或使用sync包中的锁。24。range迭代string得到的值
range得到的索引是字符值(Unicodepointrune)第一个字节的位置,与其他编程语言不同,这个索引并不直接是字符在字符串中的位置。
注意一个字符可能占多个rune,比如法文单词caf中的。操作特殊字符可使用norm包。
forrange迭代会尝试将string翻译为UTF8文本,对任何无效的码点都直接使用0XFFFDrune()UNicode替代字符来表示。如果string中有任何非UTF8的数据,应将string保存为byteslice再进行操作。funcmain(){data:Afor,v:rangedata{fmt。Printf(x,v)0x410xfffd0x20xfffd0x4错误}for,v:range〔〕byte(data){fmt。Printf(x,v)0x410xfe0x20xff0x4正确}}25。range迭代map
如果你希望以特定的顺序(如按key排序)来迭代map,要注意每次迭代都可能产生不一样的结果。
Go的运行时是有意打乱迭代顺序的,所以你得到的迭代结果可能不一致。但也并不总会打乱,得到连续相同的5个迭代结果也是可能的,如:funcmain(){m:map〔string〕int{one:1,two:2,three:3,four:4}fork,v:rangem{fmt。Println(k,v)}}
如果你去GoPlayground重复运行上边的代码,输出是不会变的,只有你更新代码它才会重新编译。重新编译后迭代顺序是被打乱的:
26。switch中的fallthrough语句
switch语句中的case代码块会默认带上break,但可以使用fallthrough来强制执行下一个case代码块。funcmain(){isSpace:func(charbyte)bool{switchchar{case:空格符会直接break,返回false和其他语言不一样fallthrough返回truecase:returntrue}returnfalse}fmt。Println(isSpace())truefmt。Println(isSpace())false}
不过你可以在case代码块末尾使用fallthrough,强制执行下一个case代码块。
也可以改写case为多条件判断:funcmain(){isSpace:func(charbyte)bool{switchchar{case,:returntrue}returnfalse}fmt。Println(isSpace())truefmt。Println(isSpace())true}27。自增和自减运算
很多编程语言都自带前置后置的、运算。但Go特立独行,去掉了前置操作,同时、只作为运算符而非表达式。错误示例funcmain(){data:〔〕int{1,2,3}i:0isyntaxerror:unexpected,expecting}fmt。Println(data〔i〕)syntaxerror:unexpected,expecting:}正确示例funcmain(){data:〔〕int{1,2,3}i:0ifmt。Println(data〔i〕)2}28。按位取反
很多编程语言使用作为一元按位取反(NOT)操作符,Go重用XOR操作符来按位取反:错误的取反操作funcmain(){fmt。Println(2)bitwisecomplementoperatoris}正确示例funcmain(){varduint82fmt。Printf(08b,d)00000010fmt。Printf(08b,d)11111101}
同时也是按位异或(XOR)操作符。
一个操作符能重用两次,是因为一元的NOT操作NOT0x02,与二元的XOR操作0x22XOR0xff是一致的。
Go也有特殊的操作符ANDNOT操作符,不同位才取1。funcmain(){varauint80x82varbuint80x02fmt。Printf(08b〔A〕,a)fmt。Printf(08b〔B〕,b)fmt。Printf(08b(NOTB),b)fmt。Printf(08b08b08b〔BXOR0xff〕,b,0xff,b0xff)fmt。Printf(08b08b08b〔AXORB〕,a,b,ab)fmt。Printf(08b08b08b〔AANDB〕,a,b,ab)fmt。Printf(08b08b08b〔AANDNOTB〕,a,b,ab)fmt。Printf(08b(08b)08b〔AAND(NOTB)〕,a,b,a(b))}10000010〔A〕00000010〔B〕11111101(NOTB)000000101111111111111101〔BXOR0xff〕100000100000001010000000〔AXORB〕100000100000001000000010〔AANDB〕100000100000001010000000〔AANDNOTB〕10000010(00000010)10000000〔AAND(NOTB)〕29。运算符的优先级
除了位清除(bitclear)操作符,Go也有很多和其他语言一样的位操作符,但优先级另当别论。funcmain(){fmt。Printf(0x20x20x4x,0x20x20x4)优先prints:0x20x20x40x6Go:(0x20x2)0x4C:0x2(0x20x4)0x2fmt。Printf(0x20x20x1x,0x20x20x1)优先prints:0x20x20x10x6Go:0x2(0x20x1)C:(0x20x2)0x10x8fmt。Printf(0xf0x20x2x,0xf0x20x2)优先prints:0xf0x20x20xdGo:(0xf0x2)0x2C:0xf(0x20x2)0xf}
优先级列表:PrecedenceOperator543!2130。不导出的struct字段无法被encode
以小写字母开头的字段成员是无法被外部直接访问的,所以struct在进行json、xml、gob等格式的encode操作时,这些私有字段会被忽略,导出时得到零值:funcmain(){in:MyData{1,two}fmt。Printf(v,in)main。MyData{One:1,two:two}encoded,:json。Marshal(in)fmt。Println(string(encoded)){One:1}私有字段two被忽略了varoutMyDatajson。Unmarshal(encoded,out)fmt。Printf(v,out)main。MyData{One:1,two:}}31。程序退出时还有goroutine在执行
程序默认不等所有goroutine都执行完才退出,这点需要特别注意:主程序会直接退出funcmain(){workerCount:2fori:0;iworkerCount;i{godoIt(i)}time。Sleep(1time。Second)fmt。Println(alldone!)}funcdoIt(workerIDint){fmt。Printf(〔v〕isrunning,workerID)time。Sleep(3time。Second)模拟goroutine正在执行fmt。Printf(〔v〕isdone,workerID)}
如下,main()主程序不等两个goroutine执行完就直接退出了:
常用解决办法:使用WaitGroup变量,它会让主程序等待所有goroutine执行完毕再退出。
如果你的goroutine要做消息的循环处理等耗时操作,可以向它们发送一条kill消息来关闭它们。或直接关闭一个它们都等待接收数据的channel:www。topgoer。comgo语言中文文档等待所有goroutine执行完毕进入死锁funcmain(){varwgsync。WaitGroupdone:make(chanstruct{})workerCount:2fori:0;iworkerCount;i{wg。Add(1)godoIt(i,done,wg)}close(done)wg。Wait()fmt。Println(alldone!)}funcdoIt(workerIDint,donechanstruct{},wgsync。WaitGroup){fmt。Printf(〔v〕isrunning,workerID)deferwg。Done()donefmt。Printf(〔v〕isdone,workerID)}
执行结果:
看起来好像goroutine都执行完了,然而报错:
fatalerror:allgoroutinesareasleepdeadlock!
为什么会发生死锁?goroutine在退出前调用了wg。Done(),程序应该正常退出的。
原因是goroutine得到的WaitGroup变量是varwgWaitGroup的一份拷贝值,即doIt()传参只传值。所以哪怕在每个goroutine中都调用了wg。Done(),主程序中的wg变量并不会受到影响。www。topgoer。comgo语言中文文档等待所有goroutine执行完毕使用传址方式为WaitGroup变量传参使用channel关闭goroutinefuncmain(){varwgsync。WaitGroupdone:make(chanstruct{})ch:make(chaninterface{})workerCount:2fori:0;iworkerCount;i{wg。Add(1)godoIt(i,ch,done,wg)wg传指针,doIt()内部会改变wg的值}fori:0;iworkerCount;i{向ch中发送数据,关闭goroutinechi}close(done)wg。Wait()close(ch)fmt。Println(alldone!)}funcdoIt(workerIDint,chchaninterface{},donechanstruct{},wgsync。WaitGroup){fmt。Printf(〔v〕isrunning,workerID)deferwg。Done()for{select{casem:ch:fmt。Printf(〔v〕mv,workerID,m)casedone:fmt。Printf(〔v〕isdone,workerID)return}}}
运行效果:
32。向无缓冲的channel发送数据,只要receiver准备好了就会立刻返回
只有在数据被receiver处理时,sender才会阻塞。因运行环境而异,在sender发送完数据后,receiver的goroutine可能没有足够的时间处理下一个数据。如:funcmain(){ch:make(chanstring)gofunc(){form:rangech{fmt。Println(Processed:,m)time。Sleep(1time。Second)模拟需要长时间运行的操作}}()chcmd。1chcmd。2不会被接收处理}
运行效果:
33。向已关闭的channel发送数据会造成panic
从已关闭的channel接收数据是安全的:
接收状态值ok是false时表明channel中已没有数据可以接收了。类似的,从有缓冲的channel中接收数据,缓存的数据获取完再没有数据可取时,状态值也是false
向已关闭的channel中发送数据会造成panic:
针对上边有bug的这个例子,可使用一个废弃channeldone来告诉剩余的goroutine无需再向ch发送数据。此时done的结果是{}:funcmain(){ch:make(chanint)done:make(chanstruct{})fori:0;i3;i{gofunc(idxint){select{casech(idx1)2:fmt。Println(idx,Sendresult)casedone:fmt。Println(idx,Exiting)}}(i)}fmt。Println(Result:,ch)close(done)time。Sleep(3time。Second)}
运行效果:
34。使用了值为nil的channel
在一个值为nil的channel上发送和接收数据将永久阻塞:funcmain(){varchchanint未初始化,值为nilfori:0;i3;i{gofunc(iint){chi}(i)}fmt。Println(Result:,ch)time。Sleep(2time。Second)}
runtime死锁错误:
fatalerror:allgoroutinesareasleepdeadlock!
goroutine1〔chanreceive(nilchan)〕
利用这个死锁的特性,可以用在select中动态的打开和关闭case语句块:funcmain(){inCh:make(chanint)outCh:make(chanint)gofunc(){varinchanintinChvaroutchanintvarvalintfor{select{caseoutval:println()outnilininChcasevalin:println()outoutChinnil}}}()gofunc(){forr:rangeoutCh{fmt。Println(Result:,r)}}()time。Sleep(0)inCh1inCh2time。Sleep(3time。Second)}
运行效果:
35。若函数receiver传参是传值方式,则无法修改参数的原有值
方法receiver的参数与一般函数的参数类似:如果声明为值,那方法体得到的是一份参数的值拷贝,此时对参数的任何修改都不会对原有值产生影响。
除非receiver参数是map或slice类型的变量,并且是以指针方式更新map中的字段、slice中的元素的,才会更新原有值:typedatastruct{numintkeystringitemsmap〔string〕bool}func(thisdata)pointerFunc(){this。num7}func(thisdata)valueFunc(){this。num8this。keyvalueFunc。keythis。items〔valueFunc〕true}funcmain(){key:key1d:data{1,key,make(map〔string〕bool)}fmt。Printf(numvkeyvitemsv,d。num,d。key,d。items)d。pointerFunc()修改num的值为7fmt。Printf(numvkeyvitemsv,d。num,d。key,d。items)d。valueFunc()修改key和items的值fmt。Printf(numvkeyvitemsv,d。num,d。key,d。items)}
运行结果:
后羿辉光之辰限定返场?春节限定返场提前出炉,30荣耀积分记得领头条创作挑战赛文丨可儿游戏说原创马上就要到元旦节了,每年的元旦节,王者荣耀都会把一些皮肤拿出来进行返场,今年肯定也不会例外,今年返场的皮肤近期已经有网友进行了爆料,让很多玩家都非常
忍者必须死3空降海外多国榜单前列,益智解谜玩法正热休闲新游周报每周统计微信小游戏QQ小游戏抖音小游戏硬核小游戏以及AppStore和GooglePlay上的新游爆款,并按照分类制作表格,将游戏排名情况等信息公布,希望给到行业同侪帮
虎牙哥哥,再不能躺着赚钱了对于游戏直播平台来说,御寒成为了当下的话题,业内一哥虎牙也不例外。日前,虎牙发布了三季度财报,虎牙实现营收23。79亿元,连续第四个季度下跌。不过值得欣慰的是虎牙终于不亏了,净利润
12月26日外服手游日报胜利女神妮姬公开特别圣诞动画胜利女神妮姬公开MiracleSnow特别动画及圣诞快乐3D动画由SHIFTUP与LevelInfinite联手开发Gamamobi代理的美少女枪战动作手机游戏胜利女神妮姬(iOS
2022年度最令人兴奋的100项科技创新航空航天BestofWhatsNew2022POPSCI百佳科技成果奖自1988年以来,大众科学每年都会隆重介绍那些让我们的生活变得更美好的科技创新。2022年第35个年头詹姆斯韦伯太空望
苏辙的一篇雄文,却掐灭了大宋复兴的希望!文人误国,比武人更甚中国,自古以来在历史上就有着鼎盛的地位,无论是秦始皇的大一统,还是汉武帝的西北望,亦或者是震惊四海的大唐贞观游牧北疆的成吉思汗这片神秘的国土上,存在着无穷的力量。千古悠悠历史长河中
2022亮点回眸生态湿地美如画东营日报社爱东营讯站在人与自然和谐共生的高度谋划发展,报告中的这句话讲到了我们心坎里,也给了我们很大启发。山东黄河三角洲国家级自然保护区创建黄河口国家公园工作专班人员边研读党的二十
外贸稳中向好彰显经济韧性日前,海关总署发布了2022年前11个月外贸数据,我国进出口总值38。34万亿元人民币,同比增长8。6。其中,出口21。84万亿元,同比增长11。9贸易顺差5。34万亿元,扩大42
进入冬季宝宝的血管瘤更要这么护理进入寒冬季节宝宝穿的又多又厚,加上非常好动,非常担心宝宝的血管瘤破溃出血,有什么预防措施可以做呢?如果宝宝的血管瘤增长速度快,容易溃烂,一方面是因为血管瘤内血液丰富,而宝宝皮肤表皮
婴儿几个月可以吃dha呢?从小就给宝宝喂食母乳时,宝宝吃母乳就会觉得营养全面,而且婴儿长身体所需所需的营养也比不上吃母乳。等到宝宝大一些以后,父母会给宝宝提供丰富的营养,这时候可以给予补充。那么婴儿几个月可
朱元璋画像之谜一龙一猪,朱元璋到底长什么模样?黔府历史笔记文朱元璋作为中国历史上的最具争议性的人物之一,史书上关于其人物性格的刻画也是极其复杂的。反映在画像数量上就体现为,在明清宫内收藏的明朝帝王63幅画像中,朱元璋一人就占了