C语言操作符详细讲解

在学习C语言的过程中,我们都会使用很多的操作符,那么如何加深对这些操作符的理

解呢?如何正确的使用操作符呢?下面我就来带着大家去熟悉操作符。

一、操作符的分类

操作符主要分为以下10中我们经常使用的类别

目录

一、操作符的分类

二、1.算术操作符

2.移位操作符

1. 逻辑移位

2. 算术移位

3.位操作符

4.赋值操作符

5.单目操作符

6.关系操作符

7.逻辑操作符

8.条件操作符

9.逗号表达式

10.一些特别的操作符使用

整型提升的意义:

下面将计算时的整形提升排序展现给大家

操作符优先级


下面我们来开始我们的第二部分对以上操作符进行讲解

二、1.算术操作符

算术操作符:

+            -             *           /           %     

以上的操作符除了 % 在使用的时候需要两边都为整数之外,其他操作符可以及处理整

形也可以处理浮点型,% 之后保留的是俩个数相除之后保留的余数。

当然在使用 / 的时候需要注意,如果   /   的两边的数字都为整数那么算出的结果就保留

整数而不会保留小数部分,当然如果  /   的使用中存在一个浮点型数据那么结果就可以

产生小数。

+    -    *  就是我们正常的数学运算这里我们就不过多介绍 

2.移位操作符

当然这里的很多小伙伴或许没有听过什么是位操作符,不知道的小伙伴可以去看一下计

算机的存储方式计算机数据存储

这里主要有俩种位操作符

<<  左移操作符                >> 右移操作符

当然需要注意的是对于操作符来说,只能对整数的数据使用为操作符(这个原因是数据在

计算机中的存储方式不同所造成的) 

当我们知道数据在计算机中的存储方式为二进制位的时候(我们以32为计算机为操作对

象)例如:

//位操作符的使用
#include<stdio.h>

int main()
{
	int a = 1;  // 在计算机中的存储为 00000000000000000000000000000001
    printf("%d\n",a);

	a = a << 1; //当我们将他的二进制位向左移动以为 << 1相当于 a 的二进制位向左移动1位
                //他的二进制位变为  0 | 00000000000000000000000000000010 左边多的0丢弃右边补0
	printf("%d\n", a);
	return 0;
}

 可以看到他的运行结果和我们所写的二进制是想对应的 << 的使用为左边为变量 右边为

要进行操作的位数 如  a << 2;  就表示 a 的二进制位向左移动俩位最左边的俩位丢弃,

右边补0

当然如果不进行赋值操作对原来的值不会有所更改

下面是对 >> 操作符的介绍  >> 操作符的使用和 << 操作符差不多但是他们的工作原理

不同 例如:

#include<stdio.h>
int main()
{
	int b = 2;//二进制位 00000000000000000000000000000010

	printf("b = %d\n", b);

	b = b >> 1;//像右移动一位 00000000000000000000000000000001  | 0   超出部分的0被舍弃

	printf("b = %d\n", b);
                //计算机存储的为补码
	int c = -2; //-2的补码为 11111111111111111111111111111110

	c = c >> 1; //向右移动一位 11111111111111111111111111111111  | 0  超出部分的0舍弃

	printf("c = %d", c);

	return 0;
}

 

例如上述代码,当然细心的小伙伴以及发现了不同当想 右移动的数字为整数时移动的规

则和左移的规则一样,变成负数之后就不同了呢?

当然这个是为了保持数据的一致性,数字的首位为符号位,当我们进行右移的时候如果

直接补0可能会影响数据的正负问题,那么计算机采取的处理方式是,向右移动时,他

本来的符号位上的数字为什么,我们就补什么,如果为0,向右移动就补0,如果为1,

就补上一个1

当然这种移动方式有他们的名称

1. 逻辑移位

左边用0填充,右边丢弃

2. 算术移位

左边用原该值的符号位填充,右边丢弃

计算机右移操作符采取的是算术移位

3.位操作符

对于位操作符来讲主要有以下几个:

&  按位与        |  按位或        ^    按位异或

当然这些操作符的使用也必须是整数

例如下面的代码展示:

//位操作符
#include<stdio.h>
int main()
{
	int a = 1;//二进制为00000000000000000000000000000001

	int b = 2;//二进制为00000000000000000000000000000010
                // a & b
                //00000000000000000000000000000001	
                //00000000000000000000000000000010
     //c的二进制为 00000000000000000000000000000000
    int c = a & b;
	
	printf("c = %d\n", c);
	return 0;
}

 当然这里可能大家就已经看出来了,只要当俩位上的数字相同时才保留,否则就为0,

及为相同为真进行保留,不同为假赋值为0.

那么操作符  |  的使用大家应该也能猜出来了

#include<stdio.h>
int main()
{
	int a = 1;//二进制为00000000000000000000000000000001

	int b = 3;//二进制为00000000000000000000000000000010
                // a & b
                //00000000000000000000000000000001	
                //00000000000000000000000000000011
     //c的二进制为 00000000000000000000000000000011
    int c = a | b;
	
	printf("c = %d\n", c);
	return 0;
}

 这里只要有一个数字为真结果就为真,俩个都为假才为假

 ^ 的使用和上面有所不同为相同为假不同为真

例如:

#include<stdio.h>
int main()
{
	int a = 1;//二进制为00000000000000000000000000000001

	int b = 3;//二进制为00000000000000000000000000000010
                // a & b
                //00000000000000000000000000000001	
                //00000000000000000000000000000011
     //c的二进制为 00000000000000000000000000000010
    int c = a ^ b;
	
	printf("c = %d\n", c);
	return 0;
}

 可以通过上述代码的结果看到,如果相同位上的数据相同则为假,不管这个为上的数字

是0或者是1,如果相同位上的数据不同及一个为1一个为0那么就为 真(及为1)。当然

可能小伙伴就要问了,这个代码有什么作用呢,

下面我就来给大家展示一下他的一些功能

例如,交换俩个变量的值,不创建临时变量且不能出现数据溢出的情况

那么我们需要怎么做呢

当然可能会有人想到使用下面代码

#include<stdio.h>
int main()
{

	int a = 20;
	int b = 30;
	a = a + b;
	b = a - b;
	a = a - b;
	printf("a = %d, b = %d", a,b);
	return 0;
}

 可以看出最后 a 和 b 的值确实进行了交换,但是这种方法漏掉了一直情况,就是当a 

和 b 的值都接近他们的类型的最大值,但a 和 b 没有超过这个值,但是a + b就可能会越

界使我们计算的结果出错,如果我们换一直思路,用 ^ 去处理

#include<stdio.h>
int main()
{
	int a = 20;
	int b = 30;

	a = a ^ b;
	b = a ^ b;
	a = a ^ b;

	printf("a = %d, b = %d", a, b);
	return 0;
}

我们可以看到,结果和我们想要的一样,那么这个是怎么计算的呢,这个我们可以这样

去理解这种解法  及 a ^ a ==0   a = a ^ b ;  b = a ^ b  --> b = a ^ b ^ b = a;  a = a ^ b -

-> a = a ^ b ^ a = b;  所以结果就进行了交换;而且这个交换都是在32位的二进制内进

行的不存在越界的问题,小伙伴们是不是感觉这种解法很巧妙。

当然 & 的作用也是很大的,例如一个数用 移位操作符去移动一个数,然后用  1 与每次

移动的数进行 & 可以得到这个数中1 的个数,当然我们只对操作符的使用进行介绍,如

果想探究更简单的方式,自己可以在平台进行搜索。

4.赋值操作符

主要为简单赋值和复合赋值

简单赋值:

int  weight = 20; 就是一个赋值语句,将20 赋值给变量 weight

也可以进行连续赋值 例如:

int x = y = 10;

这个意思是将 10 赋值给  y  在将  y  的值赋值给  x 

复合赋值符:

+=

-=

*=

/=

%=

>>=

<<=

&=

|=

^=

当然他们的使用在这里只是简单的介绍一下,例如:

int a = 20;

a += 10;  // 等价与  a = a + 10;

a  >> = 1; // 等价与  a = a >> 1;

其他的使用与这个是相同的道理,在这就不做过多介绍

5.单目操作符

下面是一些单目操作符的使用

主要包括:

!              逻辑反操作

            负值

           正值

           取地址

sizeof      操作数的类型长度(以字节为单位)

            对一个数的二进制按位取反

--              前置、后置--

++            前置、后置++

             间接访问操作符(解引用操作符)

(类型)       强制类型转换

逻辑反操作例如

int  a = 10; // 如果 a 为非 0 的数进行逻辑反之后都会变成 0 

!a == 0;

int a = 0; // 如果 a 为 0 进行逻辑反之后 会变成 1(及为真); 

!a == 1;

正负号大家应该比较熟悉,int  a = -10; 当然 int a = +10;也行但是我们一般不写这个 + 号

当然这里的 & 的我们之前讲的为操作符并不相同, 这里的 & 是放在变量名的前面,表

示取出这个变量的地址,通常与指针相结合使用,例如,int a = 10;  int *p = &a;

或者当我们求一个操作符的大小时用sizeof例如

printf("%d\n", sizeof(int) );// 可以求出一个int占几个字节

也可以直接使用:

int a = 0;

printf("%d\n",sizeof(a)); // 求的结果为 a的变量类型所对应的字节数

~  对他的二进制位取反

int  a = 1;// 二进制为:00000000000000000000000000000001

~a; // 二进制为: 11111111111111111111111111111110

则a = -2;

当然这里的 a++; // 为 a = a + 1; a-- 为 a = a - 1;

当然前置++ / --,与后置 ++ / -- 有所不同

例如:

#include<stdio.h>
int main()
{
    int a = 10;
    int b = a++;//后置++
    printf("a = %d b = %d\n",a,b);


    int c = 10;
    int d = ++c;//前置++
    printf("c = %d d = %d\n",c,d);     


}

打印的结果有所不同

a = c = 11;  b = 10; d = 11;

6.关系操作符

>

>=

<

<=

!=     不等于的比较

==    等于的比较

需要注意的是在使用 == 的时候不要将 = 弄混,因为 = 为赋值操作,而 == 才是判断操作

7.逻辑操作符

&&     逻辑与

||        逻辑或

及判断俩个条件或者多个条件时所用的

用  &&  时 只有保持 逻辑与两侧的条件都为真结果才为真,否则为假,这里需要注意的

是,如果在进行判断的时候,如果存在运算,当一方为假,后面的逻辑与这不在进行判

断。

在用 || 的时候,只要保持逻辑或的俩侧存在一个为真则结果为真,都为假才是假,当然

这里也有需要注意的点,及在使用逻辑或进行判断的时候,只要有真值为真的情况发

生,就不在进行后面的逻辑判断。如果后面存在运算则不在进行例如:

int  a = 10; int b = 0 ;    if( (a == 10) || ( b = 2) );   不熟悉的话肯定会认为,a最后为10

, b  为 2 ,其实并不是,当 a == 10 判断为真的时候后面的判断就不在进行,就是 2

并未赋值给 b  在&&的逻辑判断也相同

8.条件操作符

exp1 ? exp2 : exp3

在这个表达式中,当条件1为真的时候执行条件2,如果条件1为假则执行条件3

例如   a > 3 ? 1 : -1; 这段代码的意思是,如果 a > 3为真,这返回 1 否则返回 -1

9.逗号表达式

逗号表达式的特点就是从前向后计算,将最后的值返回

例如:

int  a = 10;

int  b = 20;

int  d = 0;

int  c = (a =  a + 10, b = a + b, d = 50);

最后 a = 20; b = 40; d = 50; 并返回d 的结果给 c , c = 50;

10.一些特别的操作符使用

该内容主要为,数组的创建,函数的调用,和结构体的访问对象

例如:

int  arr[10];  // 我们创建了一个arr数组,里面有10元素,都是int类型的,需要用的 [ ]

或者我们写了一个加法函数

Add (x,y);// 将x ,y 的值传入进行计算,需要用到的 ( )

或者是在访问结构体成员所使用的 . 或者 -> 

当然在计算的时候我们有会经常遇到一些问题,就是关于不同类型的数值直接的计算

最后向大家介绍一下,关于整形提升的一部分知识

当然在进行介绍之前我们先来了解整形提升的意义

整型提升的意义

表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度

一般就是int的字节长度,同时也是CPU的通用寄存器的长度。

因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长

度。

通用CPUgeneral-purpose CPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令

中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转

换为intunsigned int,然后才能送入CPU去执行运算。

例如:

char 类型在计算机的存储过程中只占一个字节;

int 占4个字节

那么他们如何进行计算呢?

例如我们将一个超过 255的数字存放在char类型中,会在存储的时候进行截断

#include<stdio.h>
int main()
{
	int a = 256;
	char c = a;
	printf("%d", c);
	return 0;
}

可以看出我们最后打印出的结果是 0;

那么为什么呢?

a = 256; 他的二进制存储为  0000 0000 0000 0000 0000 0000 0000 0001 0000 0000

char b 的内存只有 0000 0000那么大,当将 int 类型的 a 的数据放在b里面时,存储的

只是后面的一个字节大小的数据 0000 0000

当然,如果我们将一个char类型的数据赋值给一个int类型的数据时,就会发生整形提升如:

#include<stdio.h>
int main()
{

	char a = 128;
	int b = a;
	printf("b = %d", b);


	return 0;
}

 这里我们可以看到,b中存放的不是128,而是-128,那么为什么呢?

我们知道计算机在存放char类型的时候只开辟了一个字节的空间大小,当要将这个赋值

给int类型的变量的时候需要整形提升,

如上述代码中 ,

char a = 128; 他的二进制位 1000 0000

而将这个数赋值给int 类型的b 的时候 对 1000 0000进行整形提升,因为首位代表符号

位所以,提升的时候看符号位,符号位为1,整形提升全补1

b 的二进制为 1111 1111 1111 1111 1111 1111 1000 0000,为补码

而首元素为1符号位为负数这反码为 1111 1111 1111 1111 1111 1111 0111 1111

原码为 1000 0000 0000 0000 0000 0000 1000 0000 为 - 128

最后打印结果为 b = -128

下面将计算时的整形提升排序展现给大家

long double

double

float

unsigned long int

long int

unsigned int

int

char

从下往上进行提升,如果运算中有这些的话

当然还有一些复杂表达式的运算顺序,也想大家展示一下

操作符优先级

大家可以点开链接去查看

  • 21
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 19
    评论
在信号处理领域,DOA(Direction of Arrival)估计是一项关键技术,主要用于确定多个信号源到达接收阵列的方向。本文将详细探讨三种ESPRIT(Estimation of Signal Parameters via Rotational Invariance Techniques)算法在DOA估计中的实现,以及它们在MATLAB环境中的具体应用。 ESPRIT算法是由Paul Kailath等人于1986年提出的,其核心思想是利用阵列数据的旋转不变性来估计信号源的角度。这种算法相比传统的 MUSIC(Multiple Signal Classification)算法具有较低的计算复杂度,且无需进行特征值分解,因此在实际应用中颇具优势。 1. 普通ESPRIT算法 普通ESPRIT算法分为两个主要步骤:构造等效旋转不变系统和估计角度。通过空间平移(如延时)构建两个子阵列,使得它们之间的关系具有旋转不变性。然后,通过对子阵列数据进行最小二乘拟合,可以得到信号源的角频率估计,进一步转换为DOA估计。 2. 常规ESPRIT算法实现 在描述中提到的`common_esprit_method1.m`和`common_esprit_method2.m`是两种不同的普通ESPRIT算法实现。它们可能在实现细节上略有差异,比如选择子阵列的方式、参数估计的策略等。MATLAB代码通常会包含预处理步骤(如数据归一化)、子阵列构造、旋转不变性矩阵的建立、最小二乘估计等部分。通过运行这两个文件,可以比较它们在估计精度和计算效率上的异同。 3. TLS_ESPRIT算法 TLS(Total Least Squares)ESPRIT是对普通ESPRIT的优化,它考虑了数据噪声的影响,提高了估计的稳健性。在TLS_ESPRIT算法中,不假设数据噪声是高斯白噪声,而是采用总最小二乘准则来拟合数据。这使得算法在噪声环境下表现更优。`TLS_esprit.m`文件应该包含了TLS_ESPRIT算法的完整实现,包括TLS估计的步骤和旋转不变性矩阵的改进处理。 在实际应用中,选择合适的ESPRIT变体取决于系统条件,例如噪声水平、信号质量以及计算资源。通过MATLAB实现,研究者和工程师可以方便地比较不同算法的效果,并根据需要进行调整和优化。同时,这些代码也为教学和学习DOA估计提供了一个直观的平台,有助于深入理解ESPRIT算法的工作原理。
评论 19
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值