在glib中有许多操作unicode的函数,如g_utf8_to_utf16等,我们知道,utf16有大端(BE)和小端(LE)的区别,那么在glib中的utf16到底是大端还是小端呢,我们可以通过分析g_utf8_to_utf16函数的原码得出答案。
在g_utf8_to_utf16函数中,通过如下代码先把把utf8转换成unicode
gunichar wc = g_utf8_get_char (in);
然后直接通过如下代码把unicode值转换成utf16编码:
if (wc < 0x10000)
{
result[i++] = wc;
}
else
{
result[i++] = (wc - 0x10000) / 0x400 + 0xd800;
result[i++] = (wc - 0x10000) % 0x400 + 0xdc00;
}
我们可以看到,当unicode值为BOM时,直接把unicode的值赋值给了utf16编码,那么g_utf8_get_char返回的unicode值的字节序就是glib中utf16的字节序,我们接下来再分析一个上述unicode值的字节序。
在g_utf8_get_char中,glib通过宏UTF8_GET把utf8编码转换成unicode值,
gunichar
g_utf8_get_char (const gchar *p)
{
int i, mask = 0, len;
gunichar result;
unsigned char c = (unsigned char) *p;
UTF8_COMPUTE (c, mask, len);
if (len == -1)
return (gunichar)-1;
UTF8_GET (result, p, i, mask, len);
return result;
}
我们来看一下这个宏的定义:
#define UTF8_GET(Result, Chars, Count, Mask, Len) \
(Result) = (Chars)[0] & (Mask); \
for ((Count) = 1; (Count) < (Len); ++(Count)) \
{ \
if (((Chars)[(Count)] & 0xc0) != 0x80) \
{ \
(Result) = -1; \
break; \
} \
(Result) <<= 6; \
(Result) |= ((Chars)[(Count)] & 0x3f); \
}
可以看到,unicode值就是通过把utf8编码字节逐个右移得到,我们可以思考一下,这样右移后,字节序由什么来决定?
举个例子:
我们有个数为:0x12345678,先保存到一个数组中,
unsigned char num[4];
num[0] = 0x12;
num[1] = 0x34;
num[2] = 0x56;
num[3] = 0x78;
如果我们执行这样的操作,那么i的值是多少呢?
unsigned int i =*(unsigned int*)num;
如果执行这样的操作,那么i的值是多少呢?
unsigned int i;
i = (num[0] << 24) | (num[1] << 16) | (num[2] << 8 ) | (num[3]);
可以通过单步调试看一个i在内存中每个字节的值,再对比大端小端的概念思考一下。
这里的答案是,在小端的window下,后一种操作的值为0x12345678,是一个正常的值,强调的是这里i的字节序是小端。
结论:
glib中utf16的字节序是由编译器决定的,而编译器一般是根据目标操作系统的字节序来设置,所以glib中utf16的字节序通常情况下是与操作系统当前工作的字节序相同的。