mbrtowc的s == NULL情况的目的是什么?

时间:2011-01-17 02:18:34

标签: c standards multibyte language-lawyer

指定

mbrtowc来处理NULL(多字节字符指针)参数的s指针,如下所示:

  

如果s是空指针,则mbrtowc()函数应等同于调用:

mbrtowc(NULL, "", 1, ps)
     

在这种情况下,参数pwc和n的值将被忽略。

据我所知,这种用法基本没用。如果ps没有存储任何部分转换的字符,则调用将返回0而没有副作用。如果ps正在存储部分转换的字符,那么由于'\0'无效,因为多字节序列中的下一个字节('\0'只能是字符串终止符),该调用将返回(size_t)-1 errno==EILSEQ ps。并使NULL处于未定义状态。

预期的用法似乎是重置状态变量,特别是在为ps传递mbtowc并且已使用内部状态时,类似于mbrtowc的行为有状态编码,但据我所知,未在任何地方指定,并且它与mbrtowc存储部分转换字符的语义冲突(如果mbrtowc当在可能有效的初始子序列之后遇到0字节时重置状态,它将无法检测到这种危险的无效序列。)

如果指定s仅在NULLps时重置状态变量,而在指向0字节时不重置状态变量,则可能出现所需的状态重置行为,但这种行为会违反书面标准。这是标准中的缺陷吗?据我所知,一旦遇到非法序列,绝对没有办法重置内部状态(在NULLmbrtowc时使用),因此没有正确的程序可以使用{{1与ps==NULL

2 个答案:

答案 0 :(得分:5)

由于'\ 0'字节必须转换为空宽字符而不管移位状态(5.2.1.2多字节字符),并且指定mbrtowc()函数以在转换为宽时重置移位状态null字符(7.24.6.3.2 / 3 mbrtowc函数),调用mbrtowc( NULL, "", 1, ps)将重置存储在mbstate_t所指向的ps中的移位状态。如果调用mbrtowc( NULL, "", 1, NULL)来使用库的内部mbstate_t对象,它将被重置为初始状态。有关标准相关位的引用,请参阅答案的结尾。

我对C标准多字节转换函数没有特别的经验(我对这种事情的经验一直使用Win32 API进行转换)。

如果mbrtowc()处理一个被0字节缩短的'不完整字符',它应返回(size_t)(-1)以指示无效的多字节字符(从而检测您描述的危险情况)。在这种情况下,转换/转换状态是未指定的(我认为你基本上是为该字符串而使用)。尝试转换但包含'\0'的多字节“序列”无效,并且对后续数据始终有效。如果'\0'不是转换序列的一部分,那么它不应该包含在可用于处理的字节数中。

如果您处于可能获得部分多字节字符(例如来自网络流)的其他后续字节的情况下,您为部分多字节字符传递的n不应包含0字节,所以你会得到一个(size_t)(-2)返回。在这种情况下,如果您在部分转换过程中传递'\0',则会丢失事实,即存在错误,并且副作用会重置正在使用的mbstate_t状态(无论是它是您自己的或正在使用的内部因为您传入了ps的NULL指针。我想我在这里重申你的问题。

但是我认为可以检测并处理这种情况,但不幸的是,它需要自己跟踪某些状态:

#define MB_ERROR    ((size_t)(-1))
#define MB_PARTIAL  ((size_t)(-2))

// function to get a stream of multibyte characters from somewhere
int get_next(void);

int bar(void)
{
    char c;
    wchar_t wc;
    mbstate_t state = {0};

    int in_partial_convert = 0;

    while ((c = get_next()) != EOF)
    {
        size_t result = mbrtowc( &wc, &c, 1, &state);

        switch (result) {
        case MB_ERROR:
            // this multibyte char is invalid
            return -1;
        case MB_PARTIAL:
            // do nothing yet, we need more data
            // but remember that we're in this state
            in_partial_convert = 1;
            break;
        case 1:
            // output the competed wide char
            in_partial_convert = 0;     // no longer in the middle of a conversion
            putwchar(wc);
            break;
        case 0:
            if (in_partial_convert) {
                // this 'last' multibyte char was mal-formed
                // return an error condidtion
                return -1;
            }
            // end of the multibyte string
            // we'll handle similar to EOF
            return 0;
        }
    }

    return 0;
}

也许不是一个理想的情况,但我认为这表明它并没有被彻底打破,因此无法使用。


标准引用:

5.2.1.2多字节字符

  
      
  • 多字节字符集可以具有依赖于状态的编码,其中   每个字节的多字节字符   从初始换档状态开始   进入其他特定于语言环境的班次   特定多字节时的状态   在中遇到的字符   序列。而在最初的转变   state,所有单字节字符   保留他们通常的解释和   不要改变换档状态。该   对后续字节的解释   序列是一个函数   当前的转变状态。

  •   
  • 所有位为零的字节应解释为空字符   独立于班次状态。

  •   
  • 所有位为零的字节不得出现在第二个或后续的位置   多字节字符的字节。
  •   

7.24.6.3.2 / 3 mbrtowc函数

  

如果相应的宽字符是   null宽字符,由此产生   描述的状态是最初的   转换状态

答案 1 :(得分:1)

在5.2.1.2,多字节字符中,C标准规定:

  

所有位为零的字节应解释为与移位无关的空字符   州。这样的字节不应作为任何其他多字节字符的一部分出现。

标准似乎区分了班次状态和转换状态,例如,7.24.6提及:

  

指向对象描述的转换状态根据需要进行更改,以跟踪关联的多字节字符序列的移位状态和多字节字符中的位置。

(我强调)。但是,我认为目的是将所有零位的字节解释为空字符,而不管mbstate_t值如何编码整个转换状态,特别是“这样的字节不应作为任何其他多字节字符的一部分出现”意味着空字节不能出现在多字节字符中。如果在错误输入中出现空字节 ,其中多字节字符的第二个,第三个等字节应该是,那么我将标准解释为说EOF中的部分多字节字符被默默忽略

对于mbrtowcs的情况,我对7.24.6.3.2,NULL函数的解读是:下一个1字节完成空宽字符,返回mbrtowc的值为0,结果状态为初始转换状态,因为:

  

如果相应的宽字符是空宽字符,则描述的结果状态是初始转换状态。

通过NULLs传递psmbrtowc的内部mbstate_t将重置为初始状态。