机翻摘抄自 Working with strings in Rust
从C开始
当人们学习Rust 编程语言时,总会出现一个问题:为什么有两种字符串类型?为什么有String
和&str
?
作者先从一个C语言程序入手:
1 2 3 4 5 6 7 8 9 10 11 12 _// in `print.c`_ _int_ _main_(_int_ _argc_, _char_ _*__*__argv_) { _printf_(_"argv = %p\n"_, _argv_); _// new!_ _for_ (_int_ _i_ _=_ _0_; _i_ _<_ _argc_; _i__++_) { _char_ _*__arg_ _=_ _argv_[_i_]; _printf_(_"argv[%d] = %p\n"_, _i_, _argv_[_i_]); _// new!_ _printf_(_"%s\n"_, _arg_); } _return_ _0_; }
看看结果,看起来不错!
1 2 3 4 5 6 $ gcc print.c -o print $ ./print "ready" "set" "go" ./print ready set go
现在看看我们到底在做什么
1 2 3 4 5 6 7 8 9 10 11 12 _// in `print.c`_ _int_ _main_(_int_ _argc_, _char_ _*__*__argv_) { _printf_(_"argv = %p\n"_, _argv_); _// new!_ _for_ (_int_ _i_ _=_ _0_; _i_ _<_ _argc_; _i__++_) { _char_ _*__arg_ _=_ _argv_[_i_]; _printf_(_"argv[%d] = %p\n"_, _i_, _argv_[_i_]); _// new!_ _printf_(_"%s\n"_, _arg_); } _return_ _0_; }
Now we’re using the %p
format specifier, which prints.. pointers! 现在我们使用%p
格式说明符,它打印..指针!
1 2 3 4 5 6 7 8 9 10 11 $ gcc print.c -o print $ ./print "ready" "set" "go" argv = 0x7ffcc35d84a8 argv[0] = 0x7ffcc35d9039 ./print argv[1] = 0x7ffcc35d9041 ready argv[2] = 0x7ffcc35d9047 set argv[3] = 0x7ffcc35d904b go
于是我们知道,argv是一个地址数组 那么%s
如何知道自己何时停止打印? 作者的代码copy过来不太方便,于是直接上图
让我们尝试将我们的程序通过管道传输到像xxd
这样的十六进制转储器中,以准确查看发生了什么:
1 2 3 4 5 6 7 $ # note: "-g 1" means "show groups of one byte", $ # xxd defaults to "-g 2". $ ./print "ready" "set" "go" | xxd -g 1 00000000: 2e 2f 70 72 69 6e 74 00 72 65 61 64 79 00 73 0a ./print.ready.s. 00000010: 72 65 61 64 79 00 73 65 74 00 67 6f 00 43 44 0a ready.set.go.CD. 00000020: 73 65 74 00 67 6f 00 43 44 50 41 54 48 3d 2e 0a set.go.CDPATH=.. 00000030: 67 6f 00 43 44 50 41 54 48 3d 2e 3a 2f 68 6f 0a go.CDPATH=.:/ho.
1 2 00000000: 2e 2f 70 72 69 6e 74 00 72 65 61 64 79 00 73 0a ./print.ready.s. . / p r i n t ^^ r e a d y ^^
我们发现他用00分割了每个参数,事实上,C具有以null(0)结尾的字符串。 所以%s
等价于一个在0上break的%c
打印程序。
==You may have noticed that when our print program went beyond the end of our arguments, it showed CDPATH=.:/ho
too. That was (part of) an environment variable! Those are stored right next to the program’s arguments in glibc, the GNU C library. But the specifics are out of scope for this article, you may want to check out the Making our own executable packer series instead. 您可能已经注意到,当我们的打印程序_超出_参数末尾时,它也显示CDPATH=.:/ho
。 那是环境变量(的一部分)!它们存储在 GNU C 库 glibc 中程序参数的旁边。 但具体细节超出了本文的范围,您可能想查看“制作我们自己的可执行加壳器” 系列 ==
现在进行最后一个测试
1 2 3 $ gcc print.c -o print $ ./print "élément" éLéMENT
好像出现了一点问题,实际我们想要的是“ÉLÉMENT”,好吧,让我们试试更简单的事情——打印每个字符%c
并用空格隔开。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 _// in `print.c`_ _#include_ _<stdio.h>_ _// printf_ _int_ _main_(_int_ _argc_, _char_ _*__*__argv_) { _for_ (_int_ _i_ _=_ _1_; _i_ _<_ _argc_; _i__++_) { _char_ _*__arg_ _=_ _argv_[_i_]; _for_ (_int_ _j_ _=_ _0_;; _j__++_) { _char_ _character_ _=_ _arg_[_j_]; _if_ (_character_ _==_ _0_) { _break_; } _// notice the space following `%c`_ _printf_(_"%c "_, _character_); } _printf_(_"\n"_); } _return_ _0_; }
1 2 3 $ gcc print.c -o print $ ./print "élément" l m e n t
不太行……还是再看看xxd好吗?
gcc print.c -o print 1 2 3 $ ./print "élément" | xxd -g 1 00000000: c3 a9 6c c3 a9 6d 65 6e 74 0a ..l..ment. ^^^^^ ^^^^^
问题就在于,É实际上是两个bytec3 a9
,即使我们使用nodejs书写一样如此,为什么他的编码是c3 a9?是时候学习一下utf-8了。
快速UTF-8入门