目录

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 右移操作符

移位规则:

  1. 逻辑移位:左边补0,右边抛弃。
  2. 算术移位:左边补原符号位,右边抛弃。(常见编译器都是算术移位)

算术移位:

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。
  2. |:两个操作数相应的位至少有一个为1,结果为1。
  3. ^:两个操作数相应的位相同为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语言,提到左值意味着:

  1. 它指定一个对象,所以引用内存中的地址;
  2. 它可用在赋值运算符的左侧,左值(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++):

  1. 先执行*p
  2. 再p++

【C语言】操作符(运算符)相关推荐

  1. C语言操作符(运算符)详解

    1.C语言的操作符有以下几种类型 2.算数操作符 + - * / %五种 / 分为:整数除法10/3=0   浮点数除法  10.0/3=3.333333(/两边至少有一个操作数为浮点数) %取余/取 ...

  2. c语言 4则运算符,C语言学习之路之四-----------C语言的运算符与表达式

    C语言的运算符与表达式 运算符与表达式 C语言的运算符又称为操作符,是数据间进行运算的符号.C语言的运算符按运算类型可分为赋值运算符.算术运算符.逻辑运算符.关系运算符.位运算符.指针运算符和取成员运 ...

  3. C语言中运算符的优先级问题(如何巧妙记忆运算符的优先级顺序)

    文章目录 C语言中的运算符 C语言中运算符的优先级 帮助记忆优先级的方法 先粗分 再细分 最后吃透 因不明确优先级而造成的常见问题 C语言中的运算符 C语言中的运算符说多也多,说不多也不多,包括以下运 ...

  4. 3014C语言_运算符

    第四章 运算符 4.1 分类 C语言的运算符范围很广,可分为以下几类: 1.算术运算符:用于各类数值运算.包括加(+).减(-).乘(*).除(/).求余(%).自增(++).自减(--)共七种. 2 ...

  5. c语言负数左移右移_C语言位运算符:与、或、异或、取反,左移和右移

    C语言位运算符:与.或.异或.取反.左移和右移 个位操作运算符.这些运算符只能用于整型操作数,即只能用于带符号或无符号的char,short,int与long类型. ,则该位的结果值为1,否则为0 | ...

  6. c语言等号运算符先计算右边,C语言运算符和表达式.ppt

    <C语言运算符和表达式.ppt>由会员分享,可在线阅读,更多相关<C语言运算符和表达式.ppt(29页珍藏版)>请在人人文库网上搜索. 1.运算符和表达式,夏涛,运算符与表达式 ...

  7. C语言之运算符,表达式和语句总结

    目录 前言 一.各种运算符 Ⅰ.算术运算符 Ⅱ.赋值运算符 Ⅲ.关系运算符 Ⅳ.逻辑运算符 Ⅴ.条件运算符(?:) Ⅵ.逗号运算符(,) Ⅶ.位运算符 Ⅷ.其他运算符 二.优先级大总结 三.类型转换 ...

  8. C语言——操作符(详解)

    目录 前言 1.操作符的分类 2.算术操作符 2.1.算数操作符的简介 2.2.除法操作符(/)的使用 2.3.取余操作符(%)的使用 3.移位操作符 3.1.什么是移位操作符 3.2.原码.反码和补 ...

  9. C语言基本运算符和表达式

    C语言基本运算符和表达式 运算符的种类.优先级和结合性 运算符的种类 1.算术运算符 2.关系运算符 3.逻辑运算符 4.位操作运算符 5.赋值运算符 6.条件运算符 7.逗号运算符 8.指针运算符 ...

  10. c语言的运算符表格,C语言教案(运算符和表格达式).ppt

    C语言教案(运算符和表格达式) 运算符与表达式 第二讲 运算符和表达式 引例 概念及分类 优先级和结合性 算术运算 关系运算 逻辑运算 位运算 赋值运算符 逗号运算符 条件运算符 引例 引例 问题 输 ...

最新文章

  1. Vivado安装器件不全
  2. centos php sftp 扩展,Linux(CentOS)上配置 SFTP
  3. 高斯旋转热源与双椭球热源_电力分公司:多措并举保证供暖质量效益双提升
  4. ODI中web service介绍
  5. 进程、后台进程以及信号
  6. 如何关闭Windows7的UAC
  7. python提取txt数据到excel_python 读取txt中每行数据,并且保存到excel中的实例
  8. CentOS 6 5安装Erlang/OTP 17 0
  9. ios上的pvr与png
  10. Struts2中的异常处理
  11. GIT的使用中的问题处理
  12. ORACLE RAC 视频教程
  13. java编译jni错误_JNI开发的常见错误
  14. PHP初中高级学习在线文档下载
  15. 打开虚拟机电脑就重启和虚拟机不兼容hyper - v无法开启的解决方案。
  16. 手游服务器价格表,杭州高防43.241.17.1
  17. 计算机系统软件和应用软件的区别
  18. 荣耀路由器w831刷linux,华为荣耀WS831路由器设置的方法
  19. 软考和PMP哪个更好考?
  20. 计算机excel按F4是那个公式,excel中键盘F4到底怎么用?_excle 中的f4

热门文章

  1. 【云原生】-Docker容器技术小结
  2. 基于WebGIS的留守儿童社区
  3. KMP常见问题及解决方法【Z】
  4. java,javascript对18位身份证格式的验证算法
  5. ExtJs 4.1.1 API离线浏览
  6. macbook历代_苹果历代MacBook笔记本简要回顾
  7. AMQ初级使用(队列模式+主题模式)
  8. 正则表达式,保留中英文字符,标点符号,数字
  9. python学习日记ex17
  10. 计算机应用基础成绩单样板,陕西 : 西安08年7月计算机应用基础上机考核成绩单...