VisualC++2010中初学者常见错误、警告和问题这部分将帮助⼤家解释⼀些常见的错误、警告和问题,帮助⼤家去理解和解决⼀些常见问题,并了解它的根本原因。
<iostream.h>与<iostream>
下⾯的代码为什么在VC2010下⾯编译不过去?
#include <iostream.h>
int main()
{
cout<<"Hello World."<<endl;
return 0;
}
错误信息:fatal error C1083:⽆法打开包括⽂件:“iostream.h”: No such file or directory
造成这个错误的原因在于历史原因,在过去C++98标准尚未订⽴的时候,C++的标准输⼊输出流确实是定义在这个⽂件⾥⾯的,这
是C风格的定义⽅法,随着C++98标准的确定,iostream.h已经被取消,⾄少在VC2010下⾯是这样的,取⽽代之的是我们要⽤头⽂件来代替,你甚⾄可以认为是这样定义的:
namespace std
{
#include "iostream.h"
}
因此我们可以简单的修改我们的Hello World。
#include <iostream>
using namespace std;
int main()
{
cout<<"Hello World."<<endl;
return 0;
}
iostream.h是属于C++的头⽂件,⽽⾮C的,因此标准订⽴的时候被改成了。⽽C的头⽂件stdio.h等依然可以继续使⽤,这是为了兼容C代码。但是它们依然有对应的C++版本,如等。记住,在VC2010上⾯采⽤C++风格的头⽂件⽽不是C风格的头⽂件,除⾮你是在⽤C。
warning C4996
这是⼀个警告,请看下⾯的代码:
#include <iostream>
using namespace std;
int main()
{
char sz[128] = {0};
strcpy( sz, "Hello World!" );
cout<< sz << endl;
return 0;
}
上⾯的strcpy会产⽣这个警告:
warning C4996: 'strcpy': This function or variable may be unsafe. Consider using strcpy_s instead. To disable deprecation, use
_CRT_SECURE_NO_WARNINGS. See online help for details.
这是因为VC从2005版本开始,微软引⼊了⼀系列的安全加强的函数来增强CRT(C运⾏时),这⾥对应的是strcpy_s。_s意为safe的意思,同样的道理,strcat也是同样。因此要解决这个问题,我们可以
⽤strcpy_s来替换strcpy,但是注意strcpy_s并⾮所有编译器都提供,因此如果要跨编译器,请采⽤错误信息中所提⽰的⽅式,定义_CRT_SECURE_NO_WARNINGS宏来掩⽿盗铃吧。另外注意并⾮所有的加强函数都是在屁股后⾯加_s,⽐如stricmp这个字符串⽐较函数的增强版名字是_stricmp。下⾯,⽤strcpy_s来更改程序:
int main()
{
char sz[128] = {0};
strcpy_s( sz, "Hello World!" );
cout<< sz << endl;
char* pSz2 = new char[128];
strcpy_s( pSz2, 128, "hello");
cout<< pSz2 << endl;
delete pSz2;
return 0;
}
注意,strcpy_s有两个版本,⼀个可以帮助我们⾃动推断缓冲区的⼤⼩,⽽另外⼀个不能帮助我们推断,因此在编译器不能推断缓冲区⼤⼩的时候,我们需要⾃⼰指定缓冲区的⼤⼩,如上⾯的程序所演⽰的那样,关于增强版的函数请参考我写的《深⼊学习C++ String2.1版》。 TCHAR、wchar_t、char
请⼤家看下⾯这个程序:
#include <iostream>
#include <Windows.h>
#include <tchar.h>
using namespace std;
int main()
{
MessageBox( NULL, "你好HelloWorld!", "Information", 0 );
return 0;
}
貌似没什么问题吧?错了,如果你是按照我教你的⽅法创建的控制台空⼯程的话,那么会有编译错误:
error C2664: “MessageBoxW”:不能将参数2从“const char [17]”转换为“LPCWSTR”
这个问题太普遍了,⼏乎所有的初学者都会遇到⽽且感到难以应付,因为按照提⽰使⽤(LPCWSTR)强制转型貌似并不能帮助我们解决问题,⽽且这个程序在VC6下⾯应该是没有任何问题的,那问题出现在哪⾥呢?问题在这⾥,请右键单击解决⽅案浏览器下⾯的项⽬,属性,
问题的根本就是字符集问题,在VC6中,我们默认使⽤的是多字节字符集,⽽现在我们默认需要的是UNICODE字符集,简单的,我们把这个字符集改成多字节字符集这个问题就解决了:
再试试应该就可以了吧?但是我并不推荐⼤家这么做,因为让⾃⼰的程序适应各种字符集是我们写代码的⼈义不容辞的义务。
我们把程序改成下⾯这样:
#include <iostream>
#include <Windows.h>
#include <tchar.h>
using namespace std;
int main()
{
MessageBox( NULL, TEXT("你好HelloWorld!"), TEXT("Information"), 0 );
MessageBox( NULL, _T("你好HelloWorld!"), _T("Information"), 0 );
return 0;
}
⽤两个宏TEXT或者_T都可以解决这个问题,它们两个并没有太⼤区别,也许区别在于前者是通过windows.h头⽂件引⼊的,⽽_T是通
过tchar.h引⼊的,我推荐⼤家使⽤_T和tchar.h,因为tchar.h还帮助我们引⼊了其它⼀些很有⽤的宏,⽐如_tcscpy_s,这个宏在使
⽤UNICODE字符集的时候被替换成wcscpy_s,在使⽤多字节字符集的使⽤被替换成strcpy_s。关于这部分的内容,请⼤家不要错过《Windows核⼼编程》的第⼆章(第四版或第五版都可以),以及《深⼊学习C++ String2.1版》。它们都有提到。
有⼈听说_T可以把多字节字符串转换成UNICODE,因此他写了如下的代码:
const char* pStr = "haha哈哈";
MessageBox( NULL, _T(pStr), _T("Information"), 0 );
当然,除⾮你运⽓好的抓狂,否则你是编译不过去的,为什么呢?我们现在应该知道对于"Hello"这样的字符串,VC2010会默认的将它视为const char*,即多字节字符串,⽽L"Hello"前⾯有个L前缀的被视为UNICODE字符串,这和C#是有区别的,因为C#的字符串总是被视
为UNICODE,C++/CLI下⾯编译器也会帮助我们做到这件事情,所以它们不需要L(C++/CLI兼容L这种写法)。
让我们看看_T的定义吧:
#define wxCONCAT_HELPER(text, line) text ## line
#ifndef _T
#if !wxUSE_UNICODE
#define _T(x) x
#else
#define _T(x) wxCONCAT_HELPER(L, x)
#endif
#endif
_T在UNICODE下⾯最终会被替换成L ## x。##是⼀个编译预处理指令,意味着让L和x贴在⼀起,⽐如L ## "Hello"最终就是L"Hello",因此它可以把"Hello"转换成UNICODE字符串。那为什么上⾯的程序不⾏呢?让我们看看_T("pStr")会被替换成什么:L ## pStr -> LpStr,哦,LpStr是⼀个新的标识符,如果你没有定义过它,你当然不能通过编译啦。
因此我们可以了解到_T这样的宏只能处理直接的常量字符串,不能处理其它的情况。⽽我们上⾯演⽰的那种情况需要我们动态的去转换编码,Windows有API可以帮助我们做到,C库也有函数可以帮助我们。恰好我曾经写过这样的代码,欢迎⼤家参
考:ASCII/UNICODE/UTF8字符串互相转换的C++代码
对于_T宏,再说⼀点东西,或许你会感到奇怪为什么_T不直接定义成#define _T(x) L ## x,⽽要绕个圈⼦去调
⽤wxCONCAT_HELPER呢?这实际上涉及到宏展开顺序和截断的问题。在这⾥,我们需要说⼀个宏参数的概念,这很函数的参数是类似的,这⾥_T(x)的x就是宏参数,好,记住下⾯⼀句话:
如果你定义的宏中使⽤了#或者是##的话,宏参数将不会被展开,也就是说_T(x)如果直接定义成L##x那么在下⾯这种情况就会出错( PS: #是给参数加引号的意思):
_T(__FUNCTION__),__FUNCTION__是⼀个预定义的宏,它代表了当前函数的名字,这个展开会是什么呢?L__FUNCTION__。为什么间接调⽤wxCONCAT_HELPER就能得到正确的结果呢?因为当我们调⽤wxCONCAT_HELPER的时候,__FUNCTION__已经被_T展开成了函数名。
说多了说多了,如果你觉得复杂可以暂时跳过这些东西,我只是顺便说说。
重定义的编译错误和链接错误
让我们在项⽬⾥⾯再添加⼀个Test.h头⽂件,⽅法是右击解决⽅案中的项⽬,添加,新建项,C++头⽂件,名称输⼊test.h。然后我们
在test.h中输⼊:
void print()
{
}
回到main.cpp中:
#include <iostream>
using namespace std;
#include "Test.h"
#include "Test.h"
int main()
{
return 0;
}
编译⼀下我们会得到重定义的编译错误:
error C2084:函数“void print(void)”已有主体
或许你会说,你引⽤(#include)了两次,我没你那么傻,我只引⽤⼀次不就好了么?是的。你聪明,但是是⼩聪明哈,因为你不能保证每个⼈都不去引⽤它。
这个问题演⽰的是#pragma once的⽤处,让我们解开它的注释。编译成功!#pragma once的作⽤就在于防⽌头⽂件被多次引⽤。你或许见过
#ifndef __TEST_H__
#define__TEST_H__
代码
#endif
这样的代码,它们的作⽤是⼀样的,如果你跟我⼀样懒,那么就⽤#pragma once,如果你打算去没有这个指令的编译器上编译代码,那么还是⽤后⾯⼀种⽅式吧。
现在让我们来见识⼀个对初学者稍微复杂⼀点的链接错误,⽤创建main.cpp的⽅法再添加⼀个test.h头⽂件,输⼊#include "Test.h"即可。
让我们再编译⼀次。
1>test.obj : error LNK2005: "void __cdecl print(void)" (?print@@YAXXZ)已经在Main.obj中定义
1>e:\documents\visual studio 2010\Projects\HelloWorld\ : fatal error LNK1169:到⼀个或多个多重定义的符号
如果说编译错误好的话,链接错误对于初学者来说就有点⿇烦了,聪明的初学者会去Google、百度寻答案,笨的初学者就会所谓的⾼⼿、前辈问,⽽这些⾼⼿Or前辈未必有⼼情为你解释。要解决这个错误有⽆数种⽅法。
1.内联,把print声明为内联函数。
inline void print()
{
}
这个⽅法的好处是简单,坏处是局限性太强,意味着你总是需要公开print的实现,因为内联函数必须在编译时就知道实现才⾏。
2.static,把print声明为static函数:
static void print()。
这便告诉编译器,哥是唯⼀的,⽽且哥只能被本编译单元的代码调⽤,这和extern是对应的。简单来说,想要哥帮你做事,请
先include哥声明的头⽂件,也就是#include "test.h"。
3..h头⽂件中只放声明,实现放到.cpp中去。
现在test.h中只有void print();,⽽实现在test.cpp中:
#include "Test.h"
void print()
{
int a = 1;
cout<< a++ << endl;
}
这个时候有意思的是我们在main.cpp⽆需包含test.h头⽂件也可以引⽤print函数,因为print并⾮static的函数:
void print();
int main()
{
print();
print();
return 0;
}
但是声明⼀下是必须的。
⼯程Setup发布:
1、制作安装⽂件的过程中,⼀般会出现⼏个warning,都是说某dll⽂件是系统⾃带的,不⽤加⼊安装包中之类的。建议把这些dll从你⽣成的filesystem中删除,否则有可能遇到系统版本问题。我遇到的相关具体问题是在win7下做的安装包到了xp下就⽆法安装和运⾏了。
2、关于快捷⽅式。我采⽤的⽅法是(不知道算不算⼀种猥琐的⽅法):先将你编译好的Realse⽂件夹下⾯的exe⽂件添加到你的file system⾥。然后右键file system⾥添加好的exe⽂件,会看到⽣成快捷⽅式的选项。⽣成快捷⽅式后,再将快捷⽅式剪切复制到user desktop等⽂件夹中去。
3、Logo。⾃⼰画,或者个bmp转ico⽂件的转换器吧。不过转换完之后的效果都不怎么理想,⽑刺挺多,需要进⼀步修改。
4、.最后,添加进来的依赖dll有⼀个选项,选择selfregister的话是可以在安装时刻⾃动注册的。另外,不要忘了将注册时需要⽤到的dll也包含进来。
5、.默认安装路径等,在setup⼯程的property⾥都可以调整。
主函数main
在VC++编程中,我随便选择了⼀个以前的程序准备编译,结果报错。⼀开始怀疑我⾃⼰的问题,于是写了⼀个最简单的程序,还是报错,如下:
好奇下,决定⽤微软⾃⼰的⽤例跑跑。于是新建了⼀个⼯程,并且选择Precompiled header,⽣成的⼯程如图:
这时恍然⼤悟,原来是⼊⼝函数有问题。正在做茅塞顿开状,⼀个学长提醒我到,从VS2005,微软定义的⼊⼝函数就是_tmain了,听了我还不相信,因为⾃⼰从来都是⽤main在VS2008中通过编译的。后来在VS2008中⽣成⼯程,果然如学长所说。正在郁闷中,学长⼜提醒我可以设置⼯程的预编译项,于是⾃⼰试了试,果然成功了,在改了以后的设置中,VS2010也可以跑通了。
⽅法如下:*代表当前⼯程名
project -> properties ->(选择Configuration Properties,这个时候在顶部的Configuration选择Active(Debug),
再到Configuration Properties中选择)C/C++ -> Precompiled Header -> Precompiled Header值改为Use(/Yu),OK了!
总结:VS2008及以前的配置中,默认都是选择了类似的配置,VS2008中是:Use Precompiled Header (/Yu)
中怎么写个简单的C++程序?
#include <iostream>
void main(void)
{
count<<"hi";
}
在vs2010中编写以上程序
报错:
2 IntelliSense: identifier "count" is undefined c:\users\shiechian\documents\visual studio 2010\projects\test\test\test.cpp 4 2 test
运⾏结果:
1>------ Build started: Project: test, Configuration: Debug Win32 ------
1> test.cpp
1>c:\users\shiechian\documents\visual studio 2010\projects\test\test\test.cpp(4): error C2065: 'count' : undeclared identifier
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
还出现这个
这是什么原因?
相关答复:
cout写错了,同时还没有添加名字std(标准c++⾥是需要的,vc6与标准不符,后来新版的vs都与标准⼀样了)
#include <iostream>
using namespace std;
void main(void)
{
cout<<"hi";
}
vs 2010中VC++的6个新特点
⼀、Visual C++⼯程及其构建系统
(⼀)MSBuild
如今,Visual C++处理⽅案和项⽬都运⽤MSBuild执⾏构建,从⽽取代了原来的构建⼯具。关于其他的Visual Studio⾔语和项⽬类型,MSBuild提供了⼀样的灵敏性,可扩展性和基于XML的构建⽀持⼯具。为此,Visual C++项⽬⽂件如今也运⽤了盛⾏的XML⽂件格式,并具有.vcxproj⽂件扩展名。另外,从前期版本的Visual Studio中提供的项⽬⽂件将被⾃动转换为新的⽂件格式。相关MSBuild⼯具的更多信息,请参见⽂章“MSBuild(Visual C++)”。
(⼆)VC++⽬录
如今,VC++⽬录配置位于两个地点。你可以运⽤项⽬属性页来配置每个项⽬对应的VC++⽬录,也可以够运⽤属性维护器和⼀个属性表来配置全局性的并且使每个配置值对应的VC++⽬录。
(三)⼯程依托性text函数什么意思
在VC++前期版本中,您可以定义存储在同⼀个处理⽅案中的项⽬之间的依托性。如今在这些处理⽅案转换为新的项⽬⽂件格式后,相应的依托性也被转换为项⽬到项⽬的援⽤。这种改变将会影响使⽤顺序,由于处理⽅案依托性和项⽬到项⽬的援⽤是不⼀样的。
(四)宏与环境变量
新引⼊的_ITERATOR_DEBUG_LEVEL宏⽀持针对迭代器调⽤调试⽀持。你可以运⽤这个宏来替代较⽼
的_SECURE_SCL和_HAS_ITERATOR_DEBUGGING宏。
⼆、Visual C++库
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论