??xml version="1.0" encoding="utf-8" standalone="yes"?> "STREQUAL" "x86_64" Unknown arguments specified 在头文g中,定义?br>#ifdef MYDLL_EXPORTS 现在Q在变量m_structs前,dQ?br>template class MYDLL_API std::allocator<myStruct>; ?字节寚w对程序的影响: l构体A中包含了4字节长度的int一个,1字节长度的char一个和2字节长度的short型数据一?B也一?按理说A,B大小应该都是7字节?br>之所以出C面的l果是因为编译器要对数据成员在空间上q行寚w。上面是按照~译器的默认讄q行寚w的结?那么我们是不是可以改变编译器的这U默认对齐设|呢,当然可以.例如: ?~译器是按照什么样的原则进行对齐的? ?如何修改~译器的默认寚w? ?针对字节寚w,我们在编E中如何考虑? 如果在编E的时候要考虑节约I间的话,那么我们只需要假定结构的首地址?,然后各个变量按照上面的原则进行排列即?基本的原则就是把l构中的变量按照 cd大小从小到大声明,量减少中间的填补空?q有一U就是ؓ(f)了以I间换取旉的效?我们昄的进行填补空间进行对?比如:有一U用空间换旉?法是昑ּ的插入reserved成员Q?br> struct A{ reserved成员Ҏ(gu)们的E序没有什么意?它只是v到填补空间以辑ֈ字节寚w的目?当然即不加q个成员通常~译器也?x)给我们自动填补寚w,我们自己加上它只是v到显式的提醒作用. ?字节寚w可能带来的隐(zhn)? p=&i; ?如何查找与字节对齐方面的问题: ?相关文章:转自http://blog.csdn.net/goodluckyxl/archive/2005/10/17/506827.aspx ARM下的寚w处理 3.13 type qulifiers 有部分摘自ARM~译器文档对齐部?/p>
寚w的? p = (char*)&a; /* //q样可以很清楚的看到非对齐访问是如何产生错误?br>//以及(qing)如何消除非对齐访问带来问?br>//也可以看到非寚w讉K和对齐访问的指o(h)差异D效率问题 下面对于微Y对于c++语言的修改做一下分析! Lambda表达?/p>
很多~程~程语言都支持匿名函?anonymous function)。所谓匿名函敎ͼ是q个函数只有函数体,而没有函数名。Lambda表达式就是实现匿名函数的一U编E技巧,它ؓ(f)~写匿名函数提供了简明的函数式的句法。同hVisual Studio中的开发语aQVisual Basic和Visual C#早就实现了对Lambda表达式的支持Q终于Visual C++q次也不甘落后,在Visual Studio 2010中添加了对Lambda表达式的支持?br> Lambda表达式得函数可以在使用的地方定义,q且可以在Lambda函数中用Lambda函数之外的数据。这׃ؓ(f)针对集合操作带来了很大的便利。在作用上,Lambda表达式类g函数指针和函数对象,Lambda表达式很好地兼顾了函数指针和函数对象的优点,却没有它们的~点。相对于函数指针或是函数对象复杂的语法Ş式,Lambda表达式用非常简单的语法可以实现同L(fng)功能Q降低了Lambda表达式的学习(fn)隑ֺQ避免了使用复杂的函数对象或是函数指针所带来的错误。我们可以看一个实际的例子Q?/p>
view plaincopy to clipboardprint? using namespace std; int _tmain(int argc, _TCHAR* argv[]) return 0; q段代码循环遍历输出vector中的每一个数Qƈ判断q个数是奇数q是偶数。我们可以随时修改Lambda表达式而改变这个匿名函数的实现Q修改对集合的操作。在q段代码中,C++使用一对中括号“[]”来表CLambda表达式的开始,其后?#8221;(int n)”表示Lambda表达式的参数。这些参数将在Lambda表达式中使用到。ؓ(f)了体?x)Lambda表达式的z,我们来看看同L(fng)功能Q如何用函数对象实玎ͼ(x) struct LambdaFunctor { } int _tmain(int argc, _TCHAR* argv[]) for (int i = 0; i < 10; ++i) { for_each(v.begin(), v.end(), LambdaFunctor()); return 0; 静态断astatic_assert 在之前的C++标准C++03中,我们可以使用两种断言Q?br> • 使用预处理中的条件编译和#error指o(h)Q可以在预处理阶D|查一些编译条?br> • 可以使用宏assert来进行运行时(g)查,以确保程序逻辑的正?/p>
但?errorҎ(gu)是非常烦(ch)琐的Qƈ且不能够Ҏ(gu)板参数进行检查,因ؓ(f)模板实例化是在编译时q行Q?errorҎ(gu)是在预处理阶D进行的。而assert宏是在运行时q行(g)查。不隑֏玎ͼ我们~少了一样东西,那就是可用于在编译时(g)查的工具。于是,静态断a应运而生?/p>
在新的C++标准C++0x中,加入了对静态断a的支持,引入了新的关键字static_assert来表C静态断a。用静态断aQ我们可以在E序的编译时期检一些条件是否成立,q个Ҏ(gu)在调试模板函数的模板参数时特别有用。在~译的时候,模板函数实例化,q时我们可以用静态断aL试模板函数的参数是否按照我们的设计拥有合适的倹{例如下面这D代码:(x) view plaincopy to clipboardprint? int main() { return 0; auto关键?/p>
在C++0x中,auto关键字的意义发生了改变。从Visual C++ 2010开始,auto关键字将用于指引~译器根据变量的初始值来军_变量的数据类型。换句话_(d)我们可以把auto当成一U新的数据类型,它可?#8220;从初始化?initialize)中推导出所代表的变量的真正cd”。这U对auto关键字的使用方式可以大大消除当前替代方式所D的冗长和易出错的代码。我们看一个实际的例子Q?/p>
view plaincopy to clipboardprint? int main() { const regex r("(\\w+) (\\w+)"); for (string s; getline(cin, s); ) { return 0; 另外Q跟其他数据cd一P我们也可以对auto关键字进行修饎ͼ例如dconstQ指?*)Q左值引?&)Q右值引?&&){等Q编译器?x)根据a(chn)utocd所代表的真正的数据来决定这些修饰的具体含义?/p>
Z兼容一些旧有的C++代码Q我们可以?Zc:autoq个~译器选项Q来告诉~译器是采用auto关键字的原有定义q是在新标准C++0x中的定义?/p>
叛_引?/p>
作ؓ(f)最重要的一语aҎ(gu),叛_引?rvalue references)被引入到 C++0x中。我们可以通过操作W?#8220;&&”来声明一个右值引用,原先在C++中?#8220;&”操作W声明的引用现在被称为左值引用? int a; int b; int& a_lvref = int(); // error C2440: 'initializing' : cannot convert from 'int' to 'int &' 叛_是无名的数据,例如函数的返回g般说来就是右倹{当对右D行操作的时候,叛_本w往往没有必要保留Q因此在某些情况下可以直?#8220;Ud”之。通过叛_引用,E序可以明确的区分出传入的参数是否ؓ(f)叛_|从而避免了不必要的拯Q程序的效率也就得到了提高。我们考虑一个简单的数据交换的小E序Q从中来体会(x)叛_引用所带来的效率提升。我们可以写一个函数swap来实C个变量值的交换Q?/p>
view plaincopy to clipboardprint? 那么Q如果用右值引用如何实现呢Q?/p>
view plaincopy to clipboardprint? template <class T> template <class T> void swap(T& a, T& b) int _tmain(int argc, _TCHAR* argv[]) return 0; 正是拯(Copy)和移?Move)的差别,使得叛_引用成为C++0x中最Ȁ动h心的新特性之一。从实践角度Ԍ它能够完是解决C++中长久以来ؓ(f)人所诟病的(f)时对象的效率问题。从语言本nԌ它健全了C++中的引用cd在左值右值方面的~陷。从库设计者的角度Ԍ它给库设计者又带来了一把利器。而对于广大的库用者而言Q不动一兵一卒便能够获得“免费?#8221;效率提升?/p>
在Visual Studio 2010中,因ؓ(f)有了对这些C++0x新特性的支持Q重新点燃了E序员们对C++的热情。C++重振雄风Q指日可待!
]]>
float x = 100.1234;
]]>
解决Q?br />
if($ENV(COMPILING_TYPE) STREQUAL "x86_64")
改ؓ(f)Q?/span>
if(($ENV(COMPILING_TYPE)) STREQUAL "x86_64")
O了!
]]>
sudo yum install centos-release-scl
2、安装devtoolset
sudo yum install devtoolset-9-gcc*
Q如果想安装7.*版本的,改成devtoolset-7-gcc*Q?
3、激zd应的devtoolsetQ所以你可以一ơ安装多个版本的devtoolsetQ?br />需要的时候用下面以下命o(h)切换到对应的版本
scl enable devtoolset-9 bash
4、查看gcc版本
gcc -v
昄?.x
O了!
]]>
]]>
]]>
{
...
private:
std::vector<MY_STRUCT> m_structs;
}
但是~译Ӟvs2005l出了warning C4251: 'CLASS_TEST::m_structs' : class 'std::vector<_Ty>' needs to have dll-interface to be used by clients of class ‘CLASS_TEST’的警告信息。费了很大的劲才解决掉,记录下来?/p>
#define MYDLL_API __declspec(dllexport)
#else
#define MYDLL_API __declspec(dllimport)
#endif
template class MYDLL_API std::vector<myStruct, std::allocator<myStruct> >;
q样Q即可以了?/p>
]]>
一.什么是字节寚w,Z么要寚w?
C计算Z内存I间都是按照byte划分的,从理Z讲似乎对Mcd的变量的讉K可以从Q何地址开始,但实际情冉|在访问特定类型变量的时候经常在?定的内存地址讉KQ这需要各U类型数据按照一定的规则在空间上排列Q而不是顺序的一个接一个的排放Q这是寚w?br> 寚w的作用和原因Q各个硬件^台对存储I间的处理上有很大的不同。一些^台对某些特定cd的数据只能从某些特定地址开始存取。比如有些架构的CPU在访?一个没有进行对齐的变量的时候会(x)发生错误,那么在这U架构下~程必须保证字节寚w.其他q_可能没有q种情况Q但是最常见的是如果不按照适合其^台要求对 数据存放q行寚wQ会(x)在存取效率上带来损失。比如有些^台每ơ读都是从偶地址开始,如果一个int型(假设?2位系l)如果存放在偶地址开始的地方Q那 么一个读周期可以读32bitQ而如果存攑֜奇地址开始的地方Q就需?个读周期Qƈ对两ơ读出的l果的高低字节进行拼凑才能得到该32bit?据。显然在d效率上下降很多?/p>
先让我们看几个例子吧(32bit,x86环境,gcc~译?:
讄构体如下定义Q?br>struct A
{
int a;
char b;
short c;
};
struct B
{
char b;
int a;
short c;
};
现在已知32位机器上各种数据cd的长度如?
char:1(有符hW号?
short:2(有符hW号?
int:4(有符hW号?
long:4(有符hW号?
float:4 double:8
那么上面两个l构大小如何?
l果?
sizeof(strcut A)gؓ(f)8
sizeof(struct B)的值却?2
#pragma pack (2) /*指定?字节寚w*/
struct C
{
char b;
int a;
short c;
};
#pragma pack () /*取消指定寚wQ恢复缺省对?/
sizeof(struct C)值是8?br>修改寚wgؓ(f)1Q?br>#pragma pack (1) /*指定?字节寚w*/
struct D
{
char b;
int a;
short c;
};
#pragma pack () /*取消指定寚wQ恢复缺省对?/
sizeof(struct D)gؓ(f)7?br>后面我们再讲?pragma pack()的作?
先让我们看四个重要的基本概念Q?br>1.数据cd自n的对齐|(x)
对于char型数据,其自w对齐gؓ(f)1Q对于short型ؓ(f)2Q对于int,float,doublecdQ其自n寚wgؓ(f)4Q单位字节?br>2.l构体或者类的自w对齐|(x)其成员中自n寚w值最大的那个倹{?br>3.指定寚w|(x)#pragma pack (value)时的指定寚w值value?br>4.数据成员、结构体和类的有效对齐|(x)自n寚w值和指定寚wg的那个倹{?br>?了这些|我们可以很方便的来讨论具体数据l构的成员和其自w的寚w方式。有效对齐值N是最l用来决定数据存攑֜址方式的|最重要。有效对齐NQ就?表示“寚w在N?#8221;Q也是说该数据?存放起始地址%N=0".而数据结构中的数据变量都是按定义的先后顺序来排放的。第一个数据变量的起始地址是?据结构的起始地址。结构体的成员变量要寚w排放Q结构体本n也要Ҏ(gu)自n的有效对齐值圆?是l构体成员变量占用总长度需要是对结构体有效寚w值的整数 倍,l合下面例子理解)。这样就不能理解上面的几个例子的g?br>例子分析Q?br>分析例子BQ?br>struct B
{
char b;
int a;
short c;
};
?设B从地址I间0x0000开始排放。该例子中没有定义指定对齐|在笔者环境下Q该值默认ؓ(f)4。第一个成员变量b的自w对齐值是1Q比指定或者默认指?寚w?,所以其有效寚wgؓ(f)1Q所以其存放地址0x0000W合0x0000%1=0.W二个成员变量aQ其自n寚wgؓ(f)4Q所以有效对齐g?Q?所以只能存攑֜起始地址?x0004?x0007q四个连l的字节I间中,复核0x0004%4=0,且紧靠第一个变量。第三个变量c,自n寚wgؓ(f) 2Q所以有效对齐g?Q可以存攑֜0x0008?x0009q两个字节空间中Q符?x0008%2=0。所以从0x0000?x0009存放?都是B内容。再看数据结构B的自w对齐gؓ(f)其变量中最大对齐?q里是bQ所以就?Q所以结构体的有效对齐g?。根据结构体圆整的要求, 0x0009?x0000=10字节Q(10Q?Q%4Q?。所?x0000A?x000B也ؓ(f)l构体B所占用。故B?x0000?x000B 共有12个字?sizeof(struct B)=12;其实如果p一个就来说它已满_节对齐了, 因ؓ(f)它的起始地址?,因此肯定是对齐的,之所以在后面补充2个字?是因为编译器Z实现l构数组的存取效?试想如果我们定义了一个结构B的数l??么第一个结构v始地址?没有问题,但是W二个结构呢?按照数组的定?数组中所有元素都是紧挨着?如果我们不把l构的大补充ؓ(f)4的整数?那么下一 个结构的起始地址是0x0000A,q显然不能满结构的地址寚w?因此我们要把l构补充成有效对齐大的整数?其实诸如:对于char型数据,?自n寚wgؓ(f)1Q对于short型ؓ(f)2Q对于int,float,doublecdQ其自n寚wgؓ(f)4Q这些已有类型的自n寚wg是基于数l考虑??是因些类型的长度已知?所以他们的自n寚wg已知了.
同理,分析上面例子CQ?br>#pragma pack (2) /*指定?字节寚w*/
struct C
{
char b;
int a;
short c;
};
#pragma pack () /*取消指定寚wQ恢复缺省对?/
W?一个变量b的自w对齐gؓ(f)1Q指定对齐gؓ(f)2Q所以,其有效对齐gؓ(f)1Q假设C?x0000开始,那么b存放?x0000Q符?x0000%1= 0;W二个变量,自n寚wgؓ(f)4Q指定对齐gؓ(f)2Q所以有效对齐gؓ(f)2Q所以顺序存攑֜0x0002?x0003?x0004?x0005四个q箋 字节中,W合0x0002%2=0。第三个变量c的自w对齐gؓ(f)2Q所以有效对齐gؓ(f)2Q顺序存?br>?x0006?x0007中,W合 0x0006%2=0。所以从0x0000?x00007共八字节存放的是C的变量。又C的自w对齐gؓ(f)4Q所以C的有效对齐gؓ(f)2。又8%2=0,C 只占?x0000?x0007的八个字节。所以sizeof(struct C)=8.
1.在VC IDE中,可以q样修改Q[Project]|[Settings],c/c++选项卡Category的Code Generation选项的Struct Member Alignment中修改,默认?字节?br>2.在编码时Q可以这样动态修改:(x)#pragma pack .注意:是pragma而不是progma.
char a;
char reserved[3];//使用I间换时?br> int b;
}
代码中关于对齐的隐?zhn)Q很多是隐式的。比如在强制cd转换的时候。例如:(x)
unsigned int i = 0x12345678;
unsigned char *p=NULL;
unsigned short *p1=NULL;
*p=0x00;
p1=(unsigned short *)(p+1);
*p1=0x0000;
最后两句代码,从奇数边界去讉Kunsignedshort型变量,昄不符合对齐的规定?br>在x86上,cM的操作只?x)?jing)响效率,但是在MIPS或者sparc上,可能是一个error,因ؓ(f)它们要求必须字节寚w.
如果出现寚w或者赋值问题首先查?br>1. ~译器的big little端设|?br>2. 看这U体pLw是否支持非寚w讉K
3. 如果支持看设|了寚w与否,如果没有则看讉K旉要加某些Ҏ(gu)的修饰来标志其特D访问操作?/p>
from DUI0067D_ADS1_2_CompLib
1.__align(num)
q个用于修改最高别对象的字节边界。在汇编中用LDRD或者STRD?br> p用到此命令__align(8)q行修饰限制。来保证数据对象是相应对齐?br> q个修饰对象的命令最大是8个字节限?可以?字节的对象进?字节
寚w,但是不能?字节的对?字节寚w?br> __align是存储类修改,他只修饰最高cd对象不能用于l构或者函数对象?br>
2.__packed
__packed是进行一字节寚w
1.不能对packed的对象进行对?br> 2.所有对象的d讉K都进行非寚w讉K
3.float?qing)包含float的结构联合及(qing)未用__packed的对象将不能字节寚w
4.__packed对局部整形变量无影响
5.强制由unpacked对象向packed对象转化是未定义,整Ş指针可以合法?br> 义ؓ(f)packed?br> __packed int* p; //__packed int 则没有意?br> 6.寚w或非寚wd讉K带来问题
__packed struct STRUCT_TEST
{
char a;
int b;
char c;
} ; //定义如下l构此时b的v始地址一定是不对齐的
//在栈中访问b可能有问?因ؓ(f)栈上数据肯定是对齐访问[from CL]
//下面变量定义成全局静态不在栈?
static char* p;
static struct STRUCT_TEST a;
void Main()
{
__packed int* q; //此时定义成__packed来修饰当前q指向为非寚w的数据地址下面的访问则可以
q = (int*)(p+1);
*q = 0x87654321;
/*
得到赋值的汇编指o(h)很清?br>ldr r5,0x20001590 ; = #0x12345678
[0xe1a00005] mov r0,r5
[0xeb0000b0] bl __rt_uwrite4 //在此处调用一个写4byte的操作函?
[0xe5c10000] strb r0,[r1,#0] //函数q行4ơstrb操作然后q回保证了数据正的讉K
[0xe1a02420] mov r2,r0,lsr #8
[0xe5c12001] strb r2,[r1,#1]
[0xe1a02820] mov r2,r0,lsr #16
[0xe5c12002] strb r2,[r1,#2]
[0xe1a02c20] mov r2,r0,lsr #24
[0xe5c12003] strb r2,[r1,#3]
[0xe1a0f00e] mov pc,r14
*/
如果q没有加__packed修饰则汇~出来指令是q样直接?x)导致奇地址处访问失?br>[0xe59f2018] ldr r2,0x20001594 ; = #0x87654321
[0xe5812000] str r2,[r1,#0]
*/
}
本文来自CSDN博客Q{载请标明出处Q?a >http://blog.csdn.net/jszj/archive/2009/02/20/3915328.aspx
]]>
微Y卛_?010q??2日发布VS2010的正式版Q对于c++语言做了修改Q之更加符合c++标准?/p>
·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150
#include "stdafx.h"
#include <algorithm>
#include <iostream>
#include <ostream>
#include <vector>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
vector<int> v;
for (int i = 0; i < 10; ++i) {
v.push_back(i);
}
for_each(v.begin(), v.end(), [] (int n) {
cout << n;
if (n % 2 == 0) {
cout << " even ";
} else {
cout << " odd ";
}
});
cout << endl;
return 0;
}
#include "stdafx.h"
#include <algorithm>
#include <iostream>
#include <ostream>
#include <vector>
{
vector<int> v;
for (int i = 0; i < 10; ++i) {
v.push_back(i);
}
for_each(v.begin(), v.end(), [] (int n) {
cout << n;
if (n % 2 == 0) {
cout << " even ";
} else {
cout << " odd ";
}
});
cout << endl;
}
view plaincopy to clipboardprint?
·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150
#include "stdafx.h"
#include <algorithm>
#include <iostream>
#include <ostream>
#include <vector>
using namespace std;
struct LambdaFunctor {
void operator()(int n) const {
cout << n << " ";
if (n % 2 == 0) {
cout << " even ";
} else {
cout << " odd ";
}
}
};
int _tmain(int argc, _TCHAR* argv[])
{
vector<int> v;
for (int i = 0; i < 10; ++i) {
v.push_back(i);
}
for_each(v.begin(), v.end(), LambdaFunctor());
cout << endl;
return 0;
}
#include "stdafx.h"
#include <algorithm>
#include <iostream>
#include <ostream>
#include <vector>
using namespace std;
void operator()(int n) const {
cout << n << " ";
if (n % 2 == 0) {
cout << " even ";
} else {
cout << " odd ";
}
};
{
vector<int> v;
v.push_back(i);
}
cout << endl;
}
通过比较我们可以发玎ͼLambda表达式的语法更加z,使用h更加单高?/p>
·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150
template <int N> struct Kitten {
static_assert(N < 2, "Kitten<N> requires N < 2.");
};
int main() {
Kitten<1> peppermint;
Kitten<3> jazz;
return 0;
}
template <int N> struct Kitten {
static_assert(N < 2, "Kitten<N> requires N < 2.");
};
Kitten<1> peppermint;
Kitten<3> jazz;
}
当我们在dC使用“1”d例化Kittenq个l构体时Q在~译的时候,静态断astatic_assert?x)测试参数N的|当N的值小?时就?x)生一个断a错误Qƈ相应的调试帮助信息输出?#8220;Error List”H口中,q样E序员就可以寚w题快速定位,解决问题更加方便了?br>
? static_assert断言?qing)其输?/p>
另外Q静态断aq带来很多其他的优势。例如静态断a在编译时q行处理Q不?x)生Q何运行时ȝ间和旉上的开销Q这׃得它比assert宏具有更好的效率。另外比较重要的一个特性是如果断言p|Q它?x)生有意义且充分的诊断信息Q帮助程序员快速解决问题?/p>
·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150
#include <iostream>
#include <map>
#include <ostream>
#include <regex>
#include <string>
using namespace std;
using namespace std::tr1;
int main() {
map<string, string> m;
const regex r("(\\w+) (\\w+)");
for (string s; getline(cin, s); ) {
smatch results;
if (regex_match(s, results, r)) {
m[results[1]] = results[2];
}
}
for (auto i = m.begin(); i != m.end(); ++i) {
cout << i->second << " are " << i->first << endl;
}
return 0;
}
#include <iostream>
#include <map>
#include <ostream>
#include <regex>
#include <string>
using namespace std;
using namespace std::tr1;
map<string, string> m;
smatch results;
if (regex_match(s, results, r)) {
m[results[1]] = results[2];
}
}
for (auto i = m.begin(); i != m.end(); ++i) {
cout << i->second << " are " << i->first << endl;
}
}
在这D代码中Q我们用auto关键字来代替了真正的数据cdmap<string, string>::iteratorQ这使得整个代码自然而简z?/p>
int& a_lvref = a; // 左值引?/p>
int&& b_rvref = b; // 叛_应?br> 左值引用和叛_引用的表现行ؓ(f)基本一_(d)它们唯一的差别就是右值引用可以绑定到一个(f)时对?叛_?上,而左值引用不可以。例如:(x)
int&& b_rvref = int(); // OK!
在第一行代码中Q我们将一个(f)时对象int()l定C个左值引用,生一个编译错误。而在W二行中Q我们将临时对象l定到右值引用,可以顺利通过~译?/p>
·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150
template <class T> swap(T& a, T& b)
{
T tmp(a); // tmp对象创徏后,我们拥有了a的两份拷?nbsp;
a = b; // 现在我们拥有b的两份拷?nbsp;
b = tmp; // 现在我们拥有a的两份拷?nbsp;
}
template <class T> swap(T& a, T& b)
{
T tmp(a); // tmp对象创徏后,我们拥有了a的两份拷?br> a = b; // 现在我们拥有b的两份拷?br> b = tmp; // 现在我们拥有a的两份拷?br>}
在这D代码中Q虽然我们只是ؓ(f)了进行简单的数据交换Q但是却执行了多ơ对象拷贝。这些对象的拯操作Q特别是当这些对象比较大的时候,无疑?x)?jing)响程序的效率?/p>
·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150
#include "stdafx.h"
template <class T>
T&& move(T&& a)
{
return a;
}
template <class T> void swap(T& a, T& b)
{
T tmp(move(a)); // 对象a被移动到对象tmpQa被清I?nbsp;
a = move(b); // 对象b被移动到对象aQb被清I?nbsp;
b = move(tmp); // 对象tmp被移动到对象b
}
int _tmain(int argc, _TCHAR* argv[])
{
int a = 1;
int b = 2;
swap(a, b);
return 0;
}
#include "stdafx.h"
T&& move(T&& a)
{
return a;
}
{
T tmp(move(a)); // 对象a被移动到对象tmpQa被清I?br> a = move(b); // 对象b被移动到对象aQb被清I?br> b = move(tmp); // 对象tmp被移动到对象b
}
{
int a = 1;
int b = 2;
swap(a, b);
}
在这D重新实现的代码中,我们使用了一个move()函数来代替对象的赋值操作符“=”,move()只是单地接受一个右值引用或者左值引用作为参敎ͼ然后直接q回相应对象的右值引用。这一q程不会(x)产生拯(Copy)操作Q而只?x)将源对象移?Move)到目标对象?/p>
本文来自CSDN博客Q{载请标明出处Q?a >http://blog.csdn.net/yincheng01/archive/2010/03/11/5367032.aspx
]]>
#include<stdio.h>
long test(int a,int b)
{
a = a + 1;
b = b + 100;
return a + b;
}
写成32位汇~就是这?br />;//////////////////////////////////////////////////////////////////////////////////////////////////////
.386
.module flat,stdcall ;q里我们用stdcall 是函数参数 压栈的时候从最后一个开始压Q和被调用函数负责清?br />option casemap:none ;区分大小?br />
includelib msvcrt.lib ;q里是引入类?#160;相当?#160;#include<stdio.h>?#160;
printf PROTO C:DWORD,:VARARG ;q个是声明一下我们要用的函数_(d)到时?#160;汇编E序?x)自动到msvcrt.lib里面扄?#160;
;:VARARG 表后面的参数不确?#160;因ؓ(f)C是q样的printf(const char *, ...);
;q样的函数要注意 不是被调用函数负责清?#160;因ؓ(f)它本w不知道有多个参数
;而是有调用者负责清?#160; 下面?x)详l说?br />.data
szTextFmt BYTE '%d',0 ;q个是用来类型{换的Q跟C的一?字符用字节类?br />a dword 1000 ;假设
b dword 2000 ;处理数值都用双?#160;没有int 跟long 的区?br />
;/////////////////////////////////////////////////////////////////////////////////////////
.code
_test proc A:DWORD,B:DWORD
push ebp
mov ebp,esp
mov eax,dword ptr ss:[ebp+8]
add eax,1
mov edx,dword ptr ss:[ebp+0Ch]
add edx,100
add eax,edx
pop ebp
retn 8
_test endp
_main proc
push dword ptr ds:b ;反汇~我们看到的b׃是b了而是一个[*****]数字 dword ptr 是我们在ds(数据D?把[*****]
;开始的一个双字长数值取出来
push dword ptr ds:a ;跟她对应的还?#160;byte ptr ****是取一个字节出?#160;比如q样 mov al,byte ptr ds:szTextFmt
;把 % 取出?#160;而不包括 d
call _test
push eax ;假设push eax的地址?#215;××××
push offset szTextFmt
call printf
add esp,8
ret
_main endp
end _main
;////////////////////////////////////////////////////////////// 下面介绍堆栈的变?br />首先要明白的?#160;操作堆栈D?#160;ss 只能?#160;esp或ebp寄存?#160;其他的寄存器eax ebx edx{都不能够用 ?#160;esp永远指向堆栈栈顶 ebp用来 在堆栈段
里面d
push 指o(h)是压?#160;ESP=ESP-4
pop 指o(h)是出?#160;ESP=ESP+4
我们假设main函数一开始堆栈定?#160;ESP=400
push dword ptr ds:b ;ESP-4=396 ->里面的值就?#160;2000 是b的数?br />push dword ptr ds:a ;ESP-4=392 ->里面的值就?#160;1000 是a的数?br />call test ;ESP-4=388Q?gt;里面的数值是什么?q个太重要了 是我们用来找游戏函数的原理所在?br /> 里面的数值就是call test 指o(h)下一条指令的地址Q?gt;即push eax的地址×××××
Ctest函数里面
push ebp ;ESP-4=384->里面保存了当前ebp的?#160;而不是把ebp清零
mov ebp,esp ;q里ESPQ?84没变化了,但是 ebp=esp=384,Z么要q样做呢 因ؓ(f)我们要用ebp到堆栈里面找参数
mov eax,dword ptr ss:[ebp+8] ;反汇~是q样?#160;xZ么a是[ebp+8]?br /> ;我们往上看看堆栈里地址392处就保存着a的?#160;q里ebp=384 加上8正好是392?br /> ;q样把传递过来的1000拿了出来eax=1000
add eax,1 ;相当?#160;a+1?#160;eax=1001
mov edx,dword ptr ss:[ebp+0Ch] ; 0Ch=12 一样道理这里指向堆栈的地址?84+12=396 是2000?#160;edx=2000
add edx,100 ;相当?#160;b+100 edx=2100
add eax,edx ;eax=eax+edxQ?001Q?100Q?101 q里eax已经保存了最l的l果?#160;
;因ؓ(f)win32汇编一般用eaxq回l果 所以如果最l结果不是在eax里面的话 q要把它攑ֈeax
;比如假设我的l果保存在变量nRet里面 最后还是要q样 mov eax,dword ptr nRet
pop ebp ;ESP=384+4=388 而保存在栈顶384的?#160;保存?#160;ebp?#160;x复ebp原来的?#160;
;因ؓ(f)一开始我们就把ebp的值压栈了Qmov ebp,esp已经改变了ebp的|q里恢复是保证了堆栈^?br />retn 8 ;ESP+8->396 q里retn是由pȝ调用?#160;我们不用?#160;pȝ?x)自动把EIP指针指向 原来的call的下一条指?br /> ;׃是系l自动恢复了call那里的压栈所?#160;真正q回到的时候ESP+4是恢复了call压栈的堆?br /> ;Cq个时?#160;ESP=400 是函数调用开始的堆栈Q就是说函数调用前跟函数调用后的堆栈是一L(fng)
;q就是堆栈^?#160;
׃我们用stdcall上面retn 8是被调用者负责恢复堆栈的意思了Q函数test是被调用者,所以负责把堆栈?,call 那里是系l自动恢复的
push eax ;ESP-4=396->里面保存了eax的?101
;上面已经看到了eax保存着q回|我们要把它传lprintf也是通过堆栈传?#160;
push offset szTextFmt ;ESP-4=392->里面保存了szTextFmt的地址 也就是C里面的指?#160;实际上没有什么把字符串传递的Q我们传的都是地址
;无论是在汇编或C 所以在汇编里没有什么字W串cd 用最多的是DWORD。嘿嘿游戏里面传递参?#160;单多?br />call printf ;ESP-4=388->里面保存了下一条指令的地址
add esp,8 ;ESP+8=400 恢复了调用printf前的堆栈状?br /> ;上面说了׃printf后面参数?VARARG q样的类型是有调用者恢复堆栈的 所以printf里面没有retn 8之类的指?br /> ;q是p用者负责清?#160;main是调用?#160;所以下面一句就?#160;add esp,8 把堆栈恢复到调用printf之前
;而call printf那里的压?#160;是由pȝ做的 恢复的工作也是系l完?#160;我们不用?#160;只是知道里面保存是返回地址够
;?br />ret ;main 函数q回 其他的事情是pȝ自动搞定 我们不用?#160;d完成
]]>
作者:(x)许式?2006q?1月某?/font>
内存理是C++E序员的痛。我的《内存管理变革》系列就是试图讨论更为有效的内存理方式Q以杜绝Q或减少Q内存泄漏,减轻C++E序员的负担。由于工作忙的缘故,q个pd目前未完Q暂停?br />
q篇短文我想换个方式Q讨Z下如何以最快的速度扑ֈ内存泄漏?br />
认是否存在内存泄漏
我们知道QMFCE序如果(g)到存在内存泄漏Q退出程序的时候会(x)在调试窗口提醒内存泄漏。例如:(x)
class CMyApp : public CWinApp
{
public:
BOOL InitApplication()
{
int* leak = new int[10];
return TRUE;
}
};
产生的内存泄漏报告大体如下:(x)
Detected memory leaks!
Dumping objects ->
c:\work\test.cpp(186) : {52} normal block at 0x003C4410, 40 bytes long.
Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
Object dump complete.
q挺好。问题是Q如果我们不喜欢MFCQ那么难道就没有办法Q或者自己做Q?
呵呵Q这不需要。其实,MFC也没有自己做。内存泄漏检的工作是VC++的Cq行库做的。也是_(d)只要你是VC++E序员,都可以很方便地检内存泄漏。我们还是给个样例:(x)
#include <crtdbg.h>
inline void EnableMemLeakCheck()
{
_CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);
}
void main()
{
EnableMemLeakCheck();
int* leak = new int[10];
}
q行Q提醒:(x)不要按Ctrl+F5Q按F5Q,你将发现Q生的内存泄漏报告与MFCcMQ但有细节不同,如下Q?/font>
Detected memory leaks!
Dumping objects ->
{52} normal block at 0x003C4410, 40 bytes long.
Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
Object dump complete.
Z么呢Q看下面?/font>
定位内存泄漏׃哪一句话引v?br />你已l发现程序存在内存泄漏。现在的问题是,我们要找泄漏的根源?/font>
一般我们首先确定内存泄漏是׃哪一句引赗在MFC中,q一点很Ҏ(gu)。你双击内存泄漏报告的文字,或者在DebugH口中按F4QIDE帮你定位到甌该内存块的地斏V对于上例,也就是这一句:(x)
int* leak = new int[10];
q多多少对你分析内存泄漏有点帮助。特别地Q如果这个new仅对应一条deleteQ或者你把delete漏写Q,q将很快可以认问题的症l?
我们前面已经看到Q不使用MFC的时候,生成的内存泄漏报告与MFC不同Q而且你立d现按F4不灵。那么难道MFC做了什么手脚?
其实不是Q我们来模拟下MFC做的事情。看下例Q?
inline void EnableMemLeakCheck()
{
_CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);
}
#ifdef _DEBUG
#define new new(_NORMAL_BLOCK, __FILE__, __LINE__)
#endif
void main()
{
EnableMemLeakCheck();
int* leak = new int[10];
}
再运行这个样例,你惊喜地发现Q现在内存泄漏报告和MFC没有M分别了?/font>
快速找到内存泄?br />单确定了内存泄漏发生在哪一行,有时候ƈ不够。特别是同一个new对应有多处释攄情Ş。在实际的工E中Q以下两U情况很典型Q?
创徏对象的地Ҏ(gu)一个类工厂QClassFactoryQ模式。很多甚臛_部类实例由同一个new创徏。对于此Q定位到了new出对象的所在行基本没有多大帮助?
COM对象。我们知道COM对象采用Reference Countl护生命周期。也是_(d)对象new的地方只有一个,但是Release的地方很多,你要一个个排除?
那么Q有什么好办法Q可以迅速定位内存泄漏?
{:(x)有?/font>
在内存泄漏情况复杂的时候,你可以用以下Ҏ(gu)定位内存泄漏。这是我个h认ؓ(f)通用的内存泄漏追t方法中最有效的手Dc(din)?/font>
我们再回头看看crtdbg生成的内存泄漏报告:(x)
Detected memory leaks!
Dumping objects ->
c:\work\test.cpp(186) : {52} normal block at 0x003C4410, 40 bytes long.
Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
Object dump complete.
除了产生该内存泄漏的内存分配语句所在的文g名、行号ؓ(f)Q我们注意到有一个比较陌生的信息Q{52}。这个整数g表了什么意思呢Q?/font>
其实Q它代表了第几次内存分配操作。象q个例子Q{52}代表了第52ơ内存分配操作发生了泄漏。你可能要说Q我只newq一ơ,怎么?x)是W?2ơ?q很Ҏ(gu)理解Q其他的内存甌操作在C的初始化q程调用的呗?)
有没有可能,我们让程序运行到W?2ơ内存分配操作的时候,自动停下来,q入调试状态?所q,crtdbg实提供了这L(fng)函数Q即 long _CrtSetBreakAlloc(long nAllocID)。我们加上它Q?/font>
inline void EnableMemLeakCheck()
{
_CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);
}
#ifdef _DEBUG
#define new new(_NORMAL_BLOCK, __FILE__, __LINE__)
#endif
void main()
{
EnableMemLeakCheck();
_CrtSetBreakAlloc(52);
int* leak = new int[10];
}
你发玎ͼE序q行?int* leak = new int[10]; 一句时Q自动停下来q入调试状态。细l体?x)一下,你可以发玎ͼq种方式你获得的信息q比在程序退出时获得文g名及(qing)行号有h(hun)值得多。因为报告泄漏文件名?qing)行P你获得的只是静态的信息Q然而_CrtSetBreakAlloc则是把整个现场恢复,你可以通过对函数调用栈分析Q我发现很多Z?fn)惯看函数调用栈Q如果你属于q种情况Q我强烈推荐你去补上q一课,因ؓ(f)它太重要了)以及(qing)其他在线调试技巧,来分析生内存泄漏的原因。通常情况下,q种分析Ҏ(gu)可以?分钟内找到肇事者?/font>
当然Q_CrtSetBreakAlloc要求你的E序执行q程是可q原的(多次执行q程的内存分配顺序不?x)发生变化)。这个假讑֜多数情况下成立。不q,在多U程的情况下Q这一Ҏ(gu)旉以保证?/font>
附加说明Q?br />对“内存管理”相关的技术感兴趣Q这里可以看到我的所有关于内存管理的文章?/font>
本文来自CSDN博客Q{载请标明出处Q?/font> http://blog.csdn.net/i_like_cpp/archive/2007/06/28/1669962.aspx