c++动态库中对于字符类型变量的格式化处理
一、问题
在使用stringstream对一个变量进行格式化的时候,发现格式化之后的字符串并不是一个可显示的字符,最后看了半天,发现问题在于这个变量定义的类型是char类型,导致格式化之后数值本身并没有变化。我记得这个问题甚至不是我第一次遇到,这个问题本身是一个很小的问题,但是既然几次遇到都没有什么印象,所以还是在这里简单记录一下吧。
二、gcc使用的stl库中对于字符串流的处理
1、stringstream的定义
gcc-4.1.0\libstdc++-v3\include\std\std_iosfwd.h
typedef basic_stringstream<char> stringstream; ///< @isiosfwd
2、字符串流的定义
gcc-4.1.0\libstdc++-v3\include\std\std_ostream.h
namespace std
{
// [27.6.2.1] Template class basic_ostream
/**
* @brief Controlling output.
*
* This is the base class for all output streams. It provides text
* formatting of all builtin types, and communicates with any class
* derived from basic_streambuf to do the actual output.
*/
template<typename _CharT, typename _Traits>
class basic_ostream : virtual public basic_ios<_CharT, _Traits>
{
……
template<typename _Traits2>
friend basic_ostream<char, _Traits2>&
operator<<(basic_ostream<char, _Traits2>&, const char*);
template<typename _CharT2, typename _Traits2>
friend basic_ostream<_CharT2, _Traits2>&
operator<<(basic_ostream<_CharT2, _Traits2>&, const char*);
gcc-4.1.0\libstdc++-v3\include\std\std_iomanip.h
// Specialization
template <class _Traits>
basic_ostream<char, _Traits>&
operator<<(basic_ostream<char, _Traits>& __out, char __c);
// Signed and unsigned
template<class _Traits>
basic_ostream<char, _Traits>&
operator<<(basic_ostream<char, _Traits>& __out, signed char __c)
{ return (__out << static_cast<char>(__c)); }
3、功能的实现
gcc-4.1.0\libstdc++-v3\include\bits\ostream.tcc
// Specializations.
template <class _Traits>
basic_ostream<char, _Traits>&
operator<<(basic_ostream<char, _Traits>& __out, char __c)
{
typedef basic_ostream<char, _Traits> __ostream_type;
typename __ostream_type::sentry __cerb(__out);
if (__cerb)
{
try
{
const streamsize __w = __out.width();
streamsize __len = 1;
char* __cs = &__c;
if (__w > __len)
{
__cs = static_cast<char*>(__builtin_alloca(__w));
__pad<char, _Traits>::_S_pad(__out, __out.fill(), __cs,
&__c, __w, __len, false);
__len = __w;
}
__out._M_write(__cs, __len);
__out.width(0);
}
catch(...)
{ __out._M_setstate(ios_base::badbit); }
}
return __out;
}
4、_M_write的实现
gcc-4.1.0\libstdc++-v3\include\std\std_ostream.h
// Core write functionality, without sentry.
void
_M_write(const char_type* __s, streamsize __n)
{
streamsize __put = this->rdbuf()->sputn(__s, __n);
if (__put != __n)
this->setstate(ios_base::badbit);
}
三、C标准库中的printf对于该内容的处理
1、格式化字符的处理位置
glibc-2.6\stdio-common\vfprintf.c
/* The function itself. */
int
vfprintf (FILE *s, const CHAR_T *format, va_list ap)
{
……
# define process_string_arg(fspec) \
LABEL (form_character): \
/* Character. */ \
if (is_long) \
goto LABEL (form_wcharacter); \
--width; /* Account for the character itself. */ \
if (!left) \
PAD (' '); \
if (fspec == NULL) \
outchar ((unsigned char) va_arg (ap, int)); /* Promoted. */ \
else \
outchar ((unsigned char) args_value[fspec->data_arg].pa_int); \
if (left) \
PAD (' '); \
break;
2、outchar的定义内容
#define outchar(Ch) \
do \
{ \
register const INT_T outc = (Ch); \
if (PUTC (outc, s) == EOF) \
{ \
done = -1; \
goto all_done; \
} \
else \
++done; \
} \
while (0)
PUTC宏的定义
# define PUTC(C, F) _IO_putc_unlocked (C, F)
也就是没有变化,直接将该内容添加到输出流中。从而对于char类型的变量直接按照原始内容放在了输出字节流中
#define _IO_putc_unlocked(_ch, _fp) \
(_IO_BE ((_fp)->_IO_write_ptr >= (_fp)->_IO_write_end, 0) \
? __overflow (_fp, (unsigned char) (_ch)) \
: (unsigned char) (*(_fp)->_IO_write_ptr++ = (_ch)))
四、总结
结论在于,当你使用标准字节流的时候,如果使用的是 << 操作符,这个时候如果变量不小心定义为了 char类型,那么这个输出结构可能就不是一个可以显示的字符。
下面是代码例子
tsecer@harry: cat outchar.cpp
#include <sstream>
#include <stdio.h>
#include <iostream>
int main()
{
int i = 1;
char c = 1;
std::stringstream ssi, ssc;
ssi<<"|"<<i<<"|";
ssc<<"|"<<c<<"|";
std::cout<<ssi.str()<<std::endl;
std::cout<<ssc.str()<<std::endl;
return 0;
}
tsecer@harry: g++ outchar.cpp
tsecer@harry: ./a.out | hexdump -C
00000000 7c 31 7c 0a 7c 01 7c 0a ||1|.|.|.|
00000008
tsecer@harry:
可以看到,内码输出还是为01,也就是保持原始值没有变化: