鱼C论坛

 找回密码
 立即注册
查看: 4210|回复: 1

[技术交流] 新手原创---窄字节与宽字节

[复制链接]
发表于 2014-3-16 17:10:19 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 新手学习中 于 2014-3-16 19:28 编辑


                        窄字节与宽字节

1、什么是窄字节,什么是宽字节:
英文字符,其大小为1字节的,叫窄字节
英文字符,其大小为2字节的,叫宽字节


2、不管编译器宽窄设置,中文字符默认占2字节,英文字符默认占1字节
如: sizeof(‘你’)  结果是2 ,不管编译器如何设置宽窄
sizeof("你");结果3  中文2字节,结束符1字节  双引号表示字符串,自动添加结束符
sizeof('abc');结果是3   单引号是字符,没有结束符,每个英文字符占1字节
sizeof(''abc'');结果是4

3、字符实际储存的大小可人为改变,英文可以变大,中文变小会出错
英文字符可转换成一个字符占2字节。如:
sizeof(L'abc');结果6  L表示强行把一个字符变成两字节,这也就是宽字节,其实是用0填充了高位
sizeof(L'我bc');结果6  当然中文默认是2字节,L之后还是2字节
char m = '你' ; 编译没错,但是这里把原本2字节的,给了只有1字节空间的m ,结果数据丢失,运行显示时会有乱码
要正常显示,需要用多个char表示 ,如char m []= '你' ;
WCHAR m = L 'a';  转为宽字节后,需要2字节大小的类型来储存。

4、什么是窄字节环境,什么是宽字节环境:
编译器设置可设置为宽字节环境或者窄字节环境。
其区别就是:根据设置不同,把通用类型转换成窄或者宽类型。以及一些有两种类型的函数
如:数据类型
T 通用类型:TCHAR   头文件tchar.h
如果是窄字节环境,TCHAR转为CHAR
TCHAR m = 'a';
CHAR m ='a';
如果是宽字节环境,TCHAR转为wchar_t
wchar_t数据类型大小为2字节。
TCHAR m =L 'a';  //一定要加上L 否则报错
WCHAR m = L 'a';   //一定要加上L 否则报错

如:通用函数 _T(x)   函数把字符串参数转换成宽或者窄
宽环境:
_T("abc"); 8字节
窄环境 :
_T("abc");4字节,也可以直接写成 "abc"

编程时,最好使用通用类型,因为方便日后修改。

MessageBox---MessageBoxA 和 MessageBoxW

如果系统设置窄---则MessageBox变成MessageBoxA
如果系统设置宽---则MessageBox变成MessageBoxW


5、为何中文字符需要两个字节储存

英文字符只有a~z 大小写总的也就48种。
一个字节8位二进制,其数字的01组合种类完全满足其个数。255个字符
但是一个中文字符,却有太多种不同。所以一个字节8位的排列组合无法满足其个
数。
所以又了两个字节16位来表示一个中文字符。(还要表示韩文,日文等)


6、编译器的默认

vc++ 6.0 默认为Ansi编码,vs2005、vs2008、vs2010 等默认都是Unicode编码


7、为何建议把环境设置成宽字节
如果是窄字节环境,其程序在日文环境,韩文环境等可能会出现乱码
原因涉及一些底层原理。
宽字节把所有字符都以2字节储存,所以缺点是占用空间大。

8、什么时候必须进行窄宽转换

某些API只提供窄版本或者宽版本。但使用其返回值等又必须转换才能给其他使用

比如GetProcAddress
其第二个参数是指向窄字节的指针。不可指向宽字节。

9、一些数据类型不仅根据宽窄而变,而且还会根据根据系统而改变

比如size_t

编译器中定义为:
#ifndef   _SIZE_T_DEFINED
  #ifdef     _WIN64
  typedef   unsigned   __int64         size_t;   //8个字节,64位
  #else
  typedef   _W64   unsigned   int       size_t;   //4个字节,32位
  #endif
  #define   _SIZE_T_DEFINED
#endif


**********************************************************************************
**********************************************************************************
**********************************************************************************
**********************************************************************************


10、如何对字符变量进行窄宽转换

WideCharToMultiByte 实现宽字节转换到窄字节,也可统计字符长度(根据参数不同还能转换成其他类型)
MultiByteToWideChar 实现窄字节转换到宽字节

关键代码:

int needBytes = WideCharToMultiByte(CP_ACP, 0, pWideChar, -1, NULL, 0, NULL, NULL);
WideCharToMultiByte(CP_ACP, 0, pWideChar, -1, pszBuf, needBytes, NULL, NULL);


说明:

1、pWideChar表示待转换的字符串指针,-1表示长度为总字符个数-总长度(包含结束符),其他参数照上面传。
2、第一次调用没传转换后的缓冲区,函数的功能是返回总长度
3、第二次调用,需要传入缓冲区指针:
pszBuf = new char[needBytes+1];
注意,这里是needBytes+1。因为转换的时候,是把包含结束符整个长度给转换了。而空间本身还需要预留一个结束符。
实际上,转换后是有两个结束符。
4、虽然我们申请了needBytes+1,但是传参数的时候,是传needBytes,因为能用的只有needBytes长度。


int needWChar = MultiByteToWideChar(CP_ACP, 0, pChar, -1, NULL, 0);
  MultiByteToWideChar(CP_ACP, 0, pChar, -1, pszBuf, needWChar);




11、获取字符串长度(字符个数)的函数

Ansi:strlen(char *str);
Unicode:wcslen(wchar_t *str);

注意:strlen获取的长度不包含结束符,也就是说,他碰到结束符\0就结束。如果字符是"ab\0ab"
那么长度就是2。

12、获取字符串占用字节数:
Ansi:

char szStr[] = "abc";
sizeof(szStr);  



  char *psz = "defgh";
(strlen(psz)+1)*sizeof(char); //求总字节数
  strlen(psz)*sizeof(char);        //有效字节数


注意:strlen(psz) 求出的长度不包含结束符,碰到\0结束统计。如果是"ab\0ab"那么其统计的结果就是2

注意:不可用sizeof(psz),这只是统计指针变量字节数。
但是如果换成数组名,那就是数组总字节数。(因为这个是指针常量)


Unicode:
wchar_t szwStr[] = L"abc";
sizeof(szwStr); wchar_t *pwsz = L"defgh";

wcslen(pwsz)*sizeof(wchar_t);
(wcslen(pwsz)+1)*sizeof(wchar_t);

通用函数求字节数:

TCHAR szStr[] = _T("abc");
sizeof(szStr);

TCHAR *psz = _T("defgh");
_tcslen(psz)*sizeof(TCHAR);
(_tcslen(psz)+1)*sizeof(TCHAR);






12、简单宽窄转换方式:A2W、W2A、T2A、T2W  

返回值就是转换好的字符串 #include <atlbase.h>


如:
void f()
{
USES_CONVERSION;
wchar_t * p = A2W("abc");
}

说明:

1、包含头文件#include <atlbase.h>

2、使用之前必须要加 USES_CONVERSION;

3、此为静态空间,会自动释放

注意:不可“直接”在循环里使用:如:

void func()
{
    while(true)
    {
        {
            USES_CONVERSION;
            A2W("abc");
        }
    }
}

原因:
出括号范围后,局部变量依然存在,只有销毁活动记录后,局部变量才跟着销毁。但是出了括号就出了变量的作用域,没法通过变量名直接访问。(  c专家编程  第125页)

意思就是说,大括号出来后,变量没被释放,但不能访问,只有函数结束后才会被释放,因此这个循环导致内存无法被释放。


解决方法:让其包含在函数里:如:

void fn2()
{
    USES_CONVERSION;
    DoSomething(A2W("SomeString"));
} void fn()
{
    while(true)
    {
        fn2();
    }
}



头文件包含:

A2W   ------- #include <atlbase.h>

TCHAR  wchar_t ------#include"tchar.h"

WideCharToMultiByte  ------  #include <windows.h>





13、vc6.0如何修改成宽字节。

 打开[工程]->[设置…]对话框,在C/C++标签对话框的“预处理程序定义”中去除_MBCS,加上_UNICODE,UNICODE。(注意中间用逗号隔开)



     如果是MFC:


  因为MFC应用程序有针对Unicode专用的程序入口点,我们要设置entry point。否则就会出现连接错误。
  设置entry point的方法是:打开[工程]->[设置…]对话框,在Link页的Output类别的Entry Point里填上wWinMainCRTStartup



  
游客,如果您要查看本帖隐藏内容请回复


想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2015-5-15 16:54:27 | 显示全部楼层
{:1_1:}
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-11-25 23:33

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表