C#与C/C++的交互zz

C#与C++交互,总体来说可以有两种方法:

  • 利用C++/CLI作为代理中间层

  • 利用PInvoke实现直接调用

第一种方法:实现起来比较简单直观,并且可以实现C#调用C++所写的类,但是问题是MONO构架不支持C++/CLI功能,因此无法实现脱离Microsoft .NET Framework跨平台运行。

第二种方法:简单的实现并不麻烦,只要添加DllImportAttribute特性即可以导入C/C++的函数,但是问题是PInvoke不能简单的实现对C++类的调用。在Warensoft3D中为了可以使用MONO实现跨平台(当然DirectX是不能跨平台的),所以使用了本方法,下面将对本方法展开详细的说明。

测试平台:

Windows7 64位,VS2010,.NET4.0

注意事项:

PInvoke从功能上来说,只支持函数调用,在被导出的函数前面一定要添加extern "C"来指明导出函数的时候使用C语言方式编译和连接,这样保证函数定义的名字和导出的名字相同,否则如果默认按C++方式导出,那个函数的名字就会变得乱七八糟,我们的程序就无法找到入口点了。

本文将说明以下几点:

  • 互调的基本原理

  • 基本数据类型的传递

  • 指针的传递

  • 函数指针的传递

  • 结构体的传递

  1. 互调的基本原理

    首先,我们来看一个再常规不过的概念—"数据类型"

    我们知道在大多数的静态语言中定义变量的时候都要先指定其数据类型,所谓数据类型,都是人们强加的一个便于记忆的名称,究其本质就是指明了这个数据在内存里到底是占用了几个字节,程序在运行的时候,首先找到这个数据的地址,然后再按着该类型的长度,读取相对应的内存,然后再处理。

    了解了前面这个事儿,所有编程语言之间进行互调就有点门道儿了。对于不同语言之间的互调,只要将该数据的指针(内存地址)传递给另一个语言,在另一个语言中根据通信协议将指针所指向的数据存储入长度对应的数据类型即可,当然要满足以下几点:

    1. 对于像Java,.NET这样有运行时虚拟机编程语言来讲,由于虚拟机会让堆内存来回转移,因此,在进行互调的时候,要保证正在被互调的数据所在的内存一定要固定,不能被转移。

    2. 有一些编程语言支持指针,有一些语言不支持指针(如Java),这个问题并不重要,所谓指针,其实就是一个内存地址,对于32位OS的指针是一个32位整数,而对于64位机OS的指针是一个64位整数。因为大多数语言中都有整型数,所以可以利用整型来接收指针。

  2. 基本数据类型的传递

互调过程中,最基本要传递的无非是数值和字符,即:int,long,float,char等等,但是此类型非彼类型,C/C++与C#中有一些数据类型长度是不一样的,下表中列出常见数据类型的异同:

C/C++

C#

长度

short

short

2Bytes

int

int

4Bytes

long(该类型在传递的时候常常会弄混)

int

4Bytes

bool

bool

1Byte

char(Ascii码字符)

byte

1Byte

wchar_t(Unicode字符,该类型与C#中的Char兼容)

char

2Bytes

float

float

4Bytes

double

double

8Bytes

最容易弄混的是就是long,char两个类型,在C/C++中long和int都是4个字节,都对应着C#中的int类型,而C/C++中的char类型占一个字节,用来表示一个ASCII码字符,在C#中能够表示一个字节的是byte类型。与C#中char类型对应的应该是C/C++中的wchar_t类型,对应的是一个2字节的Unicode字符。

下面通过实例来说明调用过程:

第一步:

建立一个C++的Win32DLL,如下图所示:

这里要注意选择"Export symbols"导出符号。点击完成。

第二步:

由于项目的名称是"TestCPPDLL",因此,会自动生成TestCPPDLL.h和TestCPPDLL.cpp两个文件,.h文件是要导出内容的声明文件,为了能清楚的说明问题,我们将TestCPPDLL.h和TestCPPDLL.cpp两个文件中的所有内容都删除,然后在TestCPPDLL.h中添加如下内容:

第一行代码中定义了一个名为"TESTCPPDLL_API"的宏,该宏对应的内容是"__declspec(dllexport)"意思是将后面修饰的内容定义为DLL中要导出的内容。当然你也可以不使用这个宏,可以直接将"__declspec(dllexport)"写在要导出的函数前面。

第二行中的"EXTERN_C",是在"winnt.h"中定义的宏,在函数前面添加"EXTERN_C"等同于在函数前面添加extern "C",意思是该函数在编译和连接时使用C语言的方式,以保证函数名字不变。

第二行的代码是一个函数的声明,说明该函数可以被模块外部调用,其定义实现在TestCPPDLL.cpp中,TestCPPDLL.cpp的代码如下所示:

第三步:

在编译C++DLL之前,需要做以下配置,在项目属性对话框中选择"C/C++"|"Advanced",将Compile AS 选项的值改为"C++"。然后确定,并编译。

生成的DLL文件如下图所示:

第四步:

首先,添加一个C#的应用程序,如果要在C#中调用C++的DLL文件,先要在C#的类中添加一个静态方法,并且使用DllImportAttribute对该方法进行修饰,代码如下所示:

DllImport中的第一个参数是指明DLL文件的位置,第二个参数"EntryPoint"用来指明对应的C/C++中的函数名称是什么。"extern"关键字表明该处声明的这个Add方法是一个外部调用。

该方法声明完毕之后,就可以像调用一个普通的静态方法一样去使用了。

下面是示例程序:

class Program

{

[DllImport(@"E:\ex\TestCPPDLL\Debug\TestCPPDLL.dll", EntryPoint = "Add")]

extern static int Add(int a, int b);

static void Main(string[] args)

{

int c = Add(1,2);

Console.WriteLine(c);

Console.Read();

}

}

在运行C#程序之前,先要修改C#的项目属性,如下图所示:

将platform target设置为x86,并且允许非安全代码(后面有用)。

然后运行该C#程序,其结果如下图所示:

第五步:

前面的Add方法中传递的是数值类型(int),其他的数据类型,如float,double,和bool类型的传递方式是一样的,下面演示如何传递字符串。

在TestCPPDLL.h中添加一个新的函数声明,代码如下:

EXTERN_C TESTCPPDLL_API void __stdcall WriteString(wchar_t*content);

这里的参数是wchar_t类型的指针,对应着C#中的char类型。TestCPPDLL.cpp中添加如下代码:

TESTCPPDLL_API void __stdcall WriteString(wchar_t*content)

{

    cout<<content;

}

该代码的功能就是将输入的字符串通过C++在控制台上输出。下面是在C#中的声明:

[DllImport(@"E:\ex\TestCPPDLL\Debug\TestCPPDLL.dll", EntryPoint = "WriteString")]

extern unsafe static void WriteString(char*c);

调用过程如下所示:

//因为使用指针,因为要声明非安全域

unsafe

{

//在传递字符串时,将字符所在的内存固化,

//并取出字符数组的指针

fixed (char* p = &("hello".ToCharArray()[0]))

{

//调用方法

WriteString(p);

}

}

其运行效果如下图所示:

3. 指针的传递

根据前面介绍的数据类型对照表,我们可以直接在方法中传递指针,但是要注意的是我们常常需要将数组的指针(数据入口地址,第一个元素的地址),数据从C/C++到C#时问题不大,但是如果从C#到C/C++时一定要将数组先固化,然后再传递处理。

下面演示如何传递指针,首先在TestCPPDLL.h中添加下列声明:

//传入一个整型指针,将其所指向的内容加1

EXTERN_C TESTCPPDLL_API void __stdcall AddInt(int *i);

//传入一个整型数组的指针以及数组长度,遍历每一个元素并且输出

EXTERN_C TESTCPPDLL_API void __stdcall AddIntArray(int *firstElement,int arraylength);

//在C++中生成一个整型数组,并且数组指针返回给C#

EXTERN_C TESTCPPDLL_API int* __stdcall GetArrayFromCPP();

其实现写在TestCPPDLL.cpp中,代码如下所示:

TESTCPPDLL_API void __stdcall AddInt(int *i)

{

    (*i)++;

}

TESTCPPDLL_API void __stdcall AddIntArray(int *firstElement,int arrayLength)

{

int*currentPointer=firstElement;

for (int i = 0; i < arrayLength; i++)

    {

        cout<<*currentPointer;

        currentPointer++;

    }

    cout<<endl;

}

int *arrPtr;

TESTCPPDLL_API int* __stdcall GetArrayFromCPP()

{

    arrPtr=new int[10];

for (int i = 0; i < 10; i++)

    {

        arrPtr[i]=i;

    }

return arrPtr;

}

对应调用的C#代码如下所示:

[DllImport(@"E:\ex\TestCPPDLL\Debug\TestCPPDLL.dll", EntryPoint = "AddInt")]

extern unsafe static void AddInt(int* i);

[DllImport(@"E:\ex\TestCPPDLL\Debug\TestCPPDLL.dll", EntryPoint = "AddIntArray")]

extern unsafe static void AddIntArray(int* firstElement, int arraylength);

[DllImport(@"E:\ex\TestCPPDLL\Debug\TestCPPDLL.dll", EntryPoint = "GetArrayFromCPP")]

extern unsafe static int* GetArrayFromCPP();

调用过程如下所示:

unsafe

{

// 调用C++中的AddInt方法

int i = 10;

AddInt(&i);

Console.WriteLine(i);

//调用C++中的AddIntArray方法将C#中的数据传递到C++中,并在C++中输出

int[] CSArray = new int[10];

for (int iArr = 0; iArr < 10; iArr++)

{

CSArray[iArr] = iArr;

}

fixed (int* pCSArray = &CSArray[0])

{

AddIntArray(pCSArray, 10);

}

//调用C++中的GetArrayFromCPP方法获取一个C++中建立的数组

int* pArrayPointer = null;

pArrayPointer = GetArrayFromCPP();

for (int iArr = 0; iArr < 10; iArr++)

{

Console.WriteLine(*pArrayPointer);

pArrayPointer++;

}

}

4. 函数指针的传递

前面说明的都是简单数据类型的及其指针的传递,利用PInvoke我们也可以实现函数指针的传递,C#中并没有函数指针的概念,但是可以使用委托(delegate)来代替函数指针,关于C#中委托的说明,可以参考笔者前面的一个文章:《C#委托及事件》

大家可能会问,为什么要传递函数指针呢?利用PInvoke可以实现C#对C/C++函数的调用,反过来,我们能不能在C/C++程序运行的某一时刻,来调用一个C#对应的函数呢?(例如在C++中存在一个独立线程,该线程可能在任意时刻触发一个事件,并且需要通知C#)。这个时候,我们就有必要将一个C#中已经指向某一个函数的函数指针(委托)传递给C++。

想要传递函数指针,首先要在C#中定义一个委托,并且在C++中定义一个函数指针,同时要保证委托和函数指针具备相同的函数原型,我们首先编写C#的代码,如下所示:

//定义一个委托,返回值为空,存在一个整型参数

public delegate void CSCallback(int tick);

//定义一个用于回调的方法,与前面定义的委托的原型一样

//该方法会被C++所调用

static void CSCallbackFunction(int tick)

{

Console.WriteLine(tick.ToString ());

}

//定义一个委托类型的实例,

//在主程序中该委托实例将指向前面定义的CSCallbackFunction方法

static CSCallback callback;

在CS的主程序中让callback指向CSCallbackFunction方法,代码如下所示:

//调用委托所指向的方法

callback = CSCallbackFunction;

然后在C/C++中定义一个函数指针,并且添加一个用于设置函数指针的函数,TestCPPDLL.h中的代码如下所示:

//定义一个函数指针

typedef void (__stdcall *CPPCallback)(int tick);

//定义一个用于设置函数指针的方法,

//并在该函数中调用C#中传递过来的委托

EXTERN_C TESTCPPDLL_API void SetCallback(CPPCallback callback);

SetCallback函数的实现在TestCPPDLL.cpp中,代码如下所示:

TESTCPPDLL_API void SetCallback(CPPCallback callback)

{

int tick=rand();

//下面的代码是对C#中委托进行调用

callback(tick);

}

在C#中添加SetCallback函数的声明,代码如下所示:

//这里使用CSCallback委托类型来兼容C++里的CPPCallback函数指针

[DllImport(@"E:\ex\TestCPPDLL\Debug\TestCPPDLL.dll", EntryPoint = "SetCallback")]

extern static void SetCallback(CSCallback callback);

在C#中的调用过程如下所示:

//让委托指向将被回调的方法

callback = CSCallbackFunction;

//将委托传递给C++

SetCallback(callback);

SetCallback方法被执行后,在C#中定义的CSCallbackFunction就会被C++所调用。

5. 结构体的传递

传递结构体的想法和传递一个int类型数据类似,struct中的数据是在内存中顺序排列的,只要保证保证以下几点,就可以直接传递结构体,甚至是结构体的指针:

  • 要传递的成员为公有的值类型字段

  • C#中结构体字段类型与C++结构体中的字段类型相兼容

  • C#结构中的字段顺序与C++结构体中的字段顺序相同,要保证该功能,需要将C#结构体标记为[StructLayout( LayoutKind.Sequential)]

下面通过代码进行说明,首先在C#中添加一个结构体,代码如下所示:

[StructLayout( LayoutKind.Sequential)]

struct Vector3

{

public float X, Y, Z;

}

该结构体表示一个3D向量,包括X,Y,Z三个float类型的分量。

然后在TestCPPDLL.h中也定义一个相同结构的结构体,代码如下所示:

struct Vector3

{

float X,Y,Z;

};

在TestCPPDLL.h中声明一个用于传递Vector3结构体的一个函数,代码如下所示:

EXTERN_C TESTCPPDLL_API void __stdcall SendStructFromCSToCPP(Vector3 vector);

在TestCPPDLL.cpp中将其实现,代码如下所示:

TESTCPPDLL_API void __stdcall SendStructFromCSToCPP(Vector3 vector)

{

    cout<<"got vector3 in cpp,x:";

    cout<<vector.X;

    cout<<",Y:";

    cout<<vector.Y;

    cout<<",Z:";

    cout<<vector.Z;

}

在C#中添加对SendStructFromCSToCPP函数的声明,代码如下所示:

[DllImport(@"E:\ex\TestCPPDLL\Debug\TestCPPDLL.dll", EntryPoint = "SendStructFromCSToCPP")]

extern static void SendStructFromCSToCPP(Vector3 vector);

C#中的调用过程如下所示:

//建立一个Vector3的实例

Vector3 vector = new Vector3() { X =10,Y=20,Z=30 };

//将vector传递给C++并在C++中输出

SendStructFromCSToCPP(vector);

基输出效果如下所示:

完整的TestCPPDLL.h代码如下所示:

复制代码

#define TESTCPPDLL_API __declspec(dllexport)

EXTERN_C TESTCPPDLL_API int __stdcall Add(int a,int b);

EXTERN_C TESTCPPDLL_API void __stdcall WriteString(wchar_t*content);

//传入一个整型指针,将其所指向的内容加1

EXTERN_C TESTCPPDLL_API void __stdcall AddInt(int *i);

//传入一个整型数组的指针以及数组长度,遍历每一个元素并且输出

EXTERN_C TESTCPPDLL_API void __stdcall AddIntArray(int *firstElement,int arraylength);

//在C++中生成一个整型数组,并且数组指针返回给C#

EXTERN_C TESTCPPDLL_API int* __stdcall GetArrayFromCPP();



//定义一个函数指针

typedef void (__stdcall *CPPCallback)(int tick);

//定义一个用于设置函数指针的方法,

//并在该函数中调用C#中传递过来的委托

EXTERN_C TESTCPPDLL_API void __stdcall SetCallback(CPPCallback callback);



struct Vector3

{

float X,Y,Z;

};

EXTERN_C TESTCPPDLL_API void __stdcall SendStructFromCSToCPP(Vector3 vector);

复制代码

完整的TestCPPDLL.CPP代码如下所示:

复制代码

#include "stdafx.h"

#include <iostream>

#include "TestCPPDLL.h"

using namespace std;

TESTCPPDLL_API int __stdcall Add(int a,int b)

{

return a+b;

}

TESTCPPDLL_API void __stdcall WriteString(wchar_t*content)

{

wprintf(content);

printf("\n");

}



TESTCPPDLL_API void __stdcall AddInt(int *i)

{

(*i)++;

}



TESTCPPDLL_API void __stdcall AddIntArray(int *firstElement,int arrayLength)

{

int*currentPointer=firstElement;

for (int i = 0; i < arrayLength; i++)

{

cout<<*currentPointer;

currentPointer++;

}

cout<<endl;

}

int *arrPtr;

TESTCPPDLL_API int* __stdcall GetArrayFromCPP()

{

arrPtr=new int[10];



for (int i = 0; i < 10; i++)

{

arrPtr[i]=i;

}



return arrPtr;

}



TESTCPPDLL_API void __stdcall SetCallback(CPPCallback callback)

{

int tick=100;

//下面的代码是对C#中委托进行调用

callback(tick);

}



TESTCPPDLL_API void __stdcall SendStructFromCSToCPP(Vector3 vector)

{

cout<<"got vector3 in cpp,x:";

cout<<vector.X;

cout<<",Y:";

cout<<vector.Y;

cout<<",Z:";

cout<<vector.Z;

}

复制代码

完整的C#代码如下所示:

复制代码

using System;

using System.Collections.Generic;

using System.Runtime.InteropServices;

using System.Text;



namespace ConsoleApplication1

{

class Program

{

[DllImport(@"E:\ex\TestCPPDLL\Debug\TestCPPDLL.dll", EntryPoint = "Add")]

extern static int Add(int a, int b);

[DllImport(@"E:\ex\TestCPPDLL\Debug\TestCPPDLL.dll", EntryPoint = "WriteString")]

extern unsafe static void WriteString(char* c);

[DllImport(@"E:\ex\TestCPPDLL\Debug\TestCPPDLL.dll", EntryPoint = "AddInt")]

extern unsafe static void AddInt(int* i);

[DllImport(@"E:\ex\TestCPPDLL\Debug\TestCPPDLL.dll", EntryPoint = "AddIntArray")]

extern unsafe static void AddIntArray(int* firstElement, int arraylength);

[DllImport(@"E:\ex\TestCPPDLL\Debug\TestCPPDLL.dll", EntryPoint = "GetArrayFromCPP")]

extern unsafe static int* GetArrayFromCPP();







//定义一个委托,返回值为空,存在一个整型参数

public delegate void CSCallback(int tick);

//定义一个用于回调的方法,与前面定义的委托的原型一样

//该方法会被C++所调用

static void CSCallbackFunction(int tick)

{

Console.WriteLine(tick.ToString());



}

//定义一个委托类型的实例,

//在主程序中该委托实例将指向前面定义的CSCallbackFunction方法

static CSCallback callback;





//这里使用CSCallback委托类型来兼容C++里的CPPCallback函数指针

[DllImport(@"E:\ex\TestCPPDLL\Debug\TestCPPDLL.dll", EntryPoint = "SetCallback")]

extern static void SetCallback(CSCallback callback);



[StructLayout(LayoutKind.Sequential)]

struct Vector3

{

public float X, Y, Z;

}



[DllImport(@"E:\ex\TestCPPDLL\Debug\TestCPPDLL.dll", EntryPoint = "SendStructFromCSToCPP")]

extern static void SendStructFromCSToCPP(Vector3 vector);



static void Main(string[] args)

{

int c = Add(1, 2);

Console.WriteLine(c);

//因为使用指针,因为要声明非安全域

unsafe

{

//在传递字符串时,将字符所在的内存固化,

//并取出字符数组的指针

fixed (char* p = &("hello".ToCharArray()[0]))

{

//调用方法

WriteString(p);

}



}

unsafe

{

// 调用C++中的AddInt方法

int i = 10;



AddInt(&i);

Console.WriteLine(i);



//调用C++中的AddIntArray方法将C#中的数据传递到C++中,并在C++中输出

int[] CSArray = new int[10];

for (int iArr = 0; iArr < 10; iArr++)

{

CSArray[iArr] = iArr;

}

fixed (int* pCSArray = &CSArray[0])

{

AddIntArray(pCSArray, 10);

}

//调用C++中的GetArrayFromCPP方法获取一个C++中建立的数组

int* pArrayPointer = null;

pArrayPointer = GetArrayFromCPP();

for (int iArr = 0; iArr < 10; iArr++)

{

Console.WriteLine(*pArrayPointer);

pArrayPointer++;

}

}









//让委托指向将被回调的方法

callback = CSCallbackFunction;

//将委托传递给C++

SetCallback(callback);



//建立一个Vector3的实例

Vector3 vector = new Vector3() { X = 10, Y = 20, Z = 30 };

//将vector传递给C++并在C++中输出

SendStructFromCSToCPP(vector);





Console.Read();

}

}

}

复制代码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/458024.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

curl查看swift状态命令_HTTP 请求与响应包括哪些,如何用Chrome查看 HTTP 请求与响应内容和curl 命令的使用...

1.HTTP的请求和响应其实就是通过电脑上的软件来进行的&#xff0c;客户端请求的内容发送到服务器上&#xff0c;服务器收到请求后就会响应客户端的请求&#xff0c;如图&#xff1a;HTTP请求的内容及格式&#xff1a;请求最多包含四部分&#xff0c;最少包含三部分。&#xff0…

Pytorch的BatchNorm层使用中容易出现的问题

前言 本文主要介绍在pytorch中的Batch Normalization的使用以及在其中容易出现的各种小问题&#xff0c;本来此文应该归属于[1]中的&#xff0c;但是考虑到此文的篇幅可能会比较大&#xff0c;因此独立成篇&#xff0c;希望能够帮助到各位读者。如有谬误&#xff0c;请联系指出…

android 比较靠谱的图片压缩

2019独角兽企业重金招聘Python工程师标准>>> 第一&#xff1a;我们先看下质量压缩方法&#xff1a; private Bitmap compressImage(Bitmap image) { ByteArrayOutputStream baos new ByteArrayOutputStream(); image.compress(Bitmap.CompressFormat.JPEG, 100, …

常用公差配合表图_ER弹簧夹头配套BT刀柄常用规格型号表

ER弹簧夹头具有定心精度高&#xff0c;夹紧力均匀的特点&#xff0c;广泛用于机械类零件的精加工和半精加工&#xff0c;通常与BT刀柄匹配使用。BT刀柄是是机械主轴与刀具和其它附件工具连接件&#xff0c;BT为日本标准(MAS403)&#xff0c;现在也是普遍使用的一种标准。传统刀…

Linux下python安装升级详细步骤 | Python2 升级 Python3

Linux下python升级步骤 Python2 ->Python3 多数情况下&#xff0c;系统自动的Python版本是2.x 或者yum直接安装的也是2.x 但是&#xff0c;现在多数情况下建议使用3.x 那么如何升级呢&#xff1f; 下面老徐详细讲解升级步骤&#xff1b; 首先下载源tar包 可利用linux自带下…

华为手机连电脑_手机、电脑无网高速互传!华为神技逆天

Huawei Share是华为的一项自研多终端传输技术&#xff0c;可以在没有网络状态下实现手机与手机、电脑等多终端设备间快速稳定的文件分享&#xff0c;尤其是在办公场景下&#xff0c;可以极大提升办公效率。华为表示&#xff0c;未来Huawei Share将应用于更多全场景跨设备无缝分…

excel统计行数_值得收藏的6个Excel函数公式(有讲解)

收藏的Excel函数大全公式再多&#xff0c;几天不用也会忘记。怎么才能不忘&#xff1f;你需要了解公式的运行原理。小编今天不再推送一大堆函数公式&#xff0c;而是根据提问最多的问题&#xff0c;精选出6个实用的&#xff0c;然后详细的解释给大家。1、计算两个时间差TEXT(B2…

Studio One正版多少钱 Studio One正版怎么购买

随着版权意识的增强&#xff0c;打击盗版的力度越来越大&#xff0c;现在网络上的盗版资源越来越少&#xff0c;资源少很难找是一方面&#xff0c;另一方面使用盗版软件不仅很多功能不能使用&#xff0c;而且很多盗版软件都被植入各种木马病毒&#xff0c;从而带来各种各样的风…

DNS简述

常见DNS记录SOA&#xff1a;域权威开始NS&#xff1a;权威域名服务器A&#xff1a;主机地址CNAME&#xff1a;别名对应的正规名称MX&#xff1a;邮件传递服务器PTR&#xff1a;域名指针 (用于反向 DNS)查询过程浏览器缓存->hosts->LDNS->LDNS缓存->ISP->ISP缓存…

cuda gpu相关汇总

1.Ubuntu16.04:在anaconda下安装pytorch-gpu 转自&#xff1a;Ubuntu16.04:在anaconda下安装pytorch-gpu_莫等闲996的博客-CSDN博客 1 创建虚拟环境并进入 conda create -n pytorch-gpu python3.6 conda activate pytorch-gpu 2 下载对应的安装包和配件 方法一(推荐)&#…

普通人学python有意义吗_学python难吗

首先&#xff0c;对于初学者来说学习Python是不错的选择&#xff0c;一方面Python语言的语法比较简单易学&#xff0c;另一方面Python的实验环境也比较容易搭建。学习Python需要的时间取决于三方面因素。(推荐学习&#xff1a;Python视频教程)其一是学习者是否具有一定的计算机…

在Visual Studio上开发Node.js程序(2)——远程调试及发布到Azure

【题外话】 上次介绍了VS上开发Node.js的插件Node.js Tools for Visual Studio&#xff08;NTVS&#xff09;&#xff0c;其提供了非常方便的开发和调试功能&#xff0c;当然很多情况下由于平台限制等原因需要在其他机器上运行程序&#xff0c;进而需要远程调试功能&#xff0c…

服务器定期监控数据_基础设施硬件监控探索与实践

本文选自 《交易技术前沿》总第三十六期文章(2019年9月)陈靖宇深圳证券交易所 系统运行部Email: jingyuchenszse.cn摘要&#xff1a;为了应对基础设施规模不断上升&#xff0c;数据中心两地三中心带来的运维挑战&#xff0c;深交所结合现有基础设施现状&#xff0c;以通用性、灵…

VS2010问题汇总

问题1&#xff1a;error C3872: "0xa0": 此字符不允许在标识符中使用 error C3872: "0xa0": 此字符不允许在标识符中使用 或者 error C3872: 0xa0: this character is not allowed in an identifier 解法&#xff1a;这是因为直接复制代码的问题。0xa0是…

vue如何获取年月日_好程序员web前端教程分享Vue相关面试题

好程序员web前端教程分享Vue相关面试题&#xff0c;Vue是一套构建用户界面的渐进式框架&#xff0c;具有简单易用、性能好、前后端分离等优势&#xff0c;是web前端工程师工作的好帮手&#xff0c;也是企业选拔人才时考察的重点技能。接下来好程序员web前端教程资源就给大家分享…

react dispatch_React系列自定义Hooks很简单

React系列-Mixin、HOC、Render Props(上)React系列-轻松学会Hooks(中)React系列-自定义Hooks很简单(下)我们在第二篇文章中介绍了一些常用的hooks&#xff0c;接着我们继续来介绍剩下的hooks吧useReducer 作为useState 的替代方案。它接收一个形如(state, action) > newStat…

前端 保存后端传来数据的id_一篇来自前端同学对后端接口的吐槽

前言去年的某个时候就想写一篇关于接口的吐槽&#xff0c;当时后端提出了接口方案对于我来说调用起来非常难受&#xff0c;但又说不上为什么&#xff0c;没有论点论据所以也就作罢。最近因为写全栈的缘故&#xff0c;团队内部也遇到了一些关于接口设计的问题&#xff0c;于是开…

2018-2019-1 《信息安全系统设计基础》教学进程

《信息安全系统设计基础》教学进程 目录 考核方式暑假准备教学进程 第01周学习任务和要求第02周学习任务和要求第03周学习任务和要求第04周学习任务和要求第05周学习任务和要求第06周学习任务和要求第07周学习任务和要求第08周学习任务和要求第09周学习任务和要求第10周学习任务…

Android中的数据库

2019独角兽企业重金招聘Python工程师标准>>> 1.1. 什么时候使用数据库 有大量相似结构的数据需要存储的时候就可以使用数据库。 1.2. SQLite的简介 SQLite是一款轻量级的数据库。它的设计目标是嵌入式的&#xff0c;而且目前已经在很多嵌入式产品中使用了它。Androi…

python计算绩效工资_python实现 --工资管理系统

原博文 2017-07-25 22:41 − # -*- coding: utf-8 -*- __author__ hjianli # import re import os info_message """Alex 100000 Rain 80000 Egon 50000 Yuan 30000 """ #序列字典 xulie_...01669 相关推荐 2019-09-28 21:13 − Python python…