C/C++bug记录

注:本文主要记录在编程实践中遇到的C/C++bug。

1.字节对齐

  字节对齐涉及到节省内存空间和提高CPU访问内存效率,而一般的PC程序员不会去设置字节对齐(默认的字节对齐访问效率较高)。字节对齐知识点,详见C语言字节对齐问题详解
  碰过的bug表现:程序中有一个C++类,它的一个int型变量表现极其诡异,明明给它赋值,但是读出的数据与赋给它的值不一样。通过调试,发现它在读取内存时错误,偏移了3个字节。在这个int型变量的前面,声明了一个bool变量,调试发现如果将bool变量放到int型变量之后,表现正常。后来发现,在程序中的其他地方(程序不是我写的),定义了一些1字节对齐的结构体,然后基本确认是字节对齐问题。果然在该C++类定义前面加上#pragma pack()这行代码,表现正常。然而,令人费解的是,我查看了所有代码文件,所有的#pragma pack(1)最终都接了一行#pragma pack(),将字节对齐设为vs中默认的字节对齐8.这里我有个疑问:既然设置了字节对齐,程序应该就会按照我的设置进行对齐访问,不应该会出现内存访问错误,为什么还会出现上述bug。而且之前的结构体重#pragma pack(1)和#pragma pack()都是成对出现的,也不可能会影响到C++类。(出现这种情况,就像是写的时候是一种内存对齐方式,而读的时候是另一种内存对齐方式,这很奇怪。)

2.Qt:connect中SIGNAL和SLOT参数传递问题

  bug表现:用如下函数进行信号槽连接connect(this, SIGNAL(signaltest(int a)), this, SLOT(slottest(int b)).然而在运行时不能进入槽函数void slottest(int b))。关于connect函数,查找Qt助手中的官方文档,其中一部分说明如下:

bool QObject::connect ( const QObject * sender, const char * signal, const QObject * receiver,
const char * method, Qt::ConnectionType type = Qt::AutoConnection ) [static]
Creates a connection of the given type from the signal in the sender object to the method in the receiver object. Returns true if the connection succeeds; otherwise returns false.
You must use the SIGNAL() and SLOT() macros when specifying the signal and the method, for example:
QLabel *label = new QLabel; QScrollBar *scrollBar = new QScrollBar;
This example ensures that the label always displays the current scroll bar value. Note that the signal and slots parameters must not contain any variable names, only the type. E.g. the following would not work and return false:
// WRONG QObject::connect(scrollBar, SIGNAL(valueChanged(int value)), label,
SLOT(setNum(int value)));
  

  从上面说明的黑体字“must not contain any variable names”可以看出,connect函数中的SIGNAL和SLOT函数不能带形参。在去掉之前connect调用中的形参之后,就解决了这个问题。

3.winsock.h和winsock2.h冲突

  这是在合并两个项目中碰到的问题,当时一个项目包含windows.h文件,另一个包含winsock2.h文件,而且windows.h文件在前。而windows.h文件包含了winsock.h文件,winsock2.h是winsock.h的升级版,其中有很多相同定义,所以会产生冲突。先来看看这3个.h文件:
  1.查看windows.h文件,其中一部分代码如下:

#ifndef WIN32_LEAN_AND_MEAN
...
#include //这是与winsock2.h冲突的引用文件
...
#endif /* WIN32_LEAN_AND_MEAN */

  发现宏定义WIN32_LEAN_AND_MEAN保护winsock.h的引入。
  2.查看winsock.h文件:

#ifndef _WINSOCKAPI_
#define _WINSOCKAPI_
winsock.h的主体代码
#endif

  发现winsock.h的所有代码被宏WINSOCKAPI保护,如果在winsock.h之前定义了宏WINSOCKAPI,则所有winsock.h中的定义都不会被包含。
  3.查看winsock2.h文件:

#ifndef _WINSOCK2API_
#define _WINSOCK2API_
#define _WINSOCKAPI_   /* Prevent inclusion of winsock.h in windows.h */
wincock2.h的主体代码
#endif

  发现其中有宏定义WINSOCKAPI,用来防止包含winsock.h。
  相应的就有3中解决办法:
  1.在项目最开始的编译文件中加#define WIN32_LEAN_AND_MEAN这一句,或者在项目属性管理器中C/C++预处理器定义中加入WIN32_LEAN_AND_MEAN这一行,即可避免windows.h文件中包含winsock.h文件,从而避免其与winsock2.h的冲突。
  2.类似上述方法,添加宏定义WINSOCKAPI,使得winsock.h中的所有代码无效。
  3.将winsock2.h文件放在windows.h的前面,由于其带有宏定义WINSOCKAPI,自然会避免包含winsock.h的代码,从而避免冲突的出现。
  另外,可以参考:WINSOCK.H WINSOCK2.H的区别及函数重复定义的解决方法

4.Qt:控件被父窗口影响

  bug表现:用Qt编的一个界面,其中添加了QlineEdit控件,但是在界面显示时,存在控件周围背景色不正常问题。
  在经过查找资料得知,Qt窗口、控件的风格设置主要通过两个方式:1) void setStyleSheet(const QString & styleSheet)函数;2)修改每个窗口或控件都包含的QPallete对象。具体可以参考Qt设置控件颜色和Qt的官方文档。
  经过代码查找,自己程序中用上述方式1)设置了控件父窗口的背景,所以出问题控件的风格应该是受到了父窗口背景的影响。链接使用setStyleSheet怎么样才不影响子组件也碰到了同样问题,发帖这最后提到了要在setStyleSheet()函数的Qstring参数,即参数字符串前面加一个符号”.”,即可避免该函数对子控件的影响。经过修改后,发现事实如此,bug解决。再来看看Qt官方文档关于Style Sheet解释的一部分:The Style Sheet Syntax,其中在Selector Types下面的examples表格中有这么一项:
  

Class Selector .QPushButton Matches instances of QPushButton, but not of its subclasses.
This is equivalent to *[class~=”QPushButton”].

  这里表明在类Selector名字(即setSyleSheet函数的参数)的前面加符号”.”,即可消除设置窗口风格对子类的影响。在我的bug中,将setSyleSheet的参数(即一个QString的字符串)QWidget……改为.QWidget……即可。

5.Qt:中文乱码问题

  在Qt编程中,经常会出现中文乱码问题,在Qt4中,添加下面这行代码:
QTextCodec::setCodecForTr(QTextCodec::codecForName("GBK"));
即可解决乱码问题。但在Qt5中,该函数已经去掉。在网上搜索了几个博客:
两种解决Qt5显示中文乱码的方法
解决Qt中文乱码以及汉字编码的问题(UTF-8/GBK)
QString乱谈(2)
从这几个博客中可知,乱码主要是因为Windows下的GBK编码与QString里的UTF编码不一样造成的。得出如下两个解决办法:
  1.在代码的开头,加入#pragma execution_character_set("utf-8"),将代码都用UTF-8编码,而在代码使用中文时,采用QString::fromUtf8(“中文”)即可。
  2.直接采用Qt5中的新函数,QStringLiteral(“中文”)即可。

6.Qt:二进制文件读写不一致

  Bug表现:采用如下代码,读入in.dat二进制文件,读入的字节比实际文件中的字节数少。
  

QFile file("in.dat");
file.open(QIODevice::ReadOnly|QIODevice::Text);
QByteArray byteArray = file.readAll();

这段代码中,实际读到的字节数少于文件中的字节数。
  原因:问题在于in.dat文件的打开,采用了QIODevice::Text模式。采用QIODevice::Text模式,本就可能造成写入读出文件的数据不一致。比如:在windows系统中,换行符是用”\r\n”两个字符表示,而在Qt中只用”\n”一个字符表示。这就造成了读出字节少于写入字节。参考链接:基本文件读写QFile
  结论:在读写二进制文件时,不要用QIODevice::Text模式,因为Qt会帮忙解析字符串,这会造成潜在的数据丢失。

7.Windows 64位系统中SysWOW64和System32文件夹的实质

  Bug表现:在开发软件时,将生成的exe放到没有装VS的电脑会运行不了。
  原因:尽管程序在Windows 64位操作系统下开发,但是开发时vs用的是win32配置。而在拷贝运行exe需要的库msvcp120.dll和msvcr120r.dll时,想当然的认为System32文件夹里装的是对应的win32的dll,所以从System32中拷出dll到exe的文件夹中,程序不能运行。后来,用SysWOW64文件夹中的dll替换后,正常运行。其原因是,64位操作系统的dll实际是放在文件夹System32中,而用于支持win32程序的dll是放在SysWOW64文件夹中的,SysWOW64实际表示的是32bit Windows On 64bit Windows(64位Windows上的32位Windows)。至于原因,参考链接:什么是SysWow64。

你可能感兴趣的:(C/C++,bug,C-C++)