c语言函数内部参数是怎么运算的?"函数内部参数的运算顺序是自右向左运算的"有这样一句话
又举c语言函数参数的运算了例子c语言函数参数的运算:
void
main()
{
int
i=3;printf("%d,%d",i,i);}
输出结果是
3c语言函数参数的运算,3
具体运算顺序要看各各运算符的优先级i输出的是3
要是i输出的就是4
C语言函数参数汇编代码gcc:
第一个问题的汇编:
movl $0, 離
incl 離
incl 離
incl 離
movl 離, 8(%esp)
movl 離, 4(%esp)
movl 離, (%esp)
call _func
汇编显示,先将n自增了3次变为3,然后调用函数,因此都是3
第二个问题的汇编:
movl $3, 16(%esp)
movl $98, 12(%esp)
movl $2, 8(%esp)
movl $97, 4(%esp)
movl $1, (%esp)
call _func2
可以看出参数对应的栈地址应该是连续的
另外,这个程序在VC6下的结果完全不一样:
func for n
000
12ff2c12ff3012ff34
func forn
321
12ff2c12ff3012ff34
func2
12ff2412ff2812ff2c12ff3012ff34
但是最后的地址也是连续的 。
此类问题知道上问过多次了 , 可以说没有什么意义 , 结果与编译器实现相关,实际编程没有用处 , 除非写代码的人喜欢玩花俏
c语言中用函数做参数怎么用数组元素就是下标变量,它与普通变量并无区别 。因此它作为函数实参使用与普通变量是完全相同的 , 在发生函数调用时,把作为实参的数组元素的值传送给形参 , 实现单向的值传送 。【例5-4】说明了这种情况 。
【例8-7】判别一个整数数组中各元素的值,若大于0 则输出该值 , 若小于等于0则输出0值 。编程如下:#include stdio.hvoid nzp(int v){if(v0)printf("%d ",v);elseprintf("%d ",0);}int main(void){int a[5],i;printf("input 5 numbers\n");for(i=0;i5;i){scanf("%d",a[i]);nzp(a[i]);}return 0;}
本程序中首先定义一个无返回值函数nzp,并说明其形参v为整型变量 。在函数体中根据v值输出相应的结果 。在main函数中用一个for语句输入数组各元素,每输入一个就以该元素作实参调用一次nzp函数,即把a[i]的值传送给形参v,供nzp函数使用 。
数组名作为函数参数
用数组名作函数参数与用数组元素作实参有几点不同 。
1) 用数组元素作实参时,只要数组类型和函数的形参变量的类型一致 , 那么作为下标变量的数组元素的类型也和函数形参变量的类型是一致的 。因此,并不要求函数的形参也是下标变量 。换句话说,对数组元素的处理是按普通变量对待的 。用数组名作函数参数时,则要求形参和相对应的实参都必须是类型相同的数组 , 都必须有明确的数组说明 。当形参和实参二者不一致时,即会发生错误 。
2) 在普通变量或下标变量作函数参数时 , 形参变量和实参变量是由编译系统分配的两个不同的内存单元 。在函数调用时发生的值传送是把实参变量的值赋予形参变量 。在用数组名作函数参数时,不是进行值的传送,即不是把实参数组的每一个元素的值都赋予形参数组的各个元素 。因为实际上形参数组并不存在,编译系统不为形参数组分配内存 。那么,数据的传送是如何实现的呢?在我们曾介绍过 , 数组名就是数组的首地址 。因此在数组名作函数参数时所进行的传送只是地址的传送,也就是说把实参数组的首地址赋予形参数组名 。形参数组名取得该首地址之后,也就等于有了实在的数组 。实际上是形参数组和实参数组为同一数组,共同拥有一段内存空间 。
上图说明了这种情形 。图中设a为实参数组,类型为整型 。a占有以2000为首地址的一块内存区 。b为形参数组名 。当发生函数调用时 , 进行地址传送,把实参数组a的首地址传送给形参数组名b,于是b也取得该地址2000 。于是a , b两数组共同占有以2000为首地址的一段连续内存单元 。从图中还可以看出a和b下标相同的元素实际上也占相同的两个内存单元(整型数组每个元素占二字节) 。例如a[0]和b[0]都占用2000和2001单元,当然a[0]等于b[0] 。类推则有a[i]等于b[i] 。
【例8-8】数组a中存放了一个学生5门课程的成绩,求平均成绩 。#include stdio.hfloat aver(float a[5]){int i;float av,s=a[0];for(i=1;i5;i)s=s a[i];av=s/5;return av;}int main(void){float sco[5],av;int i;printf("\ninput 5 scores:\n");for(i=0;i5;i)scanf("%f",sco[i]);av=aver(sco);printf("average score is %5.2f",av);return 0;}
本程序首先定义了一个实型函数aver,有一个形参为实型数组a , 长度为5 。在函数aver中,把各元素值相加求出平均值 , 返回给主函数 。主函数main 中首先完成数组sco的输入,然后以sco作为实参调用aver函数,函数返回值送av , 最后输出av值 。从运行情况可以看出 , 程序实现了所要求的功能 。
3) 前面已经讨论过,在变量作函数参数时 , 所进行的值传送是单向的 。即只能从实参传向形参,不能从形参传回实参 。形参的初值和实参相同,而形参的值发生改变后,实参并不变化,两者的终值是不同的 。而当用数组名作函数参数时,情况则不同 。由于实际上形参和实参为同一数组,因此当形参数组发生变化时 , 实参数组也随之变化 。当然这种情况不能理解为发生了“双向”的值传递 。但从实际情况来看,调用函数之后实参数组的值将由于形参数组值的变化而变化 。为了说明这种情况,把【例5.4】改为【例5.6】的形式 。
【例8-9】题目同【例8.7】 。改用数组名作函数参数 。#include stdio.hvoid nzp(int a[5]){int i;printf("\nvalues of array a are:\n");for(i=0;i5;i){if(a[i]0) a[i]=0;printf("%d ",a[i]);}}int main(void){int b[5],i;printf("\ninput 5 numbers:\n");for(i=0;i5;i)scanf("%d",b[i]);printf("initial values of array b are:\n");for(i=0;i5;i)printf("%d ",b[i]);nzp(b);printf("\nlast values of array b are:\n");for(i=0;i5;i)printf("%d ",b[i]);return 0;}
本程序中函数nzp的形参为整数组a,长度为5 。主函数中实参数组b也为整型,长度也为5 。在主函数中首先输入数组b的值 , 然后输出数组b的初始值 。然后以数组名b为实参调用nzp函数 。在nzp中,按要求把负值单元清0,并输出形参数组a的值 。返回主函数之后 , 再次输出数组b的值 。从运行结果可以看出,数组b的初值和终值是不同的,数组b的终值和数组a是相同的 。这说明实参形参为同一数组 , 它们的值同时得以改变 。
用数组名作为函数参数时还应注意以下几点:
①形参数组和实参数组的类型必须一致,否则将引起错误 。
②形参数组和实参数组的长度可以不相同 , 因为在调用时,只传送首地址而不检查形参数组的长度 。当形参数组的长度与实参数组不一致时 , 虽不至于出现语法错误(编译能通过),但程序执行结果将与实际不符,这是应予以注意的 。
【例8.10】如把例8.9修改如下:#include stdio.hvoid nzp(int a[8]){int i;printf("\nvalues of array aare:\n");for(i=0;i8;i){if(a[i]0)a[i]=0;printf("%d ",a[i]);}}int main(void){int b[5],i;printf("\ninput 5 numbers:\n");for(i=0;i5;i)scanf("%d",b[i]);printf("initial values of array b are:\n");for(i=0;i5;i)printf("%d ",b[i]);nzp(b);printf("\nlast values of array b are:\n");for(i=0;i5;i)printf("%d ",b[i]);return 0;}
本程序与【例8.9】程序比,nzp函数的形参数组长度改为8,函数体中,for语句的循环条件也改为i8 。因此 , 形参数组a和实参数组b的长度不一致 。编译能够通过 , 但从结果看,数组a的元素a[5]、a[6]、a[7]显然是无意义的 。
③在函数形参表中,允许不给出形参数组的长度 , 或用一个变量来表示数组元素的个数 。例如,可以写为:
void nzp(int a[])
或写为
void nzp( int a[], int n )
其中形参数组a没有给出长度,而由n值动态地表示数组的长度 。n的值由主调函数的实参进行传送 。由此,【例8-10】又可改为【例8-11】的形式 。
【例8-11】复制纯文本新窗口
#include stdio.hvoid nzp(int a[],int n){int i;printf("\nvalues of array a are:\n");for(i=0;in;i){if(a[i]0) a[i]=0;printf("%d ",a[i]);}}int main(void){int b[5],i;printf("\ninput 5 numbers:\n");for(i=0;i5;i)scanf("%d",b[i]);printf("initial values of array b are:\n");for(i=0;i5;i)printf("%d ",b[i]);nzp(b,5);printf("\nlast values of array b are:\n");for(i=0;i5;i)printf("%d ",b[i]);return 0;}
C语言参数中表达式计算顺序问题C语言的运算符主要用于构成表达式,同一个符号在不同的表达式中,其作用并不一致 。下面按计算的优先顺序,分别说明不同作用的表达式 。需要特别指出,在C语言标准中,并没有结合性的说法 。
相同优先级运算符,从左至右依次运算 。注意后缀运算优先级高于前缀 。因此i应解释为(i) 。
而与或非的运算优先级都不一样,因此ab || bc解释为(ab) || (bc)
合理使用优先级可以极大简化表达式 。
基本表达式 1级
基本表达式(Primary expressions) , 主要是用于运算符之间,做为运算数 。
标识 , 常量 , 字符串文字量,优先级提升表达式最优先执行 。
优先级提升表达式是指圆括号包围的表达式,如“( expression )”
后缀表达式 2极
postfix-expression [ expression ],数组下标运算 。
postfix-expression ( argument-expression-list),函数调用,括号内的参数可选 。
postfix-expression . identifier , 成员访问,
postfix-expression - identifier,成员访问,-号之前应为指针 。
postfix-expression,后缀自增
postfix-expression --,后缀自减
( type-name ) { initializer-list }
( type-name ) { initializer-list , } 复合初始化,C99后新增 。例如
int* a = (int[]) { 1, 2, 3 };
//等价于 int unamed[] = {1, 2, 3}; //unamed表示一个不可见的变量名 。
int* a = unamed;
单目/一元运算 3级
unary-expression 前缀自增
-- unary-expression 前缀自减
unary-operator cast-expression 单目转型表式式 , 包括 取地址 ,提领 * , 正号,负号- 位反~ 逻辑否! 。
sizeof unary-expression 求类型长度,对表达式求类型长度
sizeof ( type-name ) 求类型长度
强制类型表达式 4级
( type-name ) cast-expression,强制表达式成为type-name指定的类型 。
乘法表达式 5级
“ * ” 乘法运算符;“ / ”除法运算符;“ % ” 取余运算符 。
加法运算符 6级
“”加法运算符;“ - ”减法运算符 。
移位运算符 7级
左移运算符; 右移运算符 。
关系运算符 8级
、=、、=关系运算符 。
相等运算符 9级
“ == ”等于运算符;“ != ”不等于运算符 。
位与运算符 10级
“”按位与运算符
位异或运算符 11级
“ ∧ ”按位异或运算符(Bitwise exclusive OR operator) 。
位或运算符 12 级
“ | ”按位或运算符(Bitwise inclusive OR operator) 。
逻辑与运算符 13级
“”逻辑与运算符 。
逻辑或运算符 14 级
“ || ”逻辑或运算符 。
三元条件运算符 15级
? :条件运算符 。
赋值运算符 16 级
=、=、 -=、 *=、 /=、 %=、 =、 ^=、 |=、 =、 =赋值运算符 。
逗号运算符 17级
“,”逗号运算符 。
[pre]C 语言中,逗号(,)也可以是运算符,称为逗号运算符(Comma Operator) 。逗号运算符可以把两个以上(包含两个)的表达式连接成一个表达式 , 称为逗号表达式 。其一般形式为:
子表达式1, 子表达式2, ..., 子表达式n
例如:
ab, c = b, c
逗号运算符的优先级是所有运算符中级别最低的 , 通常配合 for 循环使用 。逗号表达式最右边的子表达式的值即为逗号表达式的值 。上例中,c的值(c 自增之前的值)即为该表达式的值 。
逗号运算符保证左边的子表达式运算结束后才进行右边的子表达式的运算 。也就是说,逗号运算符是一个序列点,其左边所有副作用都结束后,才对其右边的子表达式进行运算 。因此,上例中 , c 得到 b 的值后,才进行自增运算 。
优先级:C语言中,运算符的运算优先级共分为15 级 。1 级最高 , 15 级最低 。在表达式中,优先级较高的先于优先级较低的进行运算 。而在一个运算量两侧的运算符 优先级相同时,则按运算符的结合性所规定的结合方向处理 。
结合性:C语言中各运算符的结合性分为两种,即左结合性(自左至右)和右结合性(自右至左) 。例如算术运算符的结合性是自左至右,即先左后右 。如有表达式x-y z 则y 应先与“-”号结合,执行x-y 运算 , 然后再执行 z 的运算 。这种自左至右的结合 方向就称为“左结合性” 。而自右至左的结合方向称为“右结合性” 。最典型的右结合 性运算符是赋值运算符 。如x=y=z,由于“=”的右结合性,应先执行y=z 再执行x=(y=z)运算 。C语言运算符中有不少为右结合性,应注意区别,以避免理解错误 。
优先级从上到下依次递减 , 最上面具有最高的优先级,逗号操作符具有最低的优先级 。
所有的优先级中,只有三个优先级是从右至左结合的,它们是单目运算符、条件运算符、赋值运算符 。其它的都是从左至右结合 。
具有最高优先级的其实并不算是真正的运算符,它们算是一类特殊的操作 。()是与函数相关,[]与数组相关,而-及.是取结构成员 。
其次是单目运算符,所有的单目运算符具有相同的优先级 , 因此在我认为的 真正的运算符中它们具有最高的优先级,又由于它们都是从右至左结合的,因此*p与*(p)等效是毫无疑问的 。
另外在C语言里,没有前置后置之分,因为-- 是右结合所以右侧优先运算,表现为 "操作数后置优先级比较高" 的假象,前置和后置的区分是因为运算符重载而后加入C的
接下来是算术运算符 , *、/、%的优先级当然比 、-高了 。
移位运算符紧随其后 。
其次的关系运算符中,==要比 == !=高一个级别,不大好理解 。
所有的逻辑操作符都具有不同的优先级(单目运算符除外 , !和~)
逻辑位操作符的"与"比"或"高 , 而"异或"则在它们之间 。
跟在其后的比||高 。
接下来的是条件运算符,赋值运算符及逗号运算符 。
在C语言中,只有4个运算符规定了运算方向,它们是、| |、条件运算符及赋值运算符 。
、| |都是先计算左边表达式的值,当左边表达式的值能确定整个表达式的值时,就不再计算右边表达式的值 。如 a = 0b; 运算符的左边位0,则右边表达式b就不再判断 。
在条件运算符中 。如a?b:c;先判断a的值,再根据a的值对b或c之中的一个进行求值 。
赋值表达式则规定先对右边的表达式求值 , 因此使 a = b = c = 6;成为可能 。
口诀注释
优先级等级口诀
圆方括号、箭头一句号, 自增自减非反负、针强地址长度,
乘除 , 加减,再移位,
小等大等、等等不等,
八位与,七位异,六位或 , 五与,四或,三疑 , 二赋 , 一真逗 。
其中“,”号为一个等级分段 。
优先级等级注释
“圆方括号、箭头一句号”指的是第15级的运算符 。其中圆方括号很明显“()、[]”,箭头 指的是指向结构体成员运算符“-”,句号 指的是结构体成员运算符“.” ;
“自增自减非反负、针强地址长度”指的是第14级的运算符 。其中 非 指的是逻辑运算符“!”,反 指的是按位取反运算符“~”,负 指的是负号运算符“-” , 针 指的是指针运算符“*”,强 指的是强制类型转换运算符,地址 指的是地址运算符“”,长度 指的是长度运算符“sizeof ”;
“乘除,加减 , 再移位”移位指的是左移运算符“”和右移运算符“”,其中除法还包括了 取余运算符“%”;
“小等大等、等等不等” 指的是第10级到第9级的运算符:、=、和=,等等指的是等于运算符==,不等指的是不等于运算符!=
“八位与 , 七位异,六位或”其中 八位与 指的是第8级的 按位与 运算符“”,七位异 指的是第7级的按位异或运算符“^”,六位或 指的是第6级的按位或运算符“|”;
“五与,四或”指的是第5级、第4级的逻辑与运算符“”和逻辑或运算符“||”;
“三疑 , 二赋,一真逗”指的是第3级到第1级的运算符 。其中,三疑指的是条件运算符“?:” (三有双重含义:即指优先级别是三,它的运算符类型也是三目,疑也取“?”之意),二赋 指的是赋值运算符=、 =、-=、*=、/=、%=、=、=、=、^=和|=,一真逗 指的是第1级的“ , ”运算符,真字只是为了语句需要罢了 。
由于C语言的运算符优先级与C的不完全一样(主要是增加了几个运算符),所以这个口诀不能完全实用于C.但是应该能够兼容 , 大家可以比较一下他们的区别应该就能够很快掌握C的优先级的!
应用举例
1、赋值运算符:a=5;
a=b=0;
第一个赋值语句把5赋给变量a;第二个赋值语句的意思是把0同时赋值给两个变量 。这是因为赋值语句是从右向左运算的,也就是说从右端开始计算,先b=0,然后a=b 。
2、复合赋值运算符:a=1;a =3;
上面第二个赋值语句等价于a=a 3;即a=4 。
3、算术运算符:Area=Height*Width;num=num1 num2/num3-num4;
第一个赋值语句Height和Width相乘结果赋给变量Area;第二个赋值语句先完成num2与num3的整除运算,然后与num1相加,再减去num4,结果赋给num 。运算符运算顺序先算乘除再算加减 。单目正和单目负最先运算 。
4、逻辑运算符:a=1,b=1;
a||b-1;
因为a=1为真值,所以不管b-1是不是真值,总的表达式一定为真值,这时后面的表达式就不会再计算了 。
5、关系运算符:if(a0)...
如果a0,则执行if语句中的内容,否则退出 。
6、条件运算符:a=(b0)?b:-b;
当b0时,a=b;当b不大于0时,a=-b;其实上面的意思就是把b的绝对值赋值给a 。
7、逗号运算符:b=2,c=7,d=5;
a=(b,c--,d 3);
有三个表达式,用逗号分开,所以最终的值应该是最后一个表达式的值,也就是d 3=8,所以a=8 。
8、位逻辑运算符
包括:1 。位与符 2 。|位或符 3 。^位异或符 4 。~位取反符
以操作数12为例 。位运算符将数字12视为1100 。位运算符将操作数视为位而不是数值 。数值
可以是任意进制的:十进制、八进制或十六进制 。位运算符则将操作数转化为二进制 , 并相应地返回1或0 。
位运算符将数字视为二进制值,并按位进行相应运算,运算完成后再重新转换为数字 。例如:
表达式1015表示(10101111),它将返回表示1010的值10 。因为真真得真,或者是11得1,同位全是1结果也是1
表达式10|15表示(1010 | 1111),它将返回表示1111的值15 。假假得假 。全零得零 。
表达式10^15表示(1010 ^ 1111), 它将返回表示0101的值5 。此时是同性相斥,相同的就为假 。
表达式~10表示(~1010) , 它将返回表示0101的值 -11 。此号好理解,按位取反 。
c语言的函数参数中 &与*操作符是地址运算c语言函数参数的运算,获得变量c语言函数参数的运算的存储地址;而*则是间接访问操作符c语言函数参数的运算,与指针一起使用,用于访问指针所指向的值,也就是说通过地址访问地址处所存储的变量 。
举个例子c语言函数参数的运算:
有变量a
=
8,存储在地址0xFFA0处 。
a,表示a存储的地址,为0xFFA0,
*(指向a的指针) , 表示a的值=8,这里括号内的“指向a的指针”其数值等于0xFFA0 。
void
fun(char
*p);
//这里的形式参数p是一个指针 , 所以c语言函数参数的运算你要传进来的是个指针变量,也就是地址
void
main(void)
{
char
a=8;
fun(a);
//所以这里你需要计算出地址,然后传给p
}
你可以这样理解,fun()调用时相当于
char
*p=a;
void
main(void)
{
char
a
=
8;
char
*pa;
pa
=
a;
fun(pa);
}
这与上面的表达是等价的 。
【c语言函数参数的运算 c语言 参数】c语言函数参数的运算的介绍就聊到这里吧,感谢你花时间阅读本站内容,更多关于c语言 参数、c语言函数参数的运算的信息别忘了在本站进行查找喔 。
推荐阅读
- 无人机答题直播,无人机在线题库
- cpu风扇什么情况下会坏,cpu风扇问题会导致自动关机吗
- sqlserver文件组,sqlserver文件组无效
- 直播伴侣手机开播设置,直播伴侣在手机上怎么用
- 放弃go语言知乎 go语言会取代java吗
- java写万年历代码截图,java万年历代码解析
- 电信意大利无服务器,意大利天天电信怎么样
- 西瓜视频直播主播赛排名,西瓜直播排行榜
- python学函数 python 数学函数