【C语言】操作符(运算符)
目录
1. 算数操作符
2. 移位操作符
2.1 左移操作符
2.1.1 1<<n=2^n
2.2 右移操作符
2.2.1 n>>1和n/2
3. 位操作符
3.1 按位与
3.1.1 n&1和n%2
3.1.2 n>>i&1
3.1.3 n&(n-1)
3.2 按位或
3.3 按位异或
3.3.1 a^a=0 0^a=a
4. 赋值操作符
5. 单目操作符
6. 关系操作符
7. 逻辑操作符
8. 条件操作符
9. 逗号表达式
10. 下标引用操作符
11. 函数调用操作符
12. 结构成员访问操作符
13. 操作符的属性
1. 算数操作符
+加法,-减法,*乘法,/除法,%取模(取余)
- %操作符的两个操作数必须为整数
#include <stdio.h>int main()
{printf("%d\n", 11 / 5);// 2printf("%d\n", -11 / 5);//-2printf("%d\n", 11 / -5);//-2printf("%d\n", -11 / -5);// 2return 0;
}
- +-*/四个操作符可以作用于整数和浮点数
- /操作符的两个操作数都为整数时,执行整数除法。只要有一个是浮点数,执行浮点数除法
整数除法和浮点数除法不同。浮点数除法的结果是浮点数,而整数除法的结果是整数。整数是没有小数部分的数。在C语言中,整数除法结果的小数部分被丢弃,这一过程被称为截断(fruncation)。
#include <stdio.h>int main()
{printf("%d\n", 7 / 2); // 3printf("%lf\n", 7. / 2); // 3.500000printf("%lf\n", 7 / 2.0);// 3.500000printf("%lf\n", 7.0 / 2.0);// 3.500000printf("%d\n", -7 / 2); //-3printf("%lf\n", 7 / -2.); //-3.500000printf("%lf\n", 7.0 / -2); //-3.500000printf("%lf\n", -7. / -2.); // 3.500000return 0;
}
2. 移位操作符
<<左移,>>右移
2.1 左移操作符
整数的二进制表示有3种:原码、反码、补码。正整数的原码、反码、补码相同。负整数的反码是原码符号位不变,其他位按位取反,补码是反码+1。整数在内存中存储的是补码。
//+7:
//原码:00000000000000000000000000000111
//反码:00000000000000000000000000000111
//补码:00000000000000000000000000000111
//-7:
//原码:10000000000000000000000000000111
//反码:11111111111111111111111111111000
//补码:11111111111111111111111111111001
移位规则:左边抛弃,右边补0。
int a = 7;
int b = a << 1;
0[00000000000000000000000000001110]
int a = -7;
int b = a << 1;
1[11111111111111111111111111110010]
2.1.1 1<<n=2^n
int a = 1;
int b = a << 1;
0[00000000000000000000000000000010]=2
int b = a << 2;
00[00000000000000000000000000000100]=2^2
int b = a << 3;
000[00000000000000000000000000001000]=2^3
所以,1<<n=2^n
2.2 右移操作符
移位规则:
- 逻辑移位:左边补0,右边抛弃。
- 算术移位:左边补原符号位,右边抛弃。(常见编译器都是算术移位)
算术移位:
int a = 7;
int b = a >> 1;
[00000000000000000000000000000011]1
int a = -7;
int b = a >> 1;
[11111111111111111111111111111100]1
2.2.1 n>>1和n/2
n为非负数时,n>>1=n/2 10>>1=5 10/2=5,0>>1=0 0/2=0
n为负偶数时,n>>1=n/2 -10>>1=-5 -10/2=-5
n为负奇数时,n>>1=n/2-1 -5>>1=-3 -5/2=-2(n>>1是n除以2向下取整,n/2是n除以2向上取整)
位运算比除法运算效率更高。
3. 位操作符
&按位与,|按位或,^按位异或
位操作符通过逐位比较两个运算对象,生成一个新值。对于每个位:
- &:两个操作数相应的位都为1,结果为1。
- |:两个操作数相应的位至少有一个为1,结果为1。
- ^:两个操作数相应的位相同为0,相异为1。
3.1 按位与
int a = 3;
int b = -5;
int c = a & b;
//+3的补码:00000000000000000000000000000011
//-5的补码:11111111111111111111111111111011
//c的补码: 00000000000000000000000000000011
3.1.1 n&1和n%2
n为非负数时,n&1=n%2
- n为奇数:n&1=1 n%2=1
- n为偶数:n&1=0 n%2=0
n为负数时,
- n为奇数:n&1=1 n%2=-1
- n为偶数:n&1=0 n%2=0
位运算比取余运算效率更高。
3.1.2 n>>i&1
n>>i&1用来获取二进制的每一位。
以75(000000000000000000000001001011)为例:
要想获取个位,就要&1,即
// 000000000000000000000001001011
//&000000000000000000000000000001
//=000000000000000000000000000001
//=1
要想获取十位,就要>>1,再&1,即
75>>1=0000000000000000000000001001011
// 000000000000000000000000100101
//&000000000000000000000000000001
//=000000000000000000000000000001
//=1
要想获取百位,就要>>2,再&1,即
75>>2=00000000000000000000000001001011
// 000000000000000000000000010010
//&000000000000000000000000000001
//=000000000000000000000000000000
//=0
获取一个整数二进制序列中所有的偶数位和奇数位,分别打印出二进制序列:
#include <stdio.h>int main()
{int n = 0;scanf("%d", &n);int i = 0;//奇数位for (i = 30; i >= 0; i -= 2){printf("%d ", n >> i & 1);}printf("\n");//偶数位for (i = 31; i >= 1; i -= 2){printf("%d ", n >> i & 1);}printf("\n");return 0;
}
3.1.3 n&(n-1)
n-1表示将n的二进制位最右边的1变为0,该1右边的所有0都变为1。
n&(n-1)表示将n的二进制位最右边的1变为0,其余位不变。
24: 00000000000000000000000000011000
23: 00000000000000000000000000010111
24&23:00000000000000000000000000010000
如果n&(n-1)为0,则n是2的幂。
+2: 00000000000000000000000000000010
+1: 00000000000000000000000000000001
+4: 00000000000000000000000000000100
+3: 00000000000000000000000000000011
2&1:00000000000000000000000000000000
4&3:00000000000000000000000000000000
题目:给定一个非负整数n,请计算0到n之间的每个数字的二进制表示中1的个数,并输出一个数组。
以下题解都是C++接口。
方法一:
vector<int> countBits(int n)
{vector<int> ret(n + 1, 0);//创建n+1个元素的数组,将所有元素初始化为0//0的二进制形式中没有1,现在ret[0]的值已经为0,所以可以从1开始计算for (int i = 1; i <= n; i++){int n = i;while (n){ret[i]++;//每进行一次n&(n-1)的操作,就能去掉最后一个1,去掉所有的1时(n为0时)结束循环//二进制位1的个数=循环的次数n = n & (n - 1);}}return ret;
}
如果一个整数共有k位,对于每个整数,while循环最多循环k次,每次循环的时间复杂度为O(1),因此上述代码的时间复杂度为O(kn)。
方法二:
vector<int> countBits(int n)
{vector<int> ret(n + 1, 0);//创建n+1个元素的数组,将所有元素初始化为0//0的二进制形式中没有1,现在ret[0]的值已经为0,所以可以从1开始计算for (int i = 1; i <= n; i++){//i的二进制形式中1的个数比i&(i-1)的二进制形式中1的个数多1个ret[i] = ret[i & (i - 1)] + 1;}return ret;
}
上述代码的时间复杂度为O(n)。
方法三:
如果正整数i是偶数,那么i相当于将i/2左移一位的结果,因此偶数i和i/2的二进制形式中1的个数是相同的。
如果正整数i是奇数,那么i相当于将i/2左移一位后,再将最右边一位设为1的结果,因此奇数i的二进制形式中1的个数比i/2的二进制形式中1的个数多1个。
例如,
3:00000000000000000000000000000011
6:00000000000000000000000000000110
7:00000000000000000000000000000111
vector<int> countBits(int n)
{vector<int> ret(n + 1, 0);//创建n+1个元素的数组,将所有元素初始化为0//0的二进制形式中没有1,现在ret[0]的值已经为0,所以可以从1开始计算for (int i = 1; i <= n; i++){//ret[i] = ret[i / 2] + (i % 2);//当i为非负数时,i/2=i>>1,i%2=i&1,且位运算比除法运算和取余运算效率高//代码优化如下:ret[i] = ret[i >> 1] + (i & 1);}return ret;
}
上述代码的时间复杂度为O(n)。
3.2 按位或
int a = 3;
int b = -5;
int c = a | b;
//+3的补码:00000000000000000000000000000011
//-5的补码:11111111111111111111111111111011
//c的补码: 11111111111111111111111111111011
3.3 按位异或
int a = 3;
int b = -5;
int c = a ^ b;
//+3的补码:00000000000000000000000000000011
//-5的补码:11111111111111111111111111111011
//c的补码: 11111111111111111111111111111000
两个整型m和n的二进制表达中,有多少个位不同:
#include <stdio.h>int main()
{int m = 0;int n = 0;scanf("%d %d", &m, &n);int ret = m ^ n;//异或运算:相同为0,相异为1//计算ret的二进制位1的个数,就是m和n的二进制位相异数int count = 0;while (ret){ret = ret & (ret - 1);count++;}printf("%d\n", count);return 0;
}
3.3.1 a^a=0 0^a=a
a^a=0:
如,3^3=00000011^00000011=00000000
0^a=a:
如,0^3=00000000^00000011=00000011
不创建临时变量实现两个数的交换:
#include <stdio.h>int main()
{int a = 10;int b = 20;a = a ^ b;//10^20b = a ^ b;//10^20^20=10^0=10(^操作符支持交换律)a = a ^ b;//10^20^10=0^20=20printf("a = %d b = %d\n", a, b);return 0;
}
4. 赋值操作符
=赋值
复合赋值符:+=,-=,*=,/=,%=,<<=,>>=,&=,|=,^=
x += 10;//等价于x = x + 10;
赋值表达式语句的目的是把值储存到内存位置上。用于储存值的数据存储区域统称为数据对象(data object)。C标准只有在提到这个概念时才会用到对象这个术语。使用变量名是标识对象的一种方法。除此之外,还有其他方法。例如,可以指定数组的元素、结构的成员,或者使用指针表达式(指针中储存的是它所指向对象的地址)。左值(lvalue)是C语言的术语,用于标识特定数据对象的名称或表达式。因此,对象指的是实际的数据存储,而左值是用于标识或定位存储位置的标签。
对于早期的C语言,提到左值意味着:
- 它指定一个对象,所以引用内存中的地址;
- 它可用在赋值运算符的左侧,左值(lvalue)中的l源自left。
但是后来,标准中新增了const限定符。用const创建的变量不可修改。因此,const标识符满足上面的第1项,但是不满足第2项。一方面C继续把标识对象的表达式定义为左值,一方面某些左值却不能放在赋值运算符的左侧。有些左值不能用于赋值运算符的左侧。此时,标准对左值的定义已经不能满足当前的状况。
为此,C标准新增了一个术语:可修改的左值(modifiable lvalue),用于标识可修改的对象。所以,赋值运算符的左侧应该是可修改的左值。当前标准建议,使用术语对象定位值(object locator value)更好。
右值(rvalue)指的是能赋值给可修改左值的量,且本身不是左值。例如,考虑下面的语句:
bmw = 2002;
这里,bmw是可修改的左值,2002是右值。右值中的r源自right。右值可以是常量、变量或其他可求值的表达式(如,函数调用)。实际上,当前标准在描述这一概念时使用的是表达式的值(value of an expression),而不是右值。
我们看几个简单的示例:
int ex;
int why;
int zee;
const int TwO = 2;
why = 42;
zee = why;
ex = Two * (why + zee);
这里,ex、why和zee都是可修改的左值(或对象定位值),它们可用于赋值运算符的左侧和右侧。Two是不可改变的左值,它只能用于赋值运算符的右侧(在该例中,TWO被初始化为2,这里的=运算符表示初始化而不是赋值,因此并未违反规则)。同时,42是右值,它不能引用某指定内存位置。另外,why和zee是可修改的左值,表达式(why + zee)是右值,该表达式不能表示特定内存位置,而且也不能给它赋值。它只是程序计算的一个临时值,在计算完毕后便会被丢弃。
在学习名称时,被称为“项”(如,赋值运算符左侧的项)的就是运算对象(operand)。运算对象是运算符操作的对象。例如,可以把吃汉堡描述为:“吃”运算符操作“汉堡”运算对象。类似地可以说,=运算符的左侧运算对象应该是可修改的左值。
C的基本赋值运算符有些与众不同:
/*golf.c -- 高尔夫锦标赛记分卡*/
#include <stdio.h>
int main()
{int jane, tarzan, cheeta;cheeta = tarzan = jane = 68;printf(" cheeta tarzan jane\n" ) ;printf("First round score %4d %8d %8d\n", cheeta, tarzan, jane);return 0;
}
许多其他语言都会回避该程序中的三重赋值,但是C完全没问题。赋值的顺序是从右往左:首先把86赋给jane,然后再赋给tarzan,最后赋给cheeta。因此,程序的输出如下:
5. 单目操作符
单目就是一元的意思,只有一个运算对象。
!逻辑反操作
-负值,+正值
&取地址,*间接访问操作符(解引用操作符)
sizeof计算变量或类型所创建的变量的长度(单位:字节),返回size_t类型的值
~对一个数的二进制按位取反
++自增,--自减(前置++:先++,后使用,后置++:先使用,后++)
(类型)强制类型转换
6. 关系操作符
>大于,>=大于或等于,<小于,<=小于或等于,!=不相等,==相等
7. 逻辑操作符
&&逻辑与:两个表达式全为真,整个表达式为真
||逻辑或:两个表达式有一个为真,整个表达式为真
短路原则:
exp1&&exp2:
当exp1为真时,再判断exp2的真假,来确定整个表达式的真假;
当exp1为假时,可以确定整个表达式为假,就不用判断exp2的真假(即不执行exp2)。
exp1||exp2:
当exp1为真时,可以确定整个表达式为真,就不用判断exp2的真假(即不执行exp2);
当exp1为假时,再判断exp2的真假,来确定整个表达式的真假。
#include <stdio.h>int main()
{int a = 1;int b = 2;if (a == 10 && b-- == 2)//a == 10为假,可以确定整个表达式为假,不执行b-- == 2printf("a=%d b=%d\n", a, b);elseprintf("a=%d b=%d\n", a, b);//a=1 b=2int c = 3;int d = 4;if (c < 10 || d++ == 4)//c < 10为真,可以确定整个表达式为真,不执行d++ == 4printf("c=%d d=%d\n", c, d);//c=3 d=4elseprintf("c=%d d=%d\n", c, d);return 0;
}
8. 条件操作符
exp1?exp2:exp3
条件操作符是唯一的三目操作符。先求解exp1,若其值为真,则将exp2的值作为整个表达式的取值,否则将exp3的值作为整个表达式的取值。
9. 逗号表达式
exp1,exp2,exp3,...,expN
逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果。
10. 下标引用操作符
[]下标引用操作符
p[i]=*(p+i) p[i][j]=*(p[i]+j)=*(*(p+i)+j)
11. 函数调用操作符
接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数。
如,test(),Add(a,b)。
12. 结构成员访问操作符
. 结构体.成员名
->结构体指针->成员名
#include <stdio.h>struct Peo
{char name[20];char tele[12];char sex[5];int age;
};struct Stu
{struct Peo p;char id[20];double score;
};int main()
{struct Stu s = { { "张三","123456","男",20 },"8888",98.5 };printf("%s %s %s %d %s %lf\n", s.p.name, s.p.tele, s.p.sex, s.p.age, s.id, s.score);return 0;
}
//张三 123456 男 20 8888 98.500000
#include <stdio.h>struct Peo
{char name[20];char tele[12];char sex[5];int age;
};struct Stu
{struct Peo p;char id[20];double score;
};void print(struct Stu* ps)
{printf("%s %s %s %d %s %lf\n", (*ps).p.name, (*ps).p.tele, (*ps).p.sex, (*ps).p.age, (*ps).id, (*ps).score);printf("%s %s %s %d %s %lf\n", ps->p.name, ps->p.tele, ps->p.sex, ps->p.age, ps->id, ps->score);
}int main()
{struct Stu s = { { "张三","123456","男",20 },"8888",98.5 };print(&s);return 0;
}
//张三 123456 男 20 8888 98.500000
//张三 123456 男 20 8888 98.500000
13. 操作符的属性
表达式(expression)由操作符和操作对象组成,每个表达式都有一个值,必须根据操作符的执行顺序来求值。
操作符的执行顺序取决于它们的优先级,如果优先级相同,取决于他们的结合性。
C语言操作符优先级有15级,按优先级从大到小排序:
优先级 |
操作符 |
描述 |
用法示例 |
结合性 |
|
---|---|---|---|---|---|
1 |
() |
聚组/函数调用 |
(表达式)/函数名(形参声明) |
L-R | |
[] |
下标引用 |
数组名[常量表达式] |
|||
. |
访问结构成员 | 结构成员访问 | 结构体.成员名 | ||
-> |
访问结构指针成员 | 结构体指针->成员名 | |||
2 |
+ |
正值 |
单目操作符 |
+表达式 |
R-L |
- |
负值 |
-表达式 |
|||
(类型) |
强制类型转换 |
(数据类型)表达式 |
|||
++ |
前置自增 |
++变量 |
|||
++ |
后置自增 |
变量++ |
|||
-- |
前置自减 |
--变量 |
|||
-- |
后置自减 |
变量-- |
|||
* |
解引用 |
*指针变量 |
|||
& |
取地址 |
&变量 |
|||
! |
逻辑非 |
!表达式 |
|||
~ |
按位取反 |
~表达式 |
|||
sizeof |
计算长度 |
sizeof(数据类型/变量) |
|||
3 |
* |
乘法 |
算术操作符 |
表达式*表达式 |
L-R |
/ |
除法 |
表达式/表达式 |
|||
% |
取模 |
整型表达式/整型表达式 |
|||
4 |
+ |
加法 |
表达式+表达式 |
||
- |
减法 |
表达式-表达式 |
|||
5 |
<< |
左移 |
移位操作符 |
变量<<表达式 |
|
>> |
右移 |
变量>>表达式 |
|||
6 |
> |
大于 |
关系操作符 |
表达式>表达式 |
|
>= |
大于或等于 |
表达式>=表达式 |
|||
< |
小于 |
表达式<表达式 |
|||
<= |
小于或等于 |
表达式<=表达式 |
|||
7 |
== |
等于 |
表达式==表达式 |
||
!= |
不等于 |
表达式!=表达式 |
|||
8 |
& |
按位与 |
位操作符 |
表达式&表达式 |
|
9 |
| |
按位或 |
表达式|表达式 |
||
10 |
^ |
按位异或 |
表达式^表达式 |
||
11 |
&& |
逻辑与 |
逻辑操作符 |
表达式&&表达式 |
|
12 |
|| |
逻辑或 |
表达式||表达式 |
||
13 |
?: |
条件操作符 |
表达式1?表达式2:表达式3 |
R-L |
|
14 |
= |
赋值 |
赋值操作符 |
变量=表达式 |
|
+= |
加后赋值 |
变量+=表达式 |
|||
-= |
减后赋值 |
变量-=表达式 |
|||
*= |
乘后赋值 |
变量*=表达式 |
|||
/= |
除后赋值 |
变量/=表达式 |
|||
%= |
取模后赋值 |
变量%=表达式 |
|||
<<= |
左移后赋值 |
变量<<=表达式 |
|||
>>= |
右移后赋值 |
变量>>=表达式 |
|||
&= |
按位与后赋值 |
变量&=表达式 |
|||
|= |
按位或后赋值 |
变量|=表达式 |
|||
^= |
按位异或后赋值 |
变量^=表达式 |
|||
15 |
, |
逗号 |
表达式,表达式,… |
L-R |
1. 优先级
#include <stdio.h>int main()
{//优先级:自增>加法>赋值>逗号 int a, b, c;a = 5;c = ++a;//a=6,c=6b = ++c, c++, ++a, a++;//c=7,b=7,c=8,a=7,a=8b += a++ + c;//b=7+8+8=23,a=9printf("a = %d b = %d c = %d\n:", a, b, c);//a = 9 b = 23 c = 8return 0;
}
2. 结合性
*p++:解引用(*)和后置自增(++)优先级相同,结合性都是从右往左,所以*p++等价于*(p++):
- 先执行*p
- 再p++
【C语言】操作符(运算符)相关推荐
- C语言操作符(运算符)详解
1.C语言的操作符有以下几种类型 2.算数操作符 + - * / %五种 / 分为:整数除法10/3=0 浮点数除法 10.0/3=3.333333(/两边至少有一个操作数为浮点数) %取余/取 ...
- c语言 4则运算符,C语言学习之路之四-----------C语言的运算符与表达式
C语言的运算符与表达式 运算符与表达式 C语言的运算符又称为操作符,是数据间进行运算的符号.C语言的运算符按运算类型可分为赋值运算符.算术运算符.逻辑运算符.关系运算符.位运算符.指针运算符和取成员运 ...
- C语言中运算符的优先级问题(如何巧妙记忆运算符的优先级顺序)
文章目录 C语言中的运算符 C语言中运算符的优先级 帮助记忆优先级的方法 先粗分 再细分 最后吃透 因不明确优先级而造成的常见问题 C语言中的运算符 C语言中的运算符说多也多,说不多也不多,包括以下运 ...
- 3014C语言_运算符
第四章 运算符 4.1 分类 C语言的运算符范围很广,可分为以下几类: 1.算术运算符:用于各类数值运算.包括加(+).减(-).乘(*).除(/).求余(%).自增(++).自减(--)共七种. 2 ...
- c语言负数左移右移_C语言位运算符:与、或、异或、取反,左移和右移
C语言位运算符:与.或.异或.取反.左移和右移 个位操作运算符.这些运算符只能用于整型操作数,即只能用于带符号或无符号的char,short,int与long类型. ,则该位的结果值为1,否则为0 | ...
- c语言等号运算符先计算右边,C语言运算符和表达式.ppt
<C语言运算符和表达式.ppt>由会员分享,可在线阅读,更多相关<C语言运算符和表达式.ppt(29页珍藏版)>请在人人文库网上搜索. 1.运算符和表达式,夏涛,运算符与表达式 ...
- C语言之运算符,表达式和语句总结
目录 前言 一.各种运算符 Ⅰ.算术运算符 Ⅱ.赋值运算符 Ⅲ.关系运算符 Ⅳ.逻辑运算符 Ⅴ.条件运算符(?:) Ⅵ.逗号运算符(,) Ⅶ.位运算符 Ⅷ.其他运算符 二.优先级大总结 三.类型转换 ...
- C语言——操作符(详解)
目录 前言 1.操作符的分类 2.算术操作符 2.1.算数操作符的简介 2.2.除法操作符(/)的使用 2.3.取余操作符(%)的使用 3.移位操作符 3.1.什么是移位操作符 3.2.原码.反码和补 ...
- C语言基本运算符和表达式
C语言基本运算符和表达式 运算符的种类.优先级和结合性 运算符的种类 1.算术运算符 2.关系运算符 3.逻辑运算符 4.位操作运算符 5.赋值运算符 6.条件运算符 7.逗号运算符 8.指针运算符 ...
- c语言的运算符表格,C语言教案(运算符和表格达式).ppt
C语言教案(运算符和表格达式) 运算符与表达式 第二讲 运算符和表达式 引例 概念及分类 优先级和结合性 算术运算 关系运算 逻辑运算 位运算 赋值运算符 逗号运算符 条件运算符 引例 引例 问题 输 ...
最新文章
- Vivado安装器件不全
- centos php sftp 扩展,Linux(CentOS)上配置 SFTP
- 高斯旋转热源与双椭球热源_电力分公司:多措并举保证供暖质量效益双提升
- ODI中web service介绍
- 进程、后台进程以及信号
- 如何关闭Windows7的UAC
- python提取txt数据到excel_python 读取txt中每行数据,并且保存到excel中的实例
- CentOS 6 5安装Erlang/OTP 17 0
- ios上的pvr与png
- Struts2中的异常处理
- GIT的使用中的问题处理
- ORACLE RAC 视频教程
- java编译jni错误_JNI开发的常见错误
- PHP初中高级学习在线文档下载
- 打开虚拟机电脑就重启和虚拟机不兼容hyper - v无法开启的解决方案。
- 手游服务器价格表,杭州高防43.241.17.1
- 计算机系统软件和应用软件的区别
- 荣耀路由器w831刷linux,华为荣耀WS831路由器设置的方法
- 软考和PMP哪个更好考?
- 计算机excel按F4是那个公式,excel中键盘F4到底怎么用?_excle 中的f4