编写无警告的代码

本文内容版权归原作者所有,我不对本文内容的真实性做任何保证。
原文地址:编写无警告的代码

今天把项目的Qt版本从Qt4.6.3升级到Qt4.8.4,重新编译项目代码的时候,特别关注了一下编译器的警告。于是找到《C++编程规范——101条规则、准则与最佳实践》翻了翻,重温了一下第1条 在高级别警告干净利落地进行编译。
如果编译器对某个构造发出警告,一般表明代码中存有潜在的问题。警告就好比代码的“肿瘤”,可能是良性的也可能恶性的——作为代码医生的我们不能对其视而不见。必须“把它弄清楚”,然后通过“改写代码以排除警告”!
典型的编译器警告示例:
1.”Unused function parameter”(未使用的函数参数):
编译器会对没有使用到的函数参数报告警告。如果确实不需要,那直接删除函数参数名就行了。
2. “Variable defined but never used”(定义了从未使用过的变量):
如果确实不需要,可以直接将之删除。 如果该变量的实例化是为了启动某项服务,则经常可以通过插入一个变量本身的求值表达式,使编译器不再报警。(这种求值不会影响运行时的速度)
3. “Variable may be used without being initialized”(变量使用前可能未经初始化):
这种警告通常意味着你的代码存在问题,请慎重处理之。
4. “Missing return”(遗漏了return语句)。:
这可能是一种好现象。即使你认为控制永远都不会运行到结尾或某个分支,则应该加上执行assert(false ) 的语句。
像下面这样(兼有注释作用的断言):

  default:  
     assert( !"should never get here!" );  // !"string" 的求值结果为false  
  
  1. “signed/unsigned mismatch”(有符号数/无符号数不匹配):
    通常需要插入一个显式的强制转换。
  2. “warning: type qualifiers ignored on function return type”(忽略了函数返回类型的类型限定符):
    通常是因为在函数前面的返回值面前添加了多余的 const 限定符。
    例如,const int getAge() const { return m_age; } ,最前面的const 是多余的,编译器会报告警告。
  3. “warning: deprecated conversion from string constant to ‘char*'”(不推荐的转化用法):
    函数原型:void setName(char *name);

    函数调用:setName(“personName”); // 编译器报告警告

释义:这是因为 char * 是一个指针,其背后的含义是:给我一个字符串,我可以修改它。

而如果我们传给函数一个“字符串常量”,这应该是没法被修改的。所以说,比较合理的办法是把参数类型修改为const char * ,而这个类型背后的含义是:给我一个字符串,我只要读取它(const意味着只读)。
8. “warning: “MAX_PATH” redefined” (重复定义) :

  #ifndef MAX_PATH
  #define MAX_PATH(260)
  #endif
  
  1. 第三方头文件。

  2. “auto-importing has been activated without –enable-auto-import specified on the command line”

【教程】关于C#中调用Cpp编写的dll

dll文件
dll文件用的图片

前几天写了一篇文章,讲了如何建立dll以及如何调用dll,文章中使用的是C/C++,今天就说说怎么使用C#调用C++的dll。


首先建立一个C#工程。然后再原文件夹中的Debug文件夹中放入我们做好的dll,这里我们使用前一篇文章中建立的dll:“basic_dll.dll”,然后就是写代码。

在C#中如果要调用dll,就要using一个空间:System.Runtime.InteropServices,包含进来就好了。


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;

这里包含进了一些无关的东西,不用管它,我一建立工程就有这些了,懒得删-_-……

如果要载入dll,就要在声明方法之前加上


[DllImport("basic_dll.dll", EntryPoint = "add",CallingConvention = CallingConvention.Cdecl)]

其中,“basic_dll.dll”是我们要引入的dll的名字,要么它在程序的同一目录下,要么在系统中已经注册过,后面的“EntryPoint”是dll的入口函数,也就是我们要导入的函数名称,再后面的CallingConvention是调用约定,如果不加上这个调用约定的话,会导致编译时出现”对 PInvoke 函数“xxFunction()”的调用导致堆栈不对称。原因可能是托管的 PInvoke 签名与非托管的目标签名不匹配。请检查 PInvoke 签名的调用约定和参数与非托管的目标签名是否匹配“这样的错误,原因一会说。

然后的然后就是声明一下我们的函数了,在这里同样只有一个add(int, int)   -_-

一下是一段简单到要死的源代码


namespace Csharp调用
{

class Program
{
[DllImport("basic_dll.dll", EntryPoint = "add",CallingConvention = CallingConvention.Cdecl)]
public static extern int add(int a, int b);

static void Main(string[] args)
{
Console.WriteLine(add(3, 5));
Console.ReadKey();
}
}
}

这段代码极其简单以至于我就跳过解读了,然后说说关于刚才提到的堆栈不对称的问题。

众所周知,在c++WIN32程序中有三种calling convention(调用约定):__cdecl, __stdcall, __fastcall,默认为__cdecl。而c#中默认为CallingConvention =CallingConvention.Winapi,如果不对调用约定进行说明的话,会出现调用约定不一致的错误。当然,在dll中,要传出的方法应该放在extern ”C“中,不然会因为C++更改函数名而导致调用错误。

【教程】dll动态链接库学习第一课:使用C++创建并调用dll

很长时间以来都总想着要写技术文章,今天就先了了这个想法,写一篇关于动态链接库学习的感想。

关于dll动态链接库的介绍我就不说了,百度上360百科上写的很详细。之前看过《Windows核心编程(第五版)》,但是无奈里面写的有些晦涩。


 

废话少说,先说说怎么写一个dll。

先打开vs或者vc,新建一个Win32项目(具体的方法:新建项目->Visual C++ -> Win32 ->Win32项目),然后名称和位置填上(注意名称中最好只包括英文符号,不然极有可能出现错误)。我新建的名字是basic_dll

然后在弹出的框中点下一步,然后在“应用程序类型”中选择“DLL”,在“附加选项”中,勾上“导出符号”,完成即可。

然后剩下的事情就是编写你的文件了。在这里我只在里面放一个

int add(int, int);

相信大家看到名字也就能猜出来他是干什么的了!没错,就是返回两个int的和。具体怎么做呢?

在“basic.h”(<工程名字>就是你刚刚填写的名称,一下均遵从次约定)的最后,添加上这样一句话:

BASCI_DLL_API int add(int, int);

这句话中,BASIC_DLL_API的定义参见上面的#ifdef块。然后打开basic_dll.cpp,在里面添加定义

BASIC_DLL_API int add(int a, int b)

{

int ans = a + b;

return ans;

}

然后我们似乎大功告成啦!先生成……

啊嘞?怎么提示“无法启动程序”?

如果看到这个的话,你一定是点了调试(按F5或者是那个绿色的三角),别担心。首先,dll本身是不能独立运行的,其次,我只让你生成,没让你调试啊!!!给我好好看着!

咳咳……当你看到“1成功”的时候,就说明已经生成了你要的dll。此时还会生成一个lib文件。

然后到你的文件目录中去寻找 basic_dll.dll、basic_dll.lib、basic_dll.h 这三个文件,一会我们要用。


 

然后咱们再说说怎么用这个dll和这个lib。

首先你得新建一个控制台程序吧~步骤我就不细说了。这里假定我们新建的工程名称是“basic_exe”。

然后是准备工作。把上面的三个文件,拷贝到你的工程目录中的basic_exe文件夹中,也就是说如果你的工程文件夹是“F:\basic_exe”,那么你应该放在“F:\basic_exe\basic_exe”中,然后再把basic_dll.dll放在“F:\basic_dll\Debug”中一份,以免一会运行的时候提示找不到dll。

然后开始写我们的程序啦。先静态调用,也就是调用lib。

先把刚才的“basic_dll.h”包含进来,当然在这里要在工程中引入这个文件了~

然后下面我们开始编写代码

#include <iostream>

#include "basic_dll.h"

using namespace std;

int main()

{

int a = 1;

int b = 2;

int ans = add(a, b);

cout << ans << endl;

return 0;

}

然后调试……

咦?怎么有一个错误?1个无法解析的外部命令?这是什么鬼?

不用担心,这是因为我们虽然告诉了编译器有一个头文件,但是没有告诉它库文件.lib在哪里啊~我们只需要把它包含进来即可。项目属性-》配置属性-》连接器-》输入-》在右边的附加依赖项中加入你的lib的地址就可以了。

但是似乎还有?

这是因为—–我们没有加上extern “C”。

我们打开刚才写的dll,然后在basic_dll.h中的函数add外面,加上extern,也就是变成这样


extern "C"
{
BASIC_DLL_API int add(int a, int b);
}

然后就OK了~

然后再调试……OK!


然后再说说动态调用dll的。

先包含进库的说


#include <Windows.h>

#include <iostream>

#include "basic_dll.h"

当然这里你也要把basic_dll.h包含进工程

然后是正文。


using namespace std;

typedef int(*MYPROC) (int, int)

int CallDll()

{

HINSTANCE hinst;

MYPROC myproc;

hinst = LoadLibrary(L"basic_dll.dll");

if (hinst != NULL)

{

myproc = (MYPROC)GetProcAddress(hinst, "add");

int a = 0;

a = myproc(3, 4);

cout << "3 + 4 =" << a << endl;

FreeLibrary(hinst);

}

else

{

cout << "加载dll失败,因为hinst空" << endl;

}

return 0;

}

int _tmain(int argc, _TCHAR* argv[])

{

CallDll();

return 0;

}

大功告成!调试……

然后我就不说了~大概就到这里,千里之行属于足下,这是dll学习的第一课~