注意:以下解决方法不一定适用,仅个人记录备忘
关于VC和gcc的区别,请参考以下链接:
gccとVC(日文)
http://homepage1.nifty.com/herumi/prog/gcc-and-vc.html
(20131119)
超初心者のプログラム入門
http://www.eonet.ne.jp/~maeda/index.html
Problem 1: VC2008 运行时库
属性页->C/C++->代码生成->运行时库(一般是MTd和MT)
Problem 2: inline函数重定义问题,可以考虑用#if屏蔽掉
#if defined(_MSC_VER) && (_MSC_VER < 1500) //VC 9
inline int inet_pton(int af, register const char *cp, struct in_addr *addr)
Problem 3: libevent.lib 依赖问题。
如果使用VC2008,并且建立起libevent源代码工程。可以考虑使用“链接器->输入->附加依赖项”,这样就不再需要添加libevent.lib
Problem 4: errno赋值问题
errno = 0需要改为set_errno(0);
原因是vc2008的编译器在多线程时不允许你直接给errno赋值,貌似是为了线程安全,这时候就得写个宏。
- #if defined(_MT) || defined(_DLL)
- # define set_errno(x) (*_errno()) = (x)
- #else
- # define set_errno(x) errno = (x)
- #endif
不知道linux怎么处理线程安全(好像linux根本就不需要这么复杂)。
Problem 5:APR导入问题。
如果有一天突然想用APR或APU来编程,你就得遇到这种囧问题——不知道怎么在vc2008里链接。
一种方法是:
宏定义添加
APR_DECLARE_EXPORT(补充:或者APU_DECLARE_EXPORT)
WIN32
确保是动态导出api和动态导入APR DLL
附加依赖项目选择使用动态链接的lib(以防万一把apr和apu的都加上)
libapr-1.lib libaprutil-1.lib
如果不知道apr为何物,就最好别管这种复杂问题(Apache httpd的代码和API需要高手才看得懂的)
推荐用动态库(不会遇到一堆链接错误)如果用静态库要改用另一套lib和APR_DECLARE_STATIC
Problem 6: 函数声明和定义不同。
可能vc2008会对类型很敏感。最好改为统一。
Problem 7: snprintf问题
Windows SDK没有snprintf这个函数,用_snprintf代替
- #ifndef snprintf
- #define snprintf _snprintf
- #endif
Problem 8: strtoll和strtoull问题
- #if _MSC_VER < 1300
- #define strtoll(p, e, b) ((*(e) = (char*)(p) + (((b) == 10) ? strspn((p), "0123456789") : 0)), _atoi64(p))
- #else
- #define strtoll(p, e, b) _strtoi64(p, e, b)
- #endif
- #ifndef strtoull
- #define strtoull strtoul
- #endif
遇到这种问题需要查SDK的手册,你会发现有个叫_atoi64的API(这个函数很早就有了,用于32位操作系统上,定义在stdlib.h)。
当然如果你不喜欢上面代码写得那么复杂,可以一概改为_atoi64。另外,还需要知道有个关键字__int64,
可以用它表示有符号和无符号的64位整数(很少出现,除非你要用64位来提高性能)
_strtoi64的用法稍微不同。还有个叫_i64toa的API,是_atoi64的逆转换。
补充:可能对于64位整数,还要考虑格式化字符串printf的问题,所以问题远没有这么简单。
Problem 9:符号无定义。
比如你用了TransmitFile,你就得加mswsock.lib(不过好像加的顺序有讲究的)
如果不知道加什么,就查sdk帮助和搜索,一般微软会很热心地注明这个API需要用那个lib去
链接。把所有lib加上去试也可以(只是这种方法有点菜)
Problem 10:MySQL UDF问题
比较通用的方法是写个.def文件
LIBRARY "xxx"
EXPORTS
...
然后在工程中加入这个def
编译好dll后拷贝到
C:\Program Files (x86)\MySQL\MySQL Server 5.1\lib\plugin
或者根据show variables like "plugin_dir";判断插件位置
如果涉及别的dll,需要把依赖dll拷贝到PATH的路径下,例如
C:\Program Files (x86)\MySQL\MySQL Server 5.1\bin
否则mysqld会因为缺乏dll而启动不了UDF而认为UDF不存在。
创建SQL类似于CREATE FUNCTION xxx RETURNS INT SONAME "xxx.dll";
安装成功后select name, dl from mysql.func;查看
要调试UDF可以用fprintf(stderr,...)的方法,然后用mysqld --console来启动mysqld。
另外最好用debug版测试,万一崩溃了,mysqld会把输出一些堆栈信息到控制台上。
Problem 11: WSAStartup和memset问题
- #ifdef WIN32
- {
- WSADATA wsaData;
- if(WSAStartup(MAKEWORD(2,0), &wsaData) != 0) {
- fprintf(stderr, "Socket Initialization Error. Program aborted\n");
- return 0;
- }
- }
- #endif
这是windows的特有问题(写linux网络程序的人大概会很郁闷),
如果有一天发现getaddrinfo返回非零,
检查一下main函数有没有加WSAStartup。(连127.0.0.1都解析不了会很可笑)。
另外memset也是个需要注意的问题,例如初始化struct addrinfo变量hints需要
memset(&hints, 0, sizeof (hints));(有时候你忽略这个问题会出现一些古怪的结果)
然后给某个成员变量赋值。
linux的写法就不会那么复杂,它用C99的写法,在初始化时直接给变量的成员函数赋初始值。
顺便一提VC支持字符串的全0初始,例如char s[10] = {0};比单纯写char s[10];要安全得多。
更正:
VC其实可以像gcc那样给结构体对象赋初值,只不过不是写成 type a={.x = 10, };
而是写成type a={a.x = 10,};所以没必要用memset(低版本的VC则有必要?)
problem 12:缺少BSD套接字头文件
在linux网络编程中经常会出现
#include <netinet/tcp.h>
#include <arpa/inet.h>
有时还会有
#include <sys/socket.h>
还有其他,如果只是使用BSD套接字,
问题是,mingw没有(VC也是)。
但windows上的确存在BSD套接字API的子集,只不过需要包含的头文件不是上面那些,而是
- #include <winsock2.h>
- #include <ws2tcpip.h>
如果对这个问题感兴趣,可以参考这些资料:
* memcached1.2.6-win32移植的源代码
http://code.jellycan.com/memcached/
*PostgreSQL的源码(\src\include\port\win32),对win32的移植
http://www.postgresql.org/ftp/source/
(注:VC6似乎不行,待考)
problem 13:缺少sys/poll.h
win32没有现成的机制实现poll(轮询),也就是说:poll()不可移植
http://lists.zerezo.com/mingw-users/msg05987.html
不过事实上有人给出poll()的非正式实现:
(未经试验,不过好像可以试试看)
* libmemcached
/poll/poll.c
https://launchpad.net/libmemcached/+download
-------------------------
(2010-09-10:补充drizzle,发现它的源代码包中带有poll.c,不知道是否可用)
* drizzle
-------------------------
如果你使用cygwin,这个问题可以忽略(因为默认是有poll.h和poll()的实现)
(补充:
poll其实可以在msys的SDK中使用
msysDVLPR-1.0.0-alpha-1.tar.gz
但最终生成的exe导入了MSYS-1.0.DLL,而MSYS-1.0.DLL不可以单独使用(依赖于msys)
如果只用于msys控制台,可以尝试用这个SDK(gcc version 2.95.3-1)
安装教程:
http://www.mingw.org/wiki/HOWTO_Create_an_MSYS_Build_Environment
注意,它需要与msys目录合并,不是放在/mingw目录下
)
继续补充:
poll其实有win32的port,参考
WSAPoll
http://msdn.microsoft.com/en-us/library/ms741669(VS.85).aspx
不过这个API仅用于Vista以上
problem 14: pthread-w32静态库造成程序崩溃的问题。
由于dll.c中dllmain()调用了这两个API(定义在pthread.h)
PTW32_DLLPORT int PTW32_CDECL pthread_win32_process_attach_np(void);
PTW32_DLLPORT int PTW32_CDECL pthread_win32_process_detach_np(void);
如果pthread-w32被静态链接到程序中,需要在main()函数开头调用pthread_win32_process_attach_np初始化,否则程序会崩溃,且很难找到崩溃的原因(即使使用CDT调试)。
(2014/01/23补注:)代码如下:(在main函数最开始的地方执行)
- #ifdef __MINGW32__
- ptw32_processInitialize();
- //ptw32_processTerminate();
- #endif
problem 15: 关于VC6构造函数的初始化表的问题。
不可以使用带命名空间的基类。
例如class X:public std::Y{}
构造函数X():std::Y(0),...{}是编译出错的,
需要绕圈,用typedef std::Y std_Y,然后再初始化:X():std_Y(0),...{}
problem 16:关于VC6的模板参数和嵌套问题。
有些时候,如果没有用到模板参数,那么template可以省略,
但对于VC6来说,省略template<class T>有时就偏偏不行。
VC6似乎不能把嵌套template成员函数单独写再类定义外部。
例如template<class T> template<class C>...
一种解决办法是,放在类定义内部。
problem 17:VC6在成员函数中调用基类方法可能会编译失败。
原因不明,不过用某些方法绕过这种编译错误,例如使用未被覆盖或重载的方法代替(废话。。。)
problem 18: 使用winsock的API,在VC2008编译时出现大量的错误:
1>c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(91) : warning C4005: “AF_IPX”: 宏重定义
1> c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(460) : 参见“AF_IPX”的前一个定义
一般是因为出现这样的包含
#include <windows.h>
#include <winsock2.h>
导致一堆宏重定义
使用宏WIN32_LEAN_AND_MEAN可以屏蔽之(应该。。。)
用#define或者VC2008工程的预处理器设置均可。
problem 19: 线程局部变量的区别:
but msvc support like it with _declspec(thread).
however mingw32 can ignore that.
参见:https://github.com/mattn/mod_perlite/commit/4f811433266d90d33568a5680c1499946a3bd897
problem 20:asprintf和vasprintf
-----------------------
注意,这里有个开源实现(未试验)
http://www.ijs.si/software/snprintf/
-----------------------
出现在lua-checker的代码中
http://code.google.com/p/lua-checker/
这两个函数VC没有,需要用特殊方法近似地模拟,一种方法是使用vsnprintf(低版本的VC可能没有这个函数)
- #ifdef _MSC_VER
- #pragma warning(disable:4065)
- #pragma warning(disable:4996)
- /*
- 用malloc分配内存,在程序结束后自动回收
- see
- http://pdfrecompressor.googlecode.com/svn-history/r40/trunk/jbig2enc_modified/jbig2.cc
- */
- // -----------------------------------------------------------------------------
- // Windows, sadly, lacks asprintf
- // -----------------------------------------------------------------------------
- #include <stdarg.h>
- static int asprintf(char **strp, const char *fmt, ...)
- {
- va_list va;
- va_start(va, fmt);
- const int required = vsnprintf(NULL, 0, fmt, va);
- char *const buffer = (char *) malloc(required + 1);
- const int ret = vsnprintf(buffer, required + 1, fmt, va);
- *strp = buffer;
- va_end(va);
- return ret;
- }
- static int vasprintf(char **strp, const char *fmt, va_list va)
- {
- const int required = vsnprintf(NULL, 0, fmt, va);
- char *const buffer = (char *) malloc(required + 1);
- const int ret = vsnprintf(buffer, required + 1, fmt, va);
- *strp = buffer;
- return ret;
- }
- #endif
另一种做法是使用临时文件:
totem-plparser
https://github.com/zsx/totem-plparser
https://github.com/zsx/totem-plparser/blob/OAH/lib/asprintf.c
实现大概是这样:
- #include "config.h"
- #include <stdio.h>
- #include <stdlib.h>
- #include <stdarg.h>
- #ifdef _WIN32
- #include <windows.h>
- #endif
- int totem_private_asprintf(char **out, const char *fmt, ...)
- {
- va_list ap;
- int ret_status = EOF;
- char dir_name[2001];
- char file_name[2000];
- FILE *fp = NULL;
- char *work = NULL;
- va_start(ap, fmt);
- /* Warning: tmpfile() does not work well on Windows (MinGW)
- * if user does not have write access on the drive where
- * working dir is? */
- #ifdef _WIN32
- /* file_name = G_tempfile(); */
- GetTempPath ( 2000, dir_name );
- GetTempFileName ( dir_name, "asprintf", 0, file_name );
- fp = fopen ( file_name, "w+" );
- #else
- fp = tmpfile();
- #endif /* _WIN32 */
- if ( fp ) {
- int count;
- count = vfprintf(fp, fmt, ap);
- if (count >= 0) {
- work = calloc(count + 1, sizeof(char));
- if (work != NULL) {
- rewind(fp);
- ret_status = fread(work, sizeof(char), count, fp);
- if (ret_status != count) {
- ret_status = EOF;
- free(work);
- work = NULL;
- }
- }
- }
- fclose(fp);
- #ifdef _WIN32
- unlink ( file_name );
- #endif /* _WIN32 */
- }
- va_end(ap);
- *out = work;
- return ret_status;
- }
虽然麻烦,但在没有vsnprintf时是个不错的应急方案。
重申一下,这只是近似的方案,并不一定和真正的asprintf功能一样。
problem 21 获取指定文件名(可能是相对路径)的完整目录名
和问题20一样,出现在lua-checker的代码中
http://code.google.com/p/lua-checker/
我的移植方案是用VC的C运行时库函数_fullpath和_splitpath来模拟dirname:
- #ifndef _MSC_VER
- #include <libgen.h> // For dirname()
- #else
- #include <stdio.h>
- #include <conio.h>
- #include <stdlib.h>
- #include <direct.h>
- static const char *dirname(const char *partialPath)
- {
- static char full[1000] = {0};
- static char path_buffer[1000] = {0};
- static char drive[1000] = {0};
- static char dir[1000] = {0};
- static char fname[1000] = {0};
- static char ext[1000] = {0};
- static char fulldir[1000] = {0};
- if( _fullpath( full, partialPath, sizeof(full) ) != NULL )
- {
- _splitpath( full, drive, dir, fname, ext );
- strcpy(fulldir, drive);
- strcat(fulldir, dir);
- }
- else
- {
- strcpy(fulldir, "");
- }
- return fulldir;
- }
- #endif
problem 22 只能读文件4KB以内的内容?
使用fopen的"r"模式,一次性读取4KB内容到char[](使用fread函数),发现4KB以外的内容都是0(没有完整地读出)。
正确的做法是使用"rb“模式。
problem 23 VC2008的char类型的无符号问题
因为char在旧版的C中充当BYTE类型来使用(unsigned char),但新版的VC2008实施强类型,char类型将被当作有符号数来处理,如果想把char当作无符号来使用,应该更改VC工程的属性,添加/J参数。
problem 24 long long 输出
- #include <stdio.h>
- int main(void)
- {
- long long mem = 123456789012;
- printf("mem=%ld\n",mem);
- exit(0);
- }
- //输出不正确呀,应该用什么转移符?
- print("mem=%lld\n",men);
see http://blog.csdn.net/huangxb_csu/archive/2008/12/30/3648779.aspx
problem 25: 找不到strcasecmp
摘自
http://blog.csdn.net/chinacodec/archive/2010/01/03/5124977.aspx
- #ifdef _MSC_VER
- #include <string.h>
- #define strcasecmp stricmp
- #define strncasecmp strnicmp
- #endif
problem 26 : 使用SDL头文件时出现undefined reference to `WinMain@16'
原因是SDL.h把main宏定义了,可以用nm或dumpbin检查.o文件的符号是否有_main(可以用grep过滤)
解决办法可以取消main的宏定义:
- #undef main
- int main( int argc, char* argv[] ) {
补注: 正确做法应该是使用libSDLmain.a的WinMain入口,方法是把链接参数中的-lmingw32提前到-lSDLmain之前,例如
problem 27: drand48和srand48
Windows上可以简单地实现
- #ifdef _WIN32
- #define drand48() (((float) rand())/((float) RAND_MAX))
- #define srand48(x) (srand((x)))
- #endif
或参考http://blog.csdn.net/jimmyblind/archive/2010/05/02/5550042.aspx
注意,对随机数取模是个不好的设计,因为取模得到序列的可能不够均匀。
problem 28: bcb6的“Call to function 'xxx' with no prototype”警告
对于空参数表的函数声明,必须指明为void,例如xxx(void);
(后续,待修改)
problem 29: 在Windows上使用MinGW+SWIG书写JNI
Windows上的JNI实际上是Java虚拟机调用原生dll的导出函数。
尝试使用Cygwin+SWIG生成JNI,但不成功(提示aborted)
但使用mingw则没有问题(可能是cygwin的dll造成JNI不可用)
需要注意的是最后编译dll时使用的参数-Wl,--add-stdcall-alias,见
http://www.swig.org/tutorial.html
我尝试用手工Makefile编译JNI,编译工程是官方发布包中的swigwin-2.0.3\Examples\java\simple
方法如下:
1. 创建目录swigtest,把simple示例的代码都复制进去
2. 把jdk下的include文件(我的JDK头文件在
D:\java\jdk1.6.0_20\include
和
D:\java\jdk1.6.0_20\include\win32
)全部复制到目录swigtest(include\win32下的文件直接和include下的头文件放在一起)
这样做是因为,我在使用-I时无法指向这两个目录(原因不明)
3. 手工书写新的Makefile
(注意,我的javac在d:\java\jdk1.6.0_20\bin\javac.exe,所以指向mingw风格的目录/d/java/jdk1.6.0_20/bin/javac)
- CC := gcc
- LD := ld
- RM := rm -f
- SWIG := swig
- OBJS := example.o example_wrap.o
- CFLAGS := -I.
- # -I/D/java/jdk1.6.0_20/include -I/D/java/jdk1.6.0_20/include/win32
- # -I/cygdrive/d/java/jdk1.6.0_20/include
- JAVAC := /d/java/jdk1.6.0_20/bin/javac
- all : example.dll java
- java :
- ${JAVAC} *.java
- example.dll : ${OBJS}
- ${CC} -shared ${CFLAGS} -Wl,--add-stdcall-alias -o $@ ${OBJS}
- # -mno-cygwin
- %.o : %.c
- ${CC} ${CFLAGS} -o $@ -c $<
- # exampleJNI.java
- example_wrap.c : example.i
- ${SWIG} -o $@ -java $<
- clean :
- ${RM} *.o *.class *.dll example.java example_wrap.c exampleJNI.java
然后运行:
> make clean all
> java runme
problem 30 编译动态库和静态库
动态库:
gcc -shared -o <.dll文件> <.o文件>...
静态库:
ar rcs <.a文件> <.o文件> ...
(有时下面还可以对.a文件使用ranlib命令,但一般可以忽略不做)
problem 31 configure时输出.i文件检查宏展开后的编译错误
see
http://code.google.com/p/memcached/issues/detail?id=111
problem 32 isspace错误
解决办法如下
#define xisspace(c) isspace((unsigned char)c)
或者手工转换类型
see
https://github.com/lindner/memcached/blob/master/util.c
http://code.google.com/p/memcached/issues/detail?id=111
类似的情况出现在cygwin的大部分isXXX函数
problem 33 printf系输出的乱码问题
C 使用wprintf,_tprintf 打印简体中文的方法 【Locale.h】
http://www.cnblogs.com/niuniu502/archive/2009/02/17/1392636.html
需要分两种情况考虑:
1. 非Unicode版
_ftprintf(stderr, _T("%S\n"), "你好吗");
2. Unicode版
#include <locale.h>
setlocale(LC_ALL, "");
_ftprintf(stderr, _T("%s\n"), _T("你好吗"));
注意,两种情况的%s的大小写含义是不一样的!
这一点在微软的文档中有详细说明——
就是说printf的%s是非Unicode字符串的占位符;
而wprintf的%s是Unicode字符串的占位符;
printf的%S是Unicode字符串的占位符;
而wprintf的%S是非Unicode字符串的占位符;
wprintf还需要setlocale才能正常在控制台中输出。
另外,出现乱码还可能是因为——参数个数不匹配
- static long CPUClock __asm__("CPUClock") = 0;
- ...
- asm (
- ".intel_syntax noprefix\n\t"
- "RDTSC\n\t"
- "MOV [CPUClock], EAX\n\t"
- ".att_syntax\n"
- );
- Sleep(1000);
- asm (
- ".intel_syntax noprefix\n\t"
- "RDTSC\n\t"
- "SUB EAX, [CPUClock]\n\t"
- "MOV [CPUClock], EAX\n\t"
- ".att_syntax\n"
- );
- CPUClock /= 1000000;
- __asm
- {
- RDTSC
- MOV [CPUClock], EAX
- }
- Sleep( 1000 );
- __asm
- {
- RDTSC
- SUB EAX, [CPUClock]
- MOV [CPUClock], EAX
- }
- CPUClock /= 1000000;
- long CPUClock = 0;
-static-libstdc++在 g++ 或 ld 中加上这个参数, 就可以不用 libstdc++-6.dll
-static在 gcc/g++ 或 ld 中加上这个参数, 對所有的库都会采用静态链接的方式
problem 44:
getopt和getopt_long重入问题。
一般程序只能调用一次getopt或getopt_long,如果第二次调用,将无法获取参数信息。
解决办法是在调用之前对optreset和optind赋值为1(这两个变量声明在getopt.h中)
optreset = 1;
optind = 1;
problem 45:
fwrite返回值判断出错问题和原因
//代码只是大概意思,可能有误,仅参考用
if (fwrite(buffer, sizeof(unsigned char), nRead, file) != (size_t)(nRead)) {
fprintf(stderr, "error : %s .\n", strerror(errno));
}
如果出现不等于的情况(fwrite出错),与socket(网络套接字)的写入不同的是,不是因为写入数据不全,而是因为其它原因(例如,从strerror可以知道,因为磁盘空间不足),不要主观臆断。
另外,nRead似乎有范围限制(受限于size_t的大小,32位机器貌似最大为65536)
problem 46:
如果在模板参数中引用std标准库内的类型,必须加typename,否则编译可能会出错。
例如:假设vectorEx类继承std::vector,则
- template<class T> bool vectorEx<T>::erase(T t) {
- iterator it = begin();
要改写成:
- template<class T> bool vectorEx<T>::erase(T t) {
- typedef typename vectorEx<T>::iterator iterator;
- iterator it = vector<T>::begin();
而类内部的声明
- pair<iterator, bool> insert(T t);
(可能)需要改写成(否则iterator可能会报找不到定义的编译错误,
相当于写成
std::pair<typename vectorEx<T>::iterator, bool> insert(T t)
注意typename的位置:
- typedef typename vectorEx<T>::iterator iterator;
- typedef typename vectorEx<T>::const_iterator const_iterator;
- typedef typename vectorEx<T>::size_type size_type;
- pair<iterator, bool> insert(T t);
problem 47:链接exe时出现下划线符号强制链接成@符号
Use --enable-stdcall-fixup to disable these warnings
Use --disable-stdcall-fixup to disable these fixups
这个警告虽然会成功生成exe文件,但程序可能会崩溃。那是因为编译器不知道windows api函数Sleep是stdcall函数而非普通c函数,最好确保添加头文件以消除这个警告
- #define WIN32_LEAN_AND_MEAN
- #include <windows.h>
修改:
2014-01-28 修改problem 26
2014-01-23 添加problem 46、47
2013-05-30 添加problem 45
2013-05-25 添加problem 44
2013-04-18 添加problem 43
2012-07-04 添加problem 41, 42
2011-12-25 添加problem 38, 39, 40
2011-12-02 添加problem 37
2011-11-21 添加problem 36
2011-11-19 添加problem 35
2011-11-10 添加problem 34
2011-07-27 修改problem 20
2011-07-14 添加problem 33
2011-05-01 添加problem 31、32
2011-04-29 增加problem 29、30
2011-04-25 增加开头的参考链接
2011-04-23 补充problem 28
2011-04-10 补充problem 25、26、27
2011-03-27 补充problem 24
2011-02-01 补充problem 22、23
2011-01-25 补充problem 20、21
2010-12-21 补充problem 19
2010-12-07 补注WSAPoll的问题
2010-11-01 补充problem 18
2010-10-31 补充problem13:WSAPoll
2010-09-23 补充problem 15,16,17.来自于mysql++编译的移植问题。
2010-09-08 补充problem13中关于drizzle源码包中的poll实现
2010-09-01 补充problem13使用msysDVLPR的情况
2010-08-28 增加problem12, 13, 14
2010-08-14 修改了problem11,补充了problem7和problem5