|
本帖最后由 ancientcc 于 2020-7-26 10:54 编辑
虽然MSC和GCC同是C/C++编译器,但怎么说还是存在些差别,要让代码能被MSC和GCC成功编译,在写时就要注意些细节。
定义嵌套STL类型,右侧要用“> >”而不是“>>”
要定义一种类型:std::pair<int, std::pair<size_t, int>>,在最右侧会同时出现两个结束符“>”。使用“>>”,MSC没问题,但GCC会报错。
std::pair中,如果有成员是class或struct,那该成员必须提供没有参数的构造函数
定义std::pair<map_location, unit>,当中map_location和unit不是struct就是class,那么它们都必须提没有参数的构造函数。一旦有一个不存在,MSC没问题,但GCC会报错。
实在不想为unit提供没参数构造函数,那只好使用std::pair<map_location, unit*>定义了。
设置函数参数默认值时,不能使用类似std::vector<size_t>()
声明函数,- gui::menu* get_ability_menu(hero_map& heros, std::vector<hero*>& partial_heros = std::vector<hero*>(), int selected = 0, bool checkbox = false, std::vector<size_t>& checked = std::vector<size_t>());
复制代码 以上声明在MSC没问题,但GCC会报错
要解决这个问题,可以先定义空vector变量。- std::vector<hero*> empty_vector_hero_ptr = std::vector<hero*>();
- std::vector<size_t> empty_vector_size_t = std::vector<size_t>();
- 把它们作为参数值在函数定义中使用
- gui::menu* get_ability_menu(hero_map& heros, std::vector<hero*>& partial_heros = empty_vector_hero_ptr, int selected = 0, bool checkbox = false, std::vector<size_t>& checked = empty_vector_size_t);
复制代码 在一些场合,不能省略std::string前的const- void wml_config_from_file(std::string& fname, config &cfg)
复制代码 以以下方式进行调用:- wml_config_from_file(game_config::path + "/xwml/" + BASENAME_DATA, game_config_);
复制代码 对于参数fname,它的值是game_config::path + "/xwml/" + BASENAME_DATA,注:game_config::path是std::string类型。
对以上的声明和调用,MSC没问题,但GCC有问题。——如果把fname声明函数中可变,game_config::path + "/xwml/" + BASENAME_DATA是该如何处理呢?
要解决这问题就是把fname严格定义为const std::string& fname。
不要把源文件编码为Unicode - Codepage 1200
MSC没问题,GCC会报N个错误。源文件包括*.h,*.hpp,*.c,*.cpp,……
作为解决方法,可换为Chinese Simplified (GB2312) - Codepage 936
希望多字节对齐的“char”指针不要使用静态分配
以下定义的变量可能不是4字节对齐的!至少iOS下不是。- unsigned char savegame_cache_[CONSTANT_1M];
复制代码 要申请出一块4字节对齐地址给一个unsigned char指针可使用malloc。32位系统下,malloc会确保出来的地址是8字节对齐,而64位下是8字节或16字节对齐。(malloc返回的是void*,它这地址需能满足可转换为“所有”元类型。)
std::make_pair中enum值到int须显示转换
- std::map<const std::string, int> tags;
- tags.insert(std::make_pair("attack", (int)ATTACK));
复制代码 tags的second是int,当中的“ATTACK”是enum,当要把“ATTACK”作为second加入时须执行显示转换,否则Android下的GCC会报错。- jni/../../../src/unit_types.cpp: In function 'void apply_to_tag::fill_tags()':
- jni/../../../src/unit_types.cpp:2160:45: error: no matching function for call to
- 'make_pair(char const [7], apply_to_tag::<anonymous enum>)'
- jni/../../../src/unit_types.cpp:2160:45: note: candidate is:
- c:/movie/android/android-ndk-r8d-windows/android-ndk-r8d/sources/cxx-stl/gnu-lib
- stdc++/4.6/include/bits/stl_pair.h:272:5: note: template<class _T1, class _T2> s
- td::pair<_T1, _T2> std::make_pair(_T1, _T2)
复制代码 要对STL容器使用erase时,不要使用const_iterator
LLVM下(Xcode 4.5.2集成)、GCC,erase std::multimap使用const_iterator编译时会报错,需改为使用iterator。
声明友员时须显示加上类型关键字,像class
- class default_map_generator : public map_generator
- {
- ......
- friend class gui2::tmap_generator;
- ......
- }
复制代码 如果不在gui2::tmap_generator前加上“class”,MSC、LLVM不会报错,但GCC会报以下错误。
for中删除std::map元素时不要使用it = std::map.erase(it)
MSC支持for中it = std::map.erase,GCC的则可能不支持。- for (std::map<...>::iterator it = m.begin(); it != m.end(); ) {
- it = m.erase(it);
- }
复制代码 以上代码会有平台问题,应使用下面格式。- for (std::map<...>::iterator it = m.begin(); it != m.end(); ) {
- m.erase(it ++);
- }
复制代码 其它容器,像std::vector,则两种都支持,而且网上有说“m.erase(it ++)”才是STL推荐使用的格式。但我实测下来,当是std::vector时,在vc2008下,m.erase(it ++)会出怪现象,就是一旦m.erase(it ++)后则一定会满足“it == m.end()”,导致程序达不到自个要求。
- 对非自动排序容器,像std::vector:使用it = m.erase(it)
- 对自动排序容器,像std::set、std::map:使用m.erase(it ++)
std::string中的back、pop_back方法
MSC使用的STL支持std::string::back获得最后一个字符,pop_back移除最后一个字符,但这两个方法,至少GCC经常使用的gnu_stl是都不支持。
尽量不要用snprintf
MSC的各版本对snprintf经常存在不一致。像vs2013使用_snprintf,到vs2015就用函数实现了snprintf。
含有对象的std::vector,不要在迭代中改变值
- struct titem {
- std::string s;
- };
复制代码
titem是个含有std::string对象的结构,变量items是以它为单元构建的std::vector,不要用类似以下的迭代改变值。
- for (std::vector<titem>::iterator it = items.begin(); it != items.end(); ++ it) {
- it.s = "hello";
- }
复制代码
原因是当改变对象值时,有可能会造成STL对items重分配内存。基于同样原因,重分配会影响到指向各单元的指针,因此尽量不要使用指向std::vector的某个单元的指针。
当items内容发生改变时,ptr有可能变得指向一个错误地址。
用{}格式初始化struct时,float到int要进行显示转换
- struct SDL_Point {
- int x, y;
- };
- float pi = 3.14;
- SDL_Point point{0, y * pi};
复制代码 给point赋值的语句用了{}格式,LLVM会报类似下错误。
修正方法是强制类型转换。
- SDL_Point point = {0, (int)(y * pi)};
复制代码
int被强制转到size_t,如何由size_t转回int?
64位平台时size_t是64位,32位平台时size_t是32位,而long的位置和size_t是一致的,int却总是32位。
如何把一整数设为特定类型
使用INT64_C、UINT64_C、INT32_C、UINT32_C宏,它们定义在stdint.h。
printf如何输出64位整数
- printf("%lld/n",a);
- printf("%llu/n",a);
复制代码
它们可同时用于Windows。
std::string是空时,c_str()是否会返回NULL/nullptr
切记不要把nullptr直接赋给std::string!- std::string s = nullptr;
- std::string s = NULL;
- 在windows,以上赋值就会造成app异常退出,其它操作系统未测试。
复制代码
std::string是空时,Windows、Android时不会。iOS未测试。即使nullptr时,"%s"打印时不会非法,而是显示"(null)"
- std::string str;
- SDL_Log("s1 c_str %s", str.c_str() != nullptr? "isn't null": "is null");
- SDL_Log("s1 c_str: %p, %s", str.c_str(), str.c_str());
- str = "abc";
- SDL_Log("s2 c_str %s", str.c_str() != nullptr? "isn't null": "is null");
- SDL_Log("s2 c_str: %p, %s", str.c_str(), str.c_str());
- str.clear();
- SDL_Log("s3 c_str %s", str.c_str() != nullptr? "isn't null": "is null");
- SDL_Log("s3 c_str: %p, %s", str.c_str(), str.c_str());
- const char* ptr = nullptr;
- SDL_Log("s4 ptr: %p, %s", ptr, ptr);
- ptr = "123";
- SDL_Log("s4 ptr: %p, %s", ptr, ptr);
- -------输出
- s1 c_str isn't null
- s1 c_str: 0x9ebff581,
- s2 c_str isn't null
- s2 c_str: 0x9ebff581, abc
- s3 c_str isn't null
- s3 c_str: 0x9ebff581,
- s4 ptr: 0, (null)
- s4 ptr: 110cf4c, 123
复制代码
多线程时需要同步写int变量
多线程时,如果可能同时写某个int变量,须要给这变量采取同步措施。举个例子,线程A对变量a先后执行a++、a--,线程B也先后执行a++、a--,那也会造成a出现错误结果。
同步措施像加互斥锁,对int/long变量,还有一种方法是使用操作系统提供的api,像winapi中的InterlockedIncrement、InterlockedDecrement,对包含了webrtc的,可使用当中的跨平台函数rtc::AtomicOps::Increment、rtc::AtomicOps: ecrement。 |
|