JNI字符串和C字符串

JNI strings and C strings

本文关键字:字符串 JNI      更新时间:2023-10-16

在JNI函数的文档中FindClass我可以阅读关于参数名称的信息:

name:完全限定的类名(…)字符串以修改后的UTF-8编码。

根据修改后的文档,UTF-8必须以双"\0"字符结尾:

空字符(char)0使用两字节格式而不是一字节格式进行编码

这是否意味着我应该以这种方式从C调用FindClass:FindClass("java/lang/String")

即结尾有双"\0"?

字符集、编码和终止是三件不同的事情。显然,编码是为特定的字符集设计的,但字符集可以用多种方式编码。通常,终止符(如果使用的话)是一个编码字符,但对于修改后的UTF-8,情况并非如此。

Java使用Unicode字符集。对于字符串和字符类型,它使用UTF-16编码。对字符串类型进行计数;它不使用终止符。

在C语言中,终止字符串是常见的,各种字符集的单字节编码也是常见的。C和C++编译器使用NUL字符终止文字字符串。在编译器的目标字符集编码中,这是一个或两个0x00字节。对于非控制ASCII字符,几乎所有常见的字符集及其编码都有相同的字节表示。Unicode字符集的UTF-8编码也是如此。(但是,请注意,对于有限子集之外的字符,情况并非如此。)

JNI设计者选择在C字符串之间使用这种有限的"互操作性"。许多JNI函数接受0x00终止的修改的UTF-8字符串。这些与C编译器从源代码中的文字字符串生成的内容兼容,同样前提是字符限制为非控制ASCII字符。这涵盖了编写Java包&JNI中的类、方法和字段字符串。(好吧,差不多:Java允许在标识符中使用任何Unicode货币符号。)

因此,您可以以所见即所得的样式将C字符串文本传递给JNI函数。不需要添加终止符——编译器会这样做C编译器会将额外的"\0"字符编码为0x00,所以它不会造成任何危害,但不是必需的。

对标准UTF-8编码进行了一些修改。一种是允许C函数使用0x00终止符来"处理"修改后的UTF-8字符串,对NUL字符(U+000000)进行编码以避免0x00,这将是标准。这允许将修改后的UTF-8字符串放入缓冲区,在原始编码字符串的字节之外使用0x00终止符。另一个修改有点深奥,但这两个修改都使修改后的UTF-8字符串与严格兼容的UTF-8函数不兼容。

您没有问,但在JNI中还有另一种使用0x00终止的、经过修改的UTF-8字符串的方法。它具有GetStringUTFCharsNewStringUTF功能。(JNI文档实际上并没有说GetStringUTFChars返回0x00终止的字符串,但没有已知的JVM实现没有。请查看JVM实现者的文档或源代码。)这些函数是基于相同的"互操作性"设计的。但是,用例不同,这使得它们很危险它们通常用于在C函数之间传递Java字符串。一般来说,C函数不知道什么是经过修改的UTF-8,甚至可能不知道UTF-8或Unicode是什么。使用Java StringCharset类来转换C函数所设计的字符集和编码更为直接。通常,是系统设置、用户设置、应用程序设置或线程设置决定了C函数正在使用哪个。Java String类在没有为转换指定特定编码时,会尝试遵守这些设置。但是,在许多情况下,所需的编码是固定的,并且可以明确指定。

不,您没有编码终止零,它不是类名的一部分。

不,根据我发现的第一个引用,这意味着它应该像这样编码:

FindChar("java/lang/Stringxc0x80");
                              ^
                              |
                              |
                     This is not the shortest
                     way to encode the codepoint
                     U+0000, which is why it's
                     "modified" UTF-8.

请注意,这假设您确实在寻找名称以U+0000结尾的类名,这是不太可能的。C字符串应该像正常情况一样终止,只使用一个0字节,就像您从中得到的那样

FindChar("java/lang/String");

Modified UTF-8提供的U+0000的特殊2字节编码只在您想将U+0000放入字符串中,并且仍然能够将其与C终止符区分开来时才重要。