【问题标题】:What is the difference between these two versions of code (pointer arithmetics & unicode)?这两个版本的代码(指针算法和 unicode)有什么区别?
【发布时间】:2009-02-16 03:55:53
【问题描述】:

我正在使用 GCC 在 64 位 Solaris 系统上调试一些开源代码,它将 2 字节字符 (wchar_t) 转换为 4 字节字符 (wchar_t)。因为 Solaris 和其他一些 Unix 一样将 wchar_t 定义为 4 字节,而不是 Windows 中的 2 字节。

现在我解决了这个问题,通过将指针算法放在两行上,但我不确定原始代码有什么问题。有什么线索吗?

原始代码

int StringCopy2to4bytes(const unsigned short* src, int src_size, 
                         unsigned int* dst, int dst_size)
{
 int cp_size = 0;

 const unsigned short *src_end = NULL;
 const unsigned int   *dst_end = NULL;

 unsigned int c1, c2;

 src_end = src + src_size;
 dst_end = dst + dst_size;

 while (src < src_end)
 {
     c1 = *src++;
     if ((c1 >= UNI_SUR_HIGH_START) && (c1 <= UNI_SUR_HIGH_END))
     {
         if (src < src_end)
         {
             c2 = *src;
             if ((c2 >= UNI_SUR_LOW_START) && (c2 <= UNI_SUR_LOW_END))
             {
                c1 = ((c1 - UNI_SUR_HIGH_START) << UNI_SHIFT) + 
                      (c1 - UNI_SUR_LOW_START )  + UNI_BASE;

                ++src;
             }
         } 
         else 
             return -1;
     } 

     if (dst >= dst_end) return -2;

     *dst++ = c1;

     cp_size++;
 }

 return cp_size;
}

固定代码

int StringCopy2to4bytes(const unsigned short* src, int src_size, 
                         unsigned int* dst, int dst_size)
{
 int cp_size = 0;

 const unsigned short *src_end = NULL;
 const unsigned int   *dst_end = NULL;

 unsigned int c1, c2;

 src_end = src + src_size;
 dst_end = dst + dst_size;

 while (src < src_end)
 {
     c1 = *src; //FIX
     ++src;

     if ((c1 >= UNI_SUR_HIGH_START) && (c1 <= UNI_SUR_HIGH_END))
     {
         if (src < src_end)
         {
             c2 = *src;
             if ((c2 >= UNI_SUR_LOW_START) && (c2 <= UNI_SUR_LOW_END))
             {
                c1 = ((c1 - UNI_SUR_HIGH_START) << UNI_SHIFT) + 
                      (c1 - UNI_SUR_LOW_START )  + UNI_BASE;

                ++src;
             }
         } 
         else 
             return -1;
     } 

     if (dst >= dst_end) return -2;

     *dst = c1; //FIX
     ++dst;

     cp_size++;
 }

 return cp_size;
}

编辑:记录一下,代码不是我的,我只是在用,正好在调试,不是说有什么大的区别,但是出处是相当的大,所以我试图用镊子修复它,而不是重构所有东西,反正错误就是错误,我需要修复它并向作者发送错误消息。

常量是:

/* unicode constants */
#define UNI_SHIFT             ((int) 10 )
#define UNI_BASE              ((unsigned int) 0x0010000UL)
#define UNI_MASK              ((unsigned int) 0x3FFUL)
#define UNI_REPLACEMENT_CHAR  ((unsigned int) 0x0000FFFD)
#define UNI_MAX_BMP           ((unsigned int) 0x0000FFFF)
#define UNI_MAX_UTF16         ((unsigned int) 0x0010FFFF)
#define UNI_MAX_UTF32         ((unsigned int) 0x7FFFFFFF)
#define UNI_MAX_LEGAL_UTF32   ((unsigned int) 0x0010FFFF)
#define UNI_SUR_HIGH_START    ((unsigned int) 0xD800)
#define UNI_SUR_HIGH_END      ((unsigned int) 0xDBFF)
#define UNI_SUR_LOW_START     ((unsigned int) 0xDC00)
#define UNI_SUR_LOW_END       ((unsigned int) 0xDFFF)

【问题讨论】:

  • 这个问题有点令人困惑——你不能同时让 wchar_t 为 2 个字节和 4 个字节。另外,您使用的是哪个版本的 GCC?
  • 操作系统使用 wchar_t(4) 但需要通过网络将字符数据作为 wchar_t(2) 传输到其他系统,因此在两者之间进行转换
  • 使用 4.2.4 为两个列表生成的代码完全相同。
  • 常量的值是多少?我认为它们可能是:枚举 { UNI_SUR_HIGH_START = 0xD800, UNI_SUR_HIGH_END = 0xDBFF UNI_SUR_LOW_START = 0xDC00, UNI_SUR_LOW_END = 0xDFFF, UNI_SHIFT = 11, UNI_BASE = 0x10000 };我对 UNI_BASE 最没有信心。
  • 嗯,它们是#defines,这并不酷,但你的值是正确的

标签: c unicode pointers


【解决方案1】:

这里写的代码仍然有问题 - 当你结合 c1 和 c2 时,你需要使用 c2!也就是说,在以下几行:

c1 = ((c1 - UNI_SUR_HIGH_START) << UNI_SHIFT) +
      (c1 - UNI_SUR_LOW_START ) + UNI_BASE;

第三次出现的 c1 实际上应该是 c2。

此外,将 src_end 指针初始化为 null 然后再初始化为 src + src_size 似乎很愚蠢。为什么不马上去呢?

另外,如果字符串的开头被保留,cp_size 可能是多余的;然后它将与 (dst - initial_dst) 相同。


测试代码 - 修复了 c1 到 c2 - 使用第一个代码示例,在 Solaris 10 上使用 GCC 4.3.3。显示了 32 位和 64 位编译的结果。 Unicode 标准第 3 章中的表 3.4 中的数据(从技术上讲,是 Unicode 5.0 而不是 5.1.0,但我认为这并不重要)。

enum { NULL = 0 };
enum { UNI_SUR_HIGH_START = 0xD800, UNI_SUR_HIGH_END = 0xDBFF,
       UNI_SUR_LOW_START  = 0xDC00, UNI_SUR_LOW_END  = 0xDFFF,
       UNI_SHIFT = 10, UNI_BASE = 0x10000 };

int StringCopy2to4bytes(const unsigned short* src, int src_size, 
                         unsigned int* dst, int dst_size)
{
 int cp_size = 0;

 const unsigned short *src_end = NULL;
 const unsigned int   *dst_end = NULL;

 unsigned int c1, c2;

 src_end = src + src_size;
 dst_end = dst + dst_size;

 while (src < src_end)
 {
     c1 = *src++;
     if ((c1 >= UNI_SUR_HIGH_START) && (c1 <= UNI_SUR_HIGH_END))
     {
         if (src < src_end)
         {
             c2 = *src;
             if ((c2 >= UNI_SUR_LOW_START) && (c2 <= UNI_SUR_LOW_END))
             {
                c1 = ((c1 - UNI_SUR_HIGH_START) << UNI_SHIFT) + 
                      (c2 - UNI_SUR_LOW_START )  + UNI_BASE;    /* Fixed */

                ++src;
             }
         } 
         else 
             return -1;
     } 

     if (dst >= dst_end) return -2;

     *dst++ = c1;

     cp_size++;
 }

 return cp_size;
}

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    unsigned short w2_chars[] = { 0x004D, 0x0430, 0x4E8C, 0xD800, 0xDF02, 0x004D };
    unsigned int   w4_wanted[] = { 0x00004D, 0x000430, 0x004E8C, 0x010302, 0x00004D };
    unsigned int   w4_actual[5];
    int w2_len = 6;
    int w4_len = 5;
    int w4_actlen;
    int i;
    int failed = 0;

    w4_actlen = StringCopy2to4bytes(w2_chars, w2_len, w4_actual, w4_len);
    if (w4_actlen != w4_len)
    {
        failed = 1;
        printf("Length mismatch: got %d, wanted %d\n", w4_actlen, w4_len);
    }
    for (i = 0; i < w4_len; i++)
    {
        if (w4_actual[i] != w4_wanted[i])
        {
            printf("Mismatch: index %d: wanted 0x%06X, actual 0x%06X\n",
                   i, w4_wanted[i], w4_actual[i]);
            failed = 1;
        }
    }
    if (failed == 0)
        printf("No problem observed\n");
    return((failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE);
}


$ gcc -m32 -O utf.c -o utf32 && ./utf32
No problem observed
$ gcc -m64 -O utf.c -o utf64 && ./utf64
No problem observed
$

我想知道您的编译器或您的测试用例出了什么问题。


这是 StringCopy2to4bytes() 函数的修订版。它检测并报告原始没有的错误条件 - 即当代理对的第二个单词不是有效的低代理代码点时,它返回状态 -3。

int StringCopy2to4bytes(const unsigned short *src, int src_size, 
                        unsigned int *dst, int dst_size)
{
    const unsigned short *src_end = src + src_size;
    const unsigned int   *dst_end = dst + dst_size;
    const unsigned int   *dst0    = dst;

    while (src < src_end)
    {
        unsigned int c1 = *src++;
        if ((c1 >= UNI_SUR_HIGH_START) && (c1 <= UNI_SUR_HIGH_END))
        {
            if (src >= src_end)
                return -1;
            unsigned int c2 = *src++;
            if ((c2 >= UNI_SUR_LOW_START) && (c2 <= UNI_SUR_LOW_END))
            {
               c1 = ((c1 - UNI_SUR_HIGH_START) << UNI_SHIFT) + 
                     (c2 - UNI_SUR_LOW_START )  + UNI_BASE;    /* Fixed */
            }
            else
                return -3;  /* Invalid second code point in surrogate pair */
        } 
        if (dst >= dst_end)
            return -2; 
        *dst++ = c1;
    }
    return dst - dst0;
}

相同的测试代码会产生相同的干净健康单。 c2 的声明假定您使用的是 C99,而不是 C89。

【讨论】:

  • 我认为他是在谈论示例 1 中的第 24/25 行。我想知道指针算术是否是一个红鲱鱼,问题实际上是这个。
  • 将常量的值添加到问题中
  • 感谢 Unicode 修复!无论如何,问题是罕见的缓冲区溢出而不是指针算术:/
  • 必须在问题中添加 unicode 所以这可能是一个答案:)
  • @Robert - 只是好奇这个例程中的缓冲区溢出错误还是传入的大小不正确(例如,以字节为单位的大小而不是以字符为单位的大小)?
【解决方案2】:

这闻起来好像我们需要一些 ()。

看看区别

  1. c1 = *src++;
  2. c1 = (*src)++;
  3. c1 = *(src++);

我真的很喜欢 (),因为它们消除了程序员想要做什么的一些歧义。

/约翰

【讨论】:

    【解决方案3】:

    应该没有区别,++绑定高于*。

    快速 test.c 也没有显示出任何差异:

    #include <stdio.h>
    
    int main()
    {
        int a[] = { 0, 1, 2, 3, 4, 5 };
        int * p = a;
        printf( "%d\n", *p );
        printf( "%d\n", *p++ );
        printf( "%d\n", *p );
        printf( "%d\n", *(p++) );
        printf( "%d\n", *p );
        return 0;
    }
    

    给予:

    0
    0
    1
    1
    2
    

    是什么让您认为新代码修复了问题?

    编辑:在微不足道的事情中发现编译器错误是极不可能的。以上测试使用 GCC 4.1.2 运行。

    编辑 2: 你有一些类型不匹配。 c1 是 unsigned int,*src 是 unsigned short。大小应该是 size_t,而不是 int。如果您修复这些问题,原始代码的问题是否仍然存在?

    【讨论】:

    • 我同意 Sizes 应该是 size_t 但整个代码库都使用 int 作为尺寸,并针对 size > -1 做一些检查,所以我不能轻易更改它。
    • 呃...是的,你可以吗?只需在需要尺寸的地方使用 size_t。任何 int 都会自动转换为 size_t 。是的,这仍然是类型不匹配,但它在“你的代码”和“旧代码”之间的接口处……没有检查算法,乔纳森对此有话要说……
    猜你喜欢
    • 2019-07-15
    • 2016-10-11
    • 2020-10-10
    • 2016-08-01
    • 1970-01-01
    • 2012-01-01
    • 1970-01-01
    相关资源
    最近更新 更多