CSharp如何调用dll中带指针参数剖析

CSharp如何调用dll中带指针参数剖析
CSharp如何调用dll中带指针参数剖析

C#调用C++DLL的方法,不同参数类型的调用方法

1. 参数为基本类型,例如int, float, char等

[C++]

void fun(int value;

void fun(float vaue;

void fun(char ch;

[C#]

[DllImport("xxx.dll"]

public static extern void fun(Int32 value;

[DllImport("xxx.dll"]

public static extern void fun(float value;

[DllImport("xxx.dll"]

public static extern void fun(char ch;

2. 参数为基本类型 + 指针,例如int*, float*, char*等[C++]

void fun(int* value;

void fun(float* vaue;

void fun(char* ch;

[C#]

[DllImport("xxx.dll"]

public static extern void fun(ref Int32 value;

[DllImport("xxx.dll"]

public static extern void fun(ref float value;

参数为char*,在C#中有几种实现方式

A. public static extern void fun(string ch; //ch内容不会改变

B. public static extern void fun(StringBuilder ch; //ch内容会改变

3. 参数为结构体

[C++]

struct point

{

int value; //基本类型

char ch; //基本类型

int number[100]; //数组

char buffer[100]; //字符串数组

};

void fun(point pt;

[C#]

[StructLayout(LayoutKind.Sequential]

public struct point

{

public Int32 value;

public char ch;

[MarshalAs(UnmanagedType.ByValArray, SizeConst = 100] public Int32[] number;

[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100] public char[] buffer;

}

[DllImport("xxx.dll"]

public static extern void fun(point pt;

4. 参数为结构体指针

[C++]

void fun(point* pt;

[C#]

[DllImport("xxx.dll"]

public static extern void fun(ref point pt;

5. 参数为结构体,并且结构体还嵌套结构体

[C++]

struct point

{

int value; //基本类型

char ch; //基本类型

int number[100]; //数组

char buffer[100]; //字符串数组

struct point pt; //嵌套结构体

};

void fun(point pt;

[C#]

[StructLayout(LayoutKind.Sequential]

public struct point

{

public Int32 value;

public char ch;

[MarshalAs(UnmanagedType.ByValArray, SizeConst = 100] public Int32[] number;

[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100]

public char[] buffer;

public point pt;

}

[DllImport("xxx.dll"]

public static extern void fun(point pt;

6. 参数为结构体,并且结构体还嵌套结构体指针或者双指针[C++]

struct point

{

int value; //基本类型

char ch; //基本类型

int number[100]; //数组

char buffer[100]; //字符串数组

struct point* p1; //嵌套结构体指针

struct point** p2; //嵌套结构体双指针

};

void fun(point pt;

[C#]

[StructLayout(LayoutKind.Sequential]

public struct point

{

public Int32 value;

public char ch;

[MarshalAs(UnmanagedType.ByValArray, SizeConst = 100] public Int32[] number;

[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100]

public char[] buffer;

public IntPtr p1; //使用IntPtr替代嵌套指针

public IntPtr p2; //使用IntPtr替代嵌套指针

}

[DllImport("xxx.dll"]

public static extern void fun(point pt;

由于嵌套指针的使用比较复杂,需要借助一些手段才能够确保正常调用DLL方法[C#]

class Program

{

[StructLayout(LayoutKind.Sequential]

public struct point

{

public Int32 value;

public char ch;

[MarshalAs(UnmanagedType.ByValArray, SizeConst = 100]

public Int32[] number;

[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100]

public char[] buffer;

public IntPtr p1; //使用IntPtr替代嵌套指针

public IntPtr p2; //使用IntPtr替代嵌套指针

}

[DllImport("xxx.dll"]

public static extern void fun(point pt;

static void Main(string[] args

{

point pt = new point(;

pt.p1 = Marshal.AllocHGlobal(Marshal.Sizeof(typeof(point; pt.p2 = Marshal.AllocHGlobal((Marshal.SizeOf(typeof(point * 2; try

{

pt.value = 1;

//实现给p1赋值

point ptt = new point(;

ptt.value = 1;

Marshal.StructureToPtr(ptt, pt.p1, false;

//实现给p2赋值

//由于双指针不能够直接传值,需要用到中间结构的数组指针IntPtr[] ptr = new IntPtr[2];

ptr[0] = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(point;

ptr[1] = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(point;

try

{

Marshal.StructureToPtr(ptt, ptr[0], false;

Marshal.Copy(ptr, 0, pt.p2, 2;

fun(pt;

//测试返回的指针值是否正确

ptt = (pointMarshal.PtrToStructure(pt.p1, typeof(point; Marshal.Copy(pt.p2, ptr, 0, 2;

ptt = (pointMarshal.PtrToStructure(ptr[0], typeof(point; ptt = (pointMarshal.PtrToStructure(ptr[1], typeof(point; }

catch(System.Exception e

{

string str = e.Message;

}

finally

{

Marshal.FreeHGlobal(ptr[0];

Marshal.FreeHGlobal(ptr[1];

}

}

catch(System.Exception e

{

string str = e.Message;

}

finally

{

Marshal.FreeHGlobal(pt.p1;

Marshal.FreeHGlobal(pt.p2;

}

}

}

参数传递方式

引用在函数参数传递中的作用 传递参数有三种方法:1,传递对象本身。2,传递指向对象的指针。3,传递对象的引用。 (1)传值方式 ①传给被调用函数的是整型、长整型、浮点型或双精度型变量。被调用的函数得定义相应的变量为形参。 ②传给被调用函数的是结构变量。被调用函数得定义结构变量为形参。 ③传给被调用函数的是结构变量的成员。被调用函数得定义与该成员同类的变量为形参。 #include "stdio.h" ?#include ?main( ) ?{ ?void swap(int pt1,int pt2); ?int a,b; ?scanf("%d, %d", &a,&b); ?swap(a,b); ?printf("\n%d,%d\n",a,b); ?} ?void swap(int pt1,int pt2) ?{int p; p=pt1; pt1=pt2; pt2=p; } ?

#include "stdio.h" void swapint(); int a,b; void main() { a = 5, b = 10; swapint(); printf("%d\n%d\n",a,b); } void swapint() { int temp; temp=a; a=b; b=temp; } (2)传址方式 ①传给被调用函数的是变量的地址。被调用函数得定义指针变量为形参。 ②传给被调用函数的是数组的地址即数组名。被调用的函数得定义数组或指针变量为形参。 ③传给被调用函数的是函数的地址即函数名称。被调用函数得定义指向函

数的指针变量为形参。④传给被调用函数的是结构的地址。被调用函数得定义结构指针为形参。 #include "stdio.h" ?#include ?main( ) ?{ ?void swap(int *pt1,int *pt2); ?int a,b,*p1,*p2; ?scanf("%d, %d", &a,&b); ?p1=&a;p2=&b; ?swap(p1,p2); ?printf("\n%d,%d\n",a,b); ?} ?void swap(int *pt1,int *pt2) ?{int p; p=*pt1; *pt1=*pt2; *pt2=p; } #include "stdio.h" void swapint(int *a,int *b); void main() { int a = 5, b = 10;

4:一个经典的多线程同步问题汇总

一个经典的多线程同步问题 程序描述: 主线程启动10个子线程并将表示子线程序号的变量地址作为参数传递给子线程。子线程接收参数 -> sleep(50) -> 全局变量++ -> sleep(0) -> 输出参数和全局变量。 要求: 1.子线程输出的线程序号不能重复。 2.全局变量的输出必须递增。 下面画了个简单的示意图: 分析下这个问题的考察点,主要考察点有二个: 1.主线程创建子线程并传入一个指向变量地址的指针作参数,由于线程启动须要花费一定的时间,所以在子线程根据这个指针访问并保存数据前,主线程应等待子线程保存完毕后才能改动该参数并启动下一个线程。这涉及到主线程与子线程之间的同步。 2.子线程之间会互斥的改动和输出全局变量。要求全局变量的输出必须递增。这涉及到各子线程间的互斥。 下面列出这个程序的基本框架,可以在此代码基础上进行修改和验证。 //经典线程同步互斥问题 #include #include #include long g_nNum; //全局资源 unsigned int__stdcall Fun(void *pPM); //线程函数 const int THREAD_NUM = 10; //子线程个数 int main() { g_nNum = 0;

HANDLE handle[THREAD_NUM]; int i = 0; while (i < THREAD_NUM) { handle[i] = (HANDLE)_beginthreadex(NULL, 0, Fun, &i, 0, NULL); i++;//等子线程接收到参数时主线程可能改变了这个i的值} //保证子线程已全部运行结束 WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE); return 0; } unsigned int__stdcall Fun(void *pPM) { //由于创建线程是要一定的开销的,所以新线程并不能第一时间执行到这来int nThreadNum = *(int *)pPM; //子线程获取参数 Sleep(50);//some work should to do g_nNum++; //处理全局资源 Sleep(0);//some work should to do printf("线程编号为%d 全局资源值为%d\n", nThreadNum, g_nNum); return 0; } 运行结果:

C#多线程函数如何传参数和返回值

C#多线程函数如何传参数和返回值 提起多线程,不得不提起委托(delegates)这个概念. 我理解的委托就是具有同样参数和返回值的函数的集合. 比如 public delegate void MyDelegate(int arg); 就是这种形式的函数 void Myfuntion(int i); 的集合. 如何将一个函数加入委托的集合? MyDelegate dele = new MyDelegate(Myfuntion1); 再增加一个 dele += new MyDelegate(Myfuntion2); ... 委托函数 dele 就是具有整数参数和空返回值的函数 Myfuntion1,2的集合. 调用这个委托函数 dele(1); 就是逐个调用 Myfuntion1,2,... 一般线程函数的声明和启动 Thread t = new Thread(new ThreadStart(MyFunction)); t.Start(); 正是调用了没有参数和返回值的委托函数 ThreadStart 其中的参数MyFunction 是这个委托函数中的一员. 很明显这样无法传参数和返回值,那我们该怎么办? 答案就在委托的BeginInvoke() 方法上, BeginInvoke() 也是(异步)启动一个新线程. 例如 MyDelegate dele = new MyDelegate (MyFunction); dele.BeginInvoke(10,"abcd"); void MyFunction(int count, string str); 可以实现参数的传递. 如何收集线程函数的返回值? 与BeginInvoke 对应有个 EndInvoke 方法,而且运行完毕返回 IAsyncResult 类型的返回值.这样我们可以这样收集线程函数的返回值 MyDelegate dele = new MyDelegate (MyFunction); IAsyncResult ref = dele.BeginInvoke(10,"abcd"); ...

C#中方法的参数有四种类型

C#中方法的参数有四种类型 1. 值参数(不加任何修饰符,是默认的类型) 2. 引用型参数(以ref 修饰符声明) 3. 输出参数(以out 修饰符声明) 4. 数组型参数(以params 修饰符声明) 1. 值传递: 值类型是方法默认的参数类型,采用的是值拷贝的方式。也就是说,如果使用的是值类型,则可以在方法中更改该值,但当控制传递回调用过程时,不会保留更改的值。 使用值类型的例子如:(下面的Swap()未能实现交换的功能,因为控制传递回调用方时不保留更改的值) using System; class Test { static void Swap(int x, int y) { int temp = x; x = y; y = temp; } static void Main() { int i = 1, j = 2; Swap(i, j); Console.WriteLine("i = {0}, j = {1}", i, j); } } /* * 输出结果为: i=1, j=2 * 未能实现Swap()计划的功能 */ 2. 引用传递(ref类型) ref关键字使参数按引用传递。其效果是,当控制权传递回调用方法时,在方法中对参数所做的任何更改都将反映在该变量中。 2.1. 若要使用ref 参数,则方法定义和调用方法都必须显式使用ref关键字。 2.2. 传递到ref 参数的参数必须最先初始化。这与out 不同,out 的参数在传递之前不需要显式初始化。 2.3. 如果一个方法采用ref 或out 参数,而另一个方法不采用这两类参数,则可以进行重载。

相关实例如下: using System; class Test { static void Swap(ref int x, ref int y) { int temp = x; x = y; y = temp; } static void Main() { int i = 1, j = 2; Swap(ref i, ref j); Console.WriteLine("i = {0}, j = {1}", i, j); } } /* * 引用类型实现了Swap()计划的功能: * 输出为: * i = 2, j =1 */ 3. 输出类型(out类型) out 关键字会导致参数通过引用来传递。这与ref 关键字类似。 与ref 的不同之处: 3.1. ref 要求变量必须在传递之前进行初始化,out 参数传递的变量不需要在传递之前进行初始化。 3.2. 尽管作为out 参数传递的变量不需要在传递之前进行初始化,但需要在调用方法初始化以便在方法返回之前赋值。 示例如下: using System; class Test { static void Swap(out int x, out int y) { //在这里进行了i和j的初始化

结构体的指针应用

什么是结构体? 简单的来说,结构体就是一个可以包含不同数据类型的一个结构,它是一种可以自己定义的数据类型,它的特点和数组主要有两点不同,首先结构体可以在一个结构中声明不同的数据类型,第二相同结构的结构体变量是可以相互赋值的,而数组是做不到的,因为数组是单一数据类型的数据集合,它本身不是数据类型(而结构体是),数组名称是常量指针,所以不可以作为左值进行运算,所以数组之间就不能通过数组名称相互复制了,即使数据类型和数组大小完全相同。 定义结构体使用struct修饰符,例如: struct test { float a; int b; }; 上面的代码就定义了一个名为test的结构体,它的数据类型就是test,它包含两个成员a和b,成员a的数据类型为浮点型,成员b的数据类型为整型。由于结构体本身就是自定义的数据类型,定义结构体变量的方法和定义普通变量的方法一样。 test pn1; 这样就定义了一个test结构体数据类型的结构体变量pn1,结构体成员的访问通过点操作符进行,pn1.a=10 就对结构体变量pn1的成员a进行了赋值操作。注意:结构体生命的时候本身不占用任何内存空间,只有当你用你定义的结构体类型定义结构体变量的时候计算机才会分配内存。 结构体,同样是可以定义指针的,那么结构体指针就叫做结构指针。 结构指针通过->符号来访问成员,下面我们就以上所说的看一个完整的例子: #include #include using namespace std; struct test//定义一个名为test的结构体 { int a;//定义结构体成员a int b;//定义结构体成员b }; void main() { test pn1;//定义结构体变量pn1 test pn2;//定义结构体变量pn2 pn2.a=10;//通过成员操作符.给结构体变量pn2中的成员a赋值 pn2.b=3;//通过成员操作符.给结构体变量pn2中的成员b赋值

基于多线程的制造数据分析和可视化

2007年第24卷第10期微电子学与计算机105 4实例 焊膏印刷是SMT生产过程中的主要工序之一.下面以实现焊膏印刷的焊膏印刷机为例.对上述的原理和过程进行验证。 焊膏印刷机是通过在印刷设备的PC机中插入符合开放标准GEM/SECSlI(通用设备模型)的主机通信卡来实现关键信息的采集。所采集的数据类型包括:前刮刀压力、前刮刀印刷速度、后刮刀压力、后刮刀印刷速度、刮刀正行程位置、刮刀负行程位置、温度值、湿度值等。 该系统首先进入用户登录界面:根据用户的不同权限等级.给与不同的操作许可。选择不同的设备、订单名称、工艺文件名称,开始生产后,焊膏印刷机能方便的实时显示界面。选择想要绘制盖—霄控制图的数据类型.即可绘制出该数据类型的盖—月控制图。如图4所示。 图4-_一R控制图显示界面 5结束语 由于采用了多线程、数据动态实时显示、多媒体定时器、夏—R控制图等技术,使程序实现了对多设备大数据量的实时数据采集、存储、绘制曲线,及时判断数据是否处于失控状态.缩短了延时时间。制造数据可视化程度达95%以上为车间的操作人员提供了随时了解设备运行情况的有效手段.在现实应用中具有广泛的实用价值。 当然.程序也存在一些不足。程序运行在WindowsXP操作系统下.多任务操作系统固有的任务切换:其他驱动程序的CPU时间的抢占;高优先级应用程序的执行:不确定的操作系统的作业任务分配规则等许多问题.都可以导致多媒体定时器定时的不准确。所以,要提高精度.可以考虑采用专门的硬件电路。通过软硬件相结合,以达到高精度的定时,提高应用程序的实用性。 参考文献: 【1】帅梅,王爱周,王广毙.基于Windows数控系统得多线程实现叨.机床与液压.2003 【2】吴丽娜.高槛阳.Wmdows2000/XP下通用实时采集的设计与实现Ⅱ1.计算机应用,2005 【3】肖建明,张向利.一种改进的时间片轮转调度算法田.计算机应用.2005 『41杨乐,王厚军,戴志坚.测试仪器中的动态波形绘制技术叨.仪器仪表学报,2006 『51曹祁,杜树旺.基于微机的压缩机数据采集方法研究与实现U1.仪器仪表学报.2006 【6】杨桂元.中心极限定理及其在统计分析中的应用【J】.管理工程学报.1998 作者简介: 王小婷女.(1969一)。研究方向为计算机工程与应用。 韩方女.研究生。研究方向为图形图像处理、制造过程仿真与优化。 (上接第101页) 网络系统中节点之间的相互作用导致了系统在宏观上表现出了复杂的整体行为.这些结果与已有的研究结果是相一致的日。 4结束语 文中建立了用于分析网络流量行为的一维元胞自动机模型.以节点发送数据分组的规则来描述在传输数据分组过程中节点间的相互影响以及自组织作用后使计算机网络分组传输系统表示出来的一种整体行为.模拟了网络系统发送数据分组随机的不确定状态。仿真结果说明该模型能较好地描述网络流量非拥塞相到拥塞相的变化过程。参考文献: 【1]WolframS.Cellularautomataandcomple6xity[M].RendingMA,Addison-Wesley,1994 【2】彭麟,谭惠丽,孔令江,等.开放性边界条件下双道元胞自动机变通流模型耦合效应研究Ⅱ].物理学报,2003,52(12):3007-3013 p]Ohira T,sa砌嘶凡PhasetransitioninEeom!tⅫltertlet?worktmmemodd田.P}-y8.Rev,1998:193-195 作者简介: 雷霆男.(1972-).博士研究生。讲师。研究方向为通信与信息系统、计算机网络。 余镇危 男.(1942一),教授。研究方向为计算机网络。

指针和结构体练习题.

第十章指针 一.选择题 1.变量的指针,其含义是指该变量的。 A)值 B)地址 C)名 D)一个标志 2.已有定义int k=2;int *ptr1,*ptr2;且ptr1和ptr2均已指向变量k,下面不能正确执行的赋值语句是。 A)k=*ptr1+*ptr2 B)ptr2=k C)ptr1=ptr2 D)k=*ptr1*(*ptr2 3.若有说明:int *p,m=5,n;以下程序段正确的是。 A)p=&n ; B)p = &n ; scanf(“%d”,&p; scanf(“%d”,*p; C)scanf(“%d”,&n; D)p = &n ; *p=n ; *p = m ; 4.已有变量定义和函数调用语句:int a=25;print_value(&a;下面函数的输出结果是。 void print_value(int *x { printf(“%d\n”,++*x; } A)23 B)24 C)25 D)26 5.若有说明:int *p1, *p2,m=5,n;以下均是正确赋值语句的选项是。 A)p1=&m; p2=&p1 ; B)p1=&m; p2=&n; *p1=*p2 ; C)p1=&m; p2=p1 ; D)p1=&m; *p1=*p2 ; 6.若有语句:int *p,a=4;和p=&a;下面均代表地址的一组选项是。 A)a,p,*&a B)&*a,&a,*p C)*&p,*p,&a D)&a,&*p,p 7.下面判断正确的是。 A)char *a=”china”; 等价于char *a; *a=”china” ; B)char str[10]={“china”}; 等价于char str[10]; str[ ]={“china”;}

Winform多窗口或多线程传递数据的方法

前提:假设现在有两个窗口Form1和Form2, Form2是Form1的子窗口,现在需要通过Form2来改变Form1中的内容 效果: 方法一:使用Delegate(代理) 第一步:在Form2中定义代理并声明实例 第二步:在Form1中定义用来代理的函数 第三步:在Form1中生成Form2的实例并将代理赋值给Form2中的代理对象

第四步:在Form2中调用代理 总结:当Form2调用代理对象proEvent时实际上是在调用Form1中的Eventpro函数,由于Eventpro属于Form1,所以赋值成功。 方法二:使用自定义事件 第一步:自定义事件 第二步:在Form2中声明事件对象 第三步:在Form1中定义事件回调函数

第四步:创建Form2的对象实例,并将事件的回调函数添加到事件上(订阅事件) 第五步:在Form2中的按钮上触发事件 总结:当第五步事件被触发,事件对象Events会向所有订阅该事件的函数进行触发,而回调函数EventCallBack是Form1的成员,见第三步,所以数据传递成功。 利弊分析:第一种方法的优点显而易见,代理参数是可以自定义的,如:void EventPro(string Message),其缺点就是,每一个这样的跨窗口调用都需要在子窗口中定义一个代理对象,并在主窗口中赋值相应的函数。相对来说,我偏向于使用自定义事件,首先,不是所有学过winform的人都接触过这部分内容(高端大气上档次),其次,他可以实现一个函数向n个窗口传值,只要给事件添加订阅就可以了,frm.Events += new EventHandler(EventCallBack)。(方便)。另外,看过winform下层代

指针在结构体中的应用.

实验14 指针在结构体中的应用 一、实验目的 1.掌握结构体类型指针的定义及使用方法。 2.掌握结构体类型指针作为函数参数,实现函数调用。 3.掌握简单链表的基本操作。 二、实验要求 1.通过阅读及编程,掌握结构体类型指针的定义及使用方法。 2.通过阅读及编程,掌握结构体类型指针作为函数参数,实现函数调用。 3.通过阅读及编程,掌握简单链表的基本操作(包括链表的建立、查找、遍历、插入、删除)。 三、实验内容 1.阅读下列程序,预测程序运行结果,然后上机验证。 main () { struct num { int a ; int b ; float f ; } n={ 1 , 3, 5.0 }; struct num * pn =&n ; printf ( “ %d\n ” , ( pn->b/n.a )*(++pn ->b) ); printf ( “ %f \n ” , ( *pn ).a + pn ->f ); } 1.读下列程序,指出程序的功能。 struct tm { int hours ; int minutes ; int seconds ; }; main ( ) { struct tm time ; time. hours=0 ; time. minutes =0; time . seconds =0 ;

for ( ; ; ) { update ( &time ); display (&time ); } } update ( struct tm * t ) { t-> seconds++; if ( t-> seconds= =60 ) { t-> seconds=0; t-> minues++; } if ( t-> minues= =60 ) { t-> minues=0; t-> hours++; } if ( t-> hours= =24 ) t-> hours=0; deday ( ); } display ( struct tm * t ) { printf ( “%d: ” , t-> hours ); printf ( “%d: ” , t-> minutes ); printf ( “%d:\n” , t-> seconds ); } delay ( ) { long int t; for ( t=1; t<12800 ; ++t ); } 3. 阅读并运行下列程序,写出运行结果。 #include “stdio.h” main ( ) { struct person { char name[20];

C++中函数调用时的三种参数传递方式

在C++中,参数传递的方式是“实虚结合”。 ?按值传递(pass by value) ?地址传递(pass by pointer) ?引用传递(pass by reference) 按值传递的过程为:首先计算出实参表达式的值,接着给对应的形参变量分配一个存储空间,该空间的大小等于该形参类型的,然后把以求出的实参表达式的值一一存入到形参变量分配的存储空间中,成为形参变量的初值,供被调用函数执行时使用。这种传递是把实参表达式的值传送给对应的形参变量,故称这种传递方式为“按值传递”。 使用这种方式,调用函数本省不对实参进行操作,也就是说,即使形参的值在函数中发生了变化,实参的值也完全不会受到影响,仍为调用前的值。 [cpp]view plaincopy 1./* 2. pass By value 3.*/ 4.#include https://www.360docs.net/doc/063402265.html,ing namespace std; 6.void swap(int,int); 7.int main() 8.{ 9.int a = 3, b = 4; 10. cout << "a = " << a << ", b = " 11. << b << endl; 12. swap(a,b); 13. cout << "a = " << a << ", b = " 14. << b << endl; 15.return 0; 16.} 17.void swap(int x, int y) 18.{ 19.int t = x; 20. x = y; 21. y = t; 22.}

如果在函数定义时将形参说明成指针,对这样的函数进行调用时就需要指定地址值形式的实参。这时的参数传递方式就是地址传递方式。 地址传递与按值传递的不同在于,它把实参的存储地址传送给对应的形参,从而使得形参指针和实参指针指向同一个地址。因此,被调用函数中对形参指针所指向的地址中内容的任何改变都会影响到实参。 [cpp]view plaincopy 1.#include https://www.360docs.net/doc/063402265.html,ing namespace std; 3.void swap(int*,int*); 4.int main() 5.{ 6.int a = 3, b = 4; 7. cout << "a = " << a << ", b = " 8. << b << endl; 9. swap(&a,&b); 10. cout << "a = " << a << ", b = " 11. << b << endl; 12. system("pause"); 13.return 0; 14.} 15.void swap(int *x,int *y) 16.{ 17.int t = *x; 18. *x = *y; 19. *y = t; 20.} 按值传递方式容易理解,但形参值的改变不能对实参产生影响。 地址传递方式虽然可以使得形参的改变对相应的实参有效,但如果在函数中反复利用指针进行间接访问,会使程序容易产生错误且难以阅读。

11线程池的使用

第11章线程池的使用 第8章讲述了如何使用让线程保持用户方式的机制来实现线程同步的方法。用户方式的同步机制的出色之处在于它的同步速度很快。如果关心线程的运行速度,那么应该了解一下用户方式的同步机制是否适用。 到目前为止,已经知道创建多线程应用程序是非常困难的。需要会面临两个大问题。一个是要对线程的创建和撤消进行管理,另一个是要对线程对资源的访问实施同步。为了对资源访问实施同步,Wi n d o w s提供了许多基本要素来帮助进行操作,如事件、信标、互斥对象和关键代码段等。这些基本要素的使用都非常方便。为了使操作变得更加方便,唯一的方法是让系统能够自动保护共享资源。不幸的是,在Wi n d o w s提供一种让人满意的保护方法之前,我们已经有了一种这样的方法。 在如何对线程的创建和撤消进行管理的问题上,人人都有自己的好主意。近年来,我自己创建了若干不同的线程池实现代码,每个实现代码都进行了很好的调整,以便适应特定环境的需要。M i c r o s o f t公司的Windows 2000提供了一些新的线程池函数,使得线程的创建、撤消和基本管理变得更加容易。这个新的通用线程池并不完全适合每一种环境,但是它常常可以适合你的需要,并且能够节省大量的程序开发时间。 新的线程池函数使你能够执行下列操作: ? 异步调用函数。 ? 按照规定的时间间隔调用函数。 ? 当单个内核对象变为已通知状态时调用函数。 ? 当异步I / O请求完成时调用函数。 为了完成这些操作,线程池由4个独立的部分组成。表11 - 1显示了这些组件并描述了控制其行为特性的规则。 表11-1 线程池的组件及其行为特性

C语言中不同的结构体类型的指针间的强制转换详解

C语言中不同类型的结构体的指针间可以强制转换,很自由,也很危险。只要理解了其内部机制,你会发现C是非常灵活的。 一. 结构体声明如何内存的分布, 结构体指针声明结构体的首地址, 结构体成员声明该成员在结构体中的偏移地址。 变量的值是以二进制形式存储在内存中的,每个内存字节对应一个内存地址,而内存存储的值本身是没有整型,指针,字符等的区别的,区别的存在是因为我们对它们有不同的解读,param的值就是一个32位值,并且存储在某个内存单元中,通过这个32位值就能找到param所指向的结构的起始地址,通过这个起始地址和各个结构所包含变量离起始地址的偏移对这些变量进行引用, param->bIsDisable只是这种引用更易读的写法,只要param是指向 PAINT_PARAM的指针,那么param的值就肯定存在,param存在,偏移量已知,那么param->bIsDisable就肯定存在,只是要记住,param->bIsDisable只是代表了对param一定偏移地址的值。 不是说某个地址有那个结构体你才能引用,即使没有,你也能引用,因为你已经告诉了编译器param变量就是指向一个PAINT_PARAM结构体的变量并且指明了param的值,机器码的眼中是没有数据结构一说的,它只是机械的按照 指令的要求从内存地址取值,那刚才的例子来说,peg->x,peg->y的引用无论 0x30000000是否存在一个eg结构体都是合法的,如果0x30000000开始的8 个字节存在eg结构体,那么引用的就是这个结构体的值,如果这个位置是未定义的值,那么引用的结果就是这8个字节中的未定义值,内存位置总是存在的,而对内存中值的引用就是从这些内存位置对应的内存单元取值。 举个例子: typedefstruct_eg { int x; int y; }eg;

并发危险:解决多线程代码中的 11 个常见的问题

并发危险:解决多线程代码中的11 个常见的问题 并发危险 解决多线程代码中的11 个常见的问题 Joe Duffy 目录 数据争用忘记同步粒度错误读写撕裂无锁定重新排序重新进入死锁锁保护戳记两步舞曲优先级反转实现安全性的模式不变性纯度隔离并发现象无处不在。服 务器端程序长久以来都必须负责处理基本并发编程模型,而随着多核处理器的日益普及,客户端程序也将需要执行一些任务。随着并发操作的不断增加,有关确保安 全的问题也浮现出来。也就是说,在面对大量逻辑并发操作和不断变化的物理硬件并行性程度时,程序必须继续保持同样级别的稳定性和可靠性。 与对应的顺序代码相比,正确设计的并发代码还必须遵循一些额外的规则。对内存的读写以及对共享资源的访问必须使用同步机制进行管制,以防发生冲突。另外,通常有必要对线程进行协调以协同完成某项工作。

这些附加要求所产生的直接结果是,可以从根本上确保线程始终保持一致并且保证其顺利向前推进。同步和协调对时间的依赖性很强,这就导致了它们具有不确定性,难于进行预测和测试。 这 些属性之所以让人觉得有些困难,只是因为人们的思路还未转变过来。没有可供学习的专门 API,也没有可进行复制和粘贴的代码段。实际上的确有一组基础概念需要您学习和适应。很可能随着时间的推移某些语言和库会隐藏一些概念,但如果您现在就 开始执行并发操作,则不会遇到这种情况。本文将介绍需要注意的一些较为常见的挑战,并针对您在软件中如何运用它们给出一些建议。 首先我将讨论在并发程序中经常会出错的一类问题。我把它们称为“安全隐患”,因为它们很容易发现并且后果通常比较严重。这些危险会导致您的程序因崩溃或内存问题而中断。当 从多个线程并发访问数据时会发生数据争用(或竞争条件)。特别是,在一个或多个线程写入一段数据的同时,如果有一个或多个线程也在读取这段数据,则会发生 这种情况。之所以会出现这种问题,是因为Windows 程序(如C++ 和Microsoft .NET Framework

函数调用参数传递类型(java)的用法介绍.

函数调用参数传递类型(java)的用法介绍. java方法中传值和传引用的问题是个基本问题,但是也有很多人一时弄不清。 (一)基本数据类型:传值,方法不会改变实参的值。 public class TestFun { public static void testInt(int i){ i=5; } public static void main(String[] args) { int a=0 ; TestFun.testInt(a); System.out.println("a="+a); } } 程序执行结果:a=0 。 (二)对象类型参数:传引用,方法体内改变形参引用,不会改变实参的引用,但有可能改变实参对象的属性值。 举两个例子: (1)方法体内改变形参引用,但不会改变实参引用,实参值不变。 public class TestFun2 { public static void testStr(String str){ str="hello";//型参指向字符串“hello” } public static void main(String[] args) { String s="1" ;

TestFun2.testStr(s); System.out.println("s="+s); //实参s引用没变,值也不变 } } 执行结果打印:s=1 (2)方法体内,通过引用改变了实际参数对象的内容,注意是“内容”,引用还是不变的。 import java.util.HashMap; import java.util.Map; public class TestFun3 { public static void testMap(Map map){ map.put("key2","value2");//通过引用,改变了实参的内容 } public static void main(String[] args) { Map map = new HashMap(); map.put("key1", "value1"); new TestFun3().testMap(map); System.out.println("map size:"+map.size()); //map内容变化了 } } 执行结果,打印:map size:2 。可见在方法testMap()内改变了实参的内容。 (3)第二个例子是拿map举例的,还有经常涉及的是 StringBuffer : public class TestFun4 {

多线程高效访问共享资源

多线程高效访问共享资源 -------BY 懒牛 一、多线程访问共享资源需注意的问题: 1、多个线程如果在一个进程内,可共享进程内的资源。 2、多个线程在不同的进程内,不同进程的资源不能直接共享,要用到内存映射文件。 3、访问方式是用户方式还是内核方式,用户方式速度快,内核方式速度慢。 二、用户方式和内核方式的区别: 1、书上说的用户方式有互锁函数,CRITICAL_SECTION关键代码段,内核方式有信标、事件、互斥量。但是关键代码段也不是完全的用户方式,当一个线程进入关键代码段访问资源,另一个线程是进入内核方式等待的。也是很费时间。经过理解,我认为:用户方式是指当一个线程占有资源时,另外的线程是以什么方式在等待。 2、平常让程序等待的方式有三种,一种是让程序不断的条件循环,当条件达到时,退出。这是最标准的用户方式,依靠循环浪费时间来等待,没用到内核。第二种用sleep()函数,这个不太好用条件控制,本质上也是内核方式。第三种就是内核的Waitfor…..之类的等待函数了。 3、从浪费时间的程度上来看,要分CPU是单核还是多核的,如果是单核的,用户方式的条件循环最浪费时间,因为单核时循环要不断的在几个线程上下文切换特浪费时间;内核方式其次,所以在单核情况下,我们多线程访问资源,就直接用内核方式就OK了。在多核方式下情况有变化,一个线程在第一个CPU上访问资源时,另一个线程可以使用第二个CPU来在用户方式下做条件循环来判断是否能够访问资源了。多核方式下是真正的并发访问,二个线程同时运行,不用做上下文切换。如果这种访问资源的时间用时很短,比如说:一个线程只是在资源上做一个简单运算就离开,则另一个线程在几个用户循环的判断时间就能够访问资源了,何苦要进入内核方式呢?所以在对共享资源访问时,我们先在用户方式下做几个循环来判断,如果能访问了,OK进行访问,如果循环到一定时间,还是不能访问资源,说明这种资源的访问很费时间,在用户方式下继续做循环不合算了,我们就换成内核方式,让系统去帮我等待去吧,我们不管了,哈哈! 三、多线程访问共享资源总结如下: 1、单核时直接进入内核方式等待,等待成功则访问资源。 2、多核时先在用户方式下做循环不断的询问是否能访问,到达一定循环次数,如果条件满足则访问,如果到循环最大值仍然不能访问,则我们也不浪费时间,直接转内核方式等待。 WINDOWS核心编程中为高效访问共享资源,编制了COptex类,使用这个类建立的对象就是多个线程要访问的共享资源,下面让我们来详细分析一下: 表1-1 成员变量描述

Linux学习之线程体传递参数

在线学习好工作https://www.360docs.net/doc/063402265.html,/ Linux学习之线程体传递参数 传递参数的两种方法 线程函数只有一个参数的情况:直接定义一个变量通过应用传给线程函数。例子 #include #include using namespace std; pthread_t thread; void * fn(void *arg) { int i = *(int *)arg; cout<<"i = "<

操作系统以进程为单位分配资源。 线程是执行单位,线程函数有多个参数的情况:这种情况就必须申明一个结构体来包含所有的参数,然后在传入线程函数。 具体请查看代码: Mularg.c #include #include #include #include typedef struct arg_struct ARG ; struct arg_struct { char name[10] ; int age ; float weight ; } ; void * thfn ( void * arg ) { ARG * p = (ARG *) arg ; printf( " name is : %s , age is : % d , weight is : %f \ n " , p->name , p->age , p->weight ) ; return NULL ; } int main(int argc , char *argv [ ] ) { pthread_t tid ;

深入了解C语言(函数的参数传递和函数使用参数的方法)

深入了解C语言(函数的参数传递和函数使用参数的方法) C语言生成的代码在执行效率上比其它高级语言都高.现在让我们来看看C语言生成的代码具体是什么样子的.当你看完本文对于C语言的了解一定会更深一步了. 本文通过一个个实际案例程序来讲解C语言. 研究案例一 工具: Turboc C v2.0,Debug,MASM v5.0,NASM 实例C程序: /* example1.c */ char ch; int e_main() { e_putchar(ch); } 目标内容:C语言调用函数的方法与细节 我们使用的C编译器是16位的Turboc C v2.0,它生成的是16位的代码,比较简单,方便我们来研究.同时我们也需要用到DOS下的DEBUG来进行反汇编.由于我们很多案例中的程序并不是完整的C程序,所以Turboc下的Tlink并不能为我们生成目标程序,所以我将使用MASM 中的link.exe,同时里面的https://www.360docs.net/doc/063402265.html,也可以为我们把exe文件转换成bin文件. 这个程序没有main函数,我们用e_main来代替main函数.这样我们能避开C语言对main函数进行一系列处理的代码.同样,我们也用e_putchar()来代替我们平常使用的putchar().这里"e"的意思就是"example". 没有了main函数,我们的C程序就没有了入口,所以在开始编译这段C代码之前,我还得写几行简单的汇编代码,通过它来作为我们程序的入口. ; C程序的入口start.asm [BITS 16] [global start] [extern _e_main] start: call _e_main 按照C语言的习惯,所以C总的名词都要自动在前面加一个"_"下划线.所以,我们在C中的e_main函数,如果要在汇编中调用,就变成了_e_main函数.这段汇编代码只有一句:call _e_main,就是调用我们在C中的e_main函数

C#给线程传递参数有3种方式

C#给线程传递参数有3种方式 从《C#高级编程》了解到给线程传递参数有两种方式,一种方式是使用带ParameterizedThreadStart委托参数的Thread构造函数;另一种方式是创建一个自定义类,把线程的方法定义为实例的方法,这样就可以初始化实例的数据,之后启动线程。 方式一:使用ParameterizedThreadStart委托 如果使用了ParameterizedThreadStart委托,线程的入口必须有一个object类型的参数,且返回类型为void。且看下面的例子: using System;using System.Threading;namespace ThreadWithParameters{ class Program { static void Main(string[] args) { string hello = "hello world"; //这里也可简写成Thread thread = new Thread(ThreadMainWith Parameters); //但是为了让大家知道这里用的是ParameterizedThreadStart委托,就没有简写了 Thread thread = new Thread(new ParameterizedThreadStart(T hreadMainWithParameters)); thread.Start(hello); Console.Read(); } static void ThreadMainWithParameters(object obj) { string str = obj as string; if(!string.IsNullOrEmpty(str)) Console.WriteLine("Running in a thread,received: {0}", str); } } } 这里稍微有点麻烦的就是ThreadMainWithParameters方法里的参数必须是object类型的,我们需要进行类型转换。为什么参数必须是object类型呢,各位看看ParameterizedThreadStart委托的声明就知道了。 public delegate void ParameterizedThreadStart(object obj); //ParameterizedThreadStart委托的声明 方式二:创建自定义类

SpringMVC向页面传递参数的4种方式

SpringMVC向页面传递参数的4种方式 1、使用HttpServletRequest和Session 然后setAttribute(),就和Servlet 中一样 request.setAttribute(“user”,user_data); 2、使用ModelAndView对象 @RequestMapping("/login.do") publicModelAndView login(String name,String pass) { User user = userService.login(name,pwd); Map data = new HashMap(); data.put("user",user); return newModelAndView("success",data); } 3、使用ModelMap对象 ModelMap数据会利用HttpServletRequest的Attribute传值到success.jsp中 @RequestMapping("/login.do") public String login(String name,String pass ,ModelMapmodelMap) { User user =userService.login(name,pwd); modelMap.addAttribute("user",user); modelMap.put("name",name); return "success"; } Session存储,可以利用HttpServletReequest的getSession()方法 @RequestMapping("/login.do") Public String login (String name,Stringpwd,ModelMapmodel,HttpServletRequest request) { User user = serService.login(name,pwd); HttpSession session = request.getSession(); session.setAttribute("user",user); model.addAttribute("user",user); return "success"; } 4、使用@ModelAttribute注解

相关文档
最新文档