当前位置:首页 » 操作系统 » linuxc参数

linuxc参数

发布时间: 2023-09-04 11:22:04

linux c 引用传递参数

C/C++函数参数的传递方式有三种:值传递(pass by value)、指针传递(pass bypointer)、引用传递(pass by reference)。
C/C++函数参数的传递通道是通过堆栈传递,默认遵循__cdecl(C声明方式),参数由调用者从右往左逐个压入堆栈,在函数调用完成之后再由调用者恢复堆栈。(Win32API遵循stdcall传参规范的,不在本文讨论范围)
下面是测试代码
void Swap(__int64* _pnX, __int64* _pnY)
{
__int64 nTemp = *_pnX;
*_pnX = *_pnY;
*_pnY = nTemp;
}
void Swap(__int64& _nX, __int64& _nY)
{
__int64 nTemp = _nX;
_nX = _nY;
_nY = nTemp;
}
void SetValue(__int64 _nX)
{
__int64 nTemp = _nX;
}
// Test001
void GetMemory(__int64* _pBuff)
{
_pBuff = new __int64[4];
}
// Test002
void GetMemory(__int64** _ppBuff)
{
*_ppBuff = new __int64[4];
}
int _tmain(int argc, _TCHAR* argv[])
{
__int64 nA = 0x10;
__int64 nB = 0x20;
// Test to pass by pointer
Swap(&nA, &nB);
// Test to pass by reference
Swap(nA, nB);
// Test to pass by value
SetValue(nA);
// Test the pointer that points the pointer
__int64* _pArray = NULL;
GetMemory(&_pArray);
delete[] _pArray;
_pArray = NULL;
// Test the pointer
GetMemory(_pArray);
return 0;
}
指针传递和引用传递
// 下面看一下对应的反汇编的代码(VS版)
__int64 nA = 0x10;
0041370E mov dword ptr [nA],10h
mov dword ptr [ebp-8],0
__int64 nB = 0x20;
0041371C mov dword ptr [nB],20h
mov dword ptr [ebp-18h],0
// Test to pass by pointer
Swap(&nA, &nB);
0041372A lea eax,[nB]
0041372D push eax
0041372E lea ecx,[nA]
push ecx
call Swap (4111E5h)
add esp,8
// Test to pass by reference
Swap(nA, nB);
0041373A lea eax,[nB]
0041373D push eax
0041373E lea ecx,[nA]
push ecx
call Swap (4111E0h)
add esp,8
// GCC版
0x00401582 <+30>: lea eax,[esp+0x18]
0x00401586 <+34>: mov DWORD PTR [esp+0x4],eax
0x0040158a <+38>: lea eax,[esp+0x1c]
0x0040158e <+42>: mov DWORD PTR [esp],eax
0x00401591 <+45>: call 0x401520 <Swap(int*, int*)>
0x00401596 <+50>: lea eax,[esp+0x18]
0x0040159a <+54>: mov DWORD PTR [esp+0x4],eax
0x0040159e <+58>: lea eax,[esp+0x1c]
0x004015a2 <+62>: mov DWORD PTR [esp],eax
0x004015a5 <+65>: call 0x401542 <Swap(int&, int&)>
通过上面的反汇编代码,我们可以看出指针传递和引用传递在机制是一样的,都是将指针值(即地址)压入栈中,调用函数,然后恢复栈。Swap(nA, nB)和Swap(&nA, &nB);在实际上的汇编代码也基本上一模一样,都是从栈中取出地址来。由此可以看出引用和指针在效率上是一样的。这也是为什么指针和引用都可以达到多态的效果。指针传递和引用传递其实都是改变的地址指向的内存上的值来达到修改参数的效果。
值传递
下面是值传递对应的反汇编代码
// Test to pass by value
SetValue(nA);
0041374A mov eax,dword ptr [ebp-8]
0041374D push eax
0041374E mov ecx,dword ptr [nA]
00413751 push ecx
00413752 call SetValue (4111EAh)
00413757 add esp,8
因为我的机器是32位的CPU,从上面的汇编代码可以看64Bit的变量被分成2个32Bit的参数压入栈中。这也是我们常说的,值传递会形成一个拷贝。如果是一个自定义的结构类型,并且有很多参数,那么如果用值传递,这个结构体将被分割为非常多个32Bit的逐个拷贝到栈中去,这样的参数传递效率是非常慢的。所以结构体等自定义类型,都使用引用传递,如果不希望别人修改结构体变量,可以加上const修饰,如(const MY_STRUCT& _value);
下面来看一下Test001函数对应的反汇编代码的参数传递
__int64* _pArray = NULL;
004137E0 mov dword ptr [_pArray],0
// Test the pointer
GetMemory(_pArray);
mov eax,dword ptr [_pArray]
push eax
call GetMemory (411203h)
0041381B add esp,4
从上面的汇编代码可以看出,其实是0被压入到栈中作为参数,所以GetMemory(_pArray)无论做什么事,其实都与指针变量_pArray无关。GetMemory()分配的空间是让栈中的临时变量指向的,当函数退出时,栈得到恢复,结果申请的空间没有人管,就产生内存泄露的问题了。《C++ Primer》将参数传递分为引用传递和非引用传递两种,非引用传递其实可以理解为值传递。这样看来,指针传递在某种意义上也是值传递,因为传递的是指针的值(1个4BYTE的值)。值传递都不会改变传入实参的值的。而且普通的指针传递其实是改变的指针变量指向的内容。
下面再看一下Test002函数对应的反汇编代码的参数传递
__int64* _pArray = NULL;
004137E0 mov dword ptr [_pArray],0
GetMemory(&_pArray);
004137E7 lea eax,[_pArray]
004137EA push eax
004137EB call GetMemory (4111FEh)
004137F0 add esp,4
从上面的汇编代码lea eax,[_pArray] 可以看出,_pArray的地址被压入到栈中去了。
然后看一看GetMemory(&_pArray)的实现汇编代码。
0x0040159b <+0>: push ebp
0x0040159c <+1>: mov ebp,esp
0x0040159e <+3>: sub esp,0x18
0x004015a1 <+6>: mov DWORD PTR [esp],0x20
0x004015a8 <+13>: call 0x473ef0 <_Znaj>
0x004015ad <+18>: mov edx,DWORD PTR [ebp+0x8]
0x004015b0 <+21>: mov DWORD PTR [edx],eax
0x004015b2 <+23>: leave
0x004015b3 <+24>: ret
蓝色的代码是分配临时变量空间,然后调用分配空间函数分配空间,得到的空间指针即eax.
然后红色的汇编代码即从ebp+0x8的栈上取到上面压入栈中的参数_pArray的地址.
mov DWORD PTR [edx],eax即相当于把分配的空间指针eax让edx指向,也即让_pArray指向分配的空间eax.
总之,无论是哪种参数传递方式,参数都是通过栈上的临时变量来间接参与到被调用函数的。指针作为参数,其本身的值是不可能被改变的,能够改变的是其指向的内容。引用是通过指针来实现的,所以引用和指针在效率上一样的。

② linux C里面的write函数的第一个参数是怎么判断的

write 函数的第一个参数是 open 函数返回的文件描述符,和windows里一般文件的句柄是对应的,
这段代码中之所以这么用,是因为该程序是从控制台shell启动的,是shell的子进程,他继承了shell默认打开了文件描述符0 1 2 。
0 1 2 分别对应 标准输入 标准输出 标准出错,(在没有重定向、管道的情况下 对应了键盘 显示器 显示器)。

③ linux c 编译加什么参数 可以gdb

Linux C编译命令是gcc,gcc加上一个-g参数,编译出来的程序才能带有gdb的调试信息,才能用gdb调试。不光是gcc编译命令,编译C++程序的g++命令也是用-g选项来表示编译出的程序要带上gdb调试信息。

④ linux c变参函数参数类型不同怎么办

写一个简单的可变参数的C函数

下面我们来探讨如何写一个简单的可变参数的C函数.写可变参数的
C函数要在程序中用到以下这些宏:
void va_start( va_list arg_ptr, prev_param );

type va_arg( va_list arg_ptr, type );

void va_end( va_list arg_ptr );
va在这里是variable-argument(可变参数)的意思.
这些宏定义在stdarg.h中,所以用到可变参数的程序应该包含这个
头文件.下面我们写一个简单的可变参数的函数,改函数至少有一个整数
参数,第二个参数也是整数,是可选的.函数只是打印这两个参数的值.
void simple_va_fun(int i, ...)
{
va_list arg_ptr;
int j=0;

va_start(arg_ptr, i);
j=va_arg(arg_ptr, int);
va_end(arg_ptr);
printf("%d %d\n", i, j);
return;
}
我们可以在我们的头文件中这样声明我们的函数:
extern void simple_va_fun(int i, ...);
我们在程序中可以这样调用:
simple_va_fun(100);
simple_va_fun(100,200);
从这个函数的实现可以看到,我们使用可变参数应该有以下步骤:
1)首先在函数里定义一个va_list型的变量,这里是arg_ptr,这个变
量是指向参数的指针.
2)然后用va_start宏初始化变量arg_ptr,这个宏的第二个参数是第
一个可变参数的前一个参数,是一个固定的参数.
3)然后用va_arg返回可变的参数,并赋值给整数j. va_arg的第二个
参数是你要返回的参数的类型,这里是int型.
4)最后用va_end宏结束可变参数的获取.然后你就可以在函数里使
用第二个参数了.如果函数有多个可变参数的,依次调用va_arg获
取各个参数.
如果我们用下面三种方法调用的话,都是合法的,但结果却不一样:
1)simple_va_fun(100);
结果是:100 -123456789(会变的值)
2)simple_va_fun(100,200);
结果是:100 200
3)simple_va_fun(100,200,300);
结果是:100 200
我们看到第一种调用有错误,第二种调用正确,第三种调用尽管结果
正确,但和我们函数最初的设计有冲突.下面一节我们探讨出现这些结果
的原因和可变参数在编译器中是如何处理的.

(二)可变参数在编译器中的处理

我们知道va_start,va_arg,va_end是在stdarg.h中被定义成宏的,
由于1)硬件平台的不同 2)编译器的不同,所以定义的宏也有所不同,下
面以VC++中stdarg.h里x86平台的宏定义摘录如下(’\’号表示折行):

typedef char * va_list;

#define _INTSIZEOF(n) \
((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) )

#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )

#define va_arg(ap,t) \
( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )

#define va_end(ap) ( ap = (va_list)0 )

定义_INTSIZEOF(n)主要是为了某些需要内存的对齐的系统.C语言的函
数是从右向左压入堆栈的,图(1)是函数的参数在堆栈中的分布位置.我
们看到va_list被定义成char*,有一些平台或操作系统定义为void*.再
看va_start的定义,定义为&v+_INTSIZEOF(v),而&v是固定参数在堆栈的
地址,所以我们运行va_start(ap, v)以后,ap指向第一个可变参数在堆
栈的地址,如图:

高地址|-----------------------------|
|函数返回地址 |
|-----------------------------|
|....... |
|-----------------------------|
|第n个参数(第一个可变参数) |
|-----------------------------|<--va_start后ap指向
|第n-1个参数(最后一个固定参数)|
低地址|-----------------------------|<-- &v
图( 1 )

然后,我们用va_arg()取得类型t的可变参数值,以上例为int型为例,我
们看一下va_arg取int型的返回值:
j= ( *(int*)((ap += _INTSIZEOF(int))-_INTSIZEOF(int)) );
首先ap+=sizeof(int),已经指向下一个参数的地址了.然后返回
ap-sizeof(int)的int*指针,这正是第一个可变参数在堆栈里的地址
(图2).然后用*取得这个地址的内容(参数值)赋给j.

高地址|-----------------------------|
|函数返回地址 |
|-----------------------------|
|....... |
|-----------------------------|<--va_arg后ap指向
|第n个参数(第一个可变参数) |
|-----------------------------|<--va_start后ap指向
|第n-1个参数(最后一个固定参数)|
低地址|-----------------------------|<-- &v
图( 2 )

最后要说的是va_end宏的意思,x86平台定义为ap=(char*)0;使ap不再
指向堆栈,而是跟NULL一样.有些直接定义为((void*)0),这样编译器不
会为va_end产生代码,例如gcc在linux的x86平台就是这样定义的.
在这里大家要注意一个问题:由于参数的地址用于va_start宏,所
以参数不能声明为寄存器变量或作为函数或数组类型.
关于va_start, va_arg, va_end的描述就是这些了,我们要注意的
是不同的操作系统和硬件平台的定义有些不同,但原理却是相似的.

(三)可变参数在编程中要注意的问题

因为va_start, va_arg, va_end等定义成宏,所以它显得很愚蠢,
可变参数的类型和个数完全在该函数中由程序代码控制,它并不能智能
地识别不同参数的个数和类型.
有人会问:那么printf中不是实现了智能识别参数吗?那是因为函数
printf是从固定参数format字符串来分析出参数的类型,再调用va_arg
的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通
过在自己的程序里作判断来实现的.
另外有一个问题,因为编译器对可变参数的函数的原型检查不够严
格,对编程查错不利.如果simple_va_fun()改为:
void simple_va_fun(int i, ...)
{
va_list arg_ptr;
char *s=NULL;

va_start(arg_ptr, i);
s=va_arg(arg_ptr, char*);
va_end(arg_ptr);
printf("%d %s\n", i, s);
return;
}
可变参数为char*型,当我们忘记用两个参数来调用该函数时,就会出现
core mp(Unix) 或者页面非法的错误(window平台).但也有可能不出
错,但错误却是难以发现,不利于我们写出高质量的程序.
以下提一下va系列宏的兼容性.
System V Unix把va_start定义为只有一个参数的宏:
va_start(va_list arg_ptr);
而ANSI C则定义为:
va_start(va_list arg_ptr, prev_param);
如果我们要用system V的定义,应该用vararg.h头文件中所定义的
宏,ANSI C的宏跟system V的宏是不兼容的,我们一般都用ANSI C,所以
用ANSI C的定义就够了,也便于程序的移植.

热点内容
压缩内存软件 发布:2025-01-31 16:51:39 浏览:145
脚本lcd 发布:2025-01-31 16:41:02 浏览:515
安卓selinux干什么用的 发布:2025-01-31 16:32:04 浏览:531
侠盗猎车手加钱密码是多少 发布:2025-01-31 15:44:28 浏览:662
没密码怎么登微信 发布:2025-01-31 15:33:51 浏览:737
c语言死机程序 发布:2025-01-31 15:07:52 浏览:18
编程教育装修 发布:2025-01-31 15:04:38 浏览:402
函数和存储过程的区别 发布:2025-01-31 14:39:12 浏览:610
地下室柱子箍筋的加密 发布:2025-01-31 14:36:11 浏览:935
手机拍摄视频在哪个文件夹 发布:2025-01-31 14:34:28 浏览:761