学C的第十五天【C语言操作符详解】

=========================================================================

相关代码gitee自取:C语言学习日记: 加油努力 (gitee.com)

=========================================================================

接上期:
学C的第十四天【应用多文件的形式实现 扫雷 程序(重点)、练习】-CSDN博客

=========================================================================

                           

 1. 操作符分类:

  • 算术操作符:        +        -        *        /        %
               
  • 移位操作符:        <<        >>
             
  • 操作符:        &        |        ^
               
  • 赋值操作符:        =        +=        -=        *=        /=        ……
               
  • 单目操作符:        !        sizeof        +        -        ~        &        *
              
  • 关系操作符:        >        <        >=        <=        ==        !=
               
  • 逻辑操作符:        &&        ||
            
  • 条件操作符:        ?        :
            
  • 逗号操作符:        ,
             
  • 下标引用函数调用结构成员:        []        ()        .        ->

              


             

2. 算术操作符

(注:除了 % 操作符之外,其他的几个操作符可以作用于整数和浮点数

     

:        加法运算

        

:        减法运算

           

:        乘法运算

              


:        除法运算,除数不能为0

               

分为:

1. 整数除法(除号两端都是整数)

                        

 2. 浮点数除法(除号两端只要有一个小数就执行浮点数除法)

               


% :        取模操作符,得到的是整除后的余数

                

1. 得到的是整除后的余数

          

2. %取模操作符的两个操作数必须是整数

                    


                   

3. 移位操作符

注:

移位操作符的操作数只能是整数,对于一位运算符,不要移动负数位,
这是标准未定义的,计算结果将取决于编译器,结果不可预料

                  


                    

补充:整数二进制表示形式

             

有三种表现形式:

原码        |        反码        |        补码

                

          

1. 正整数原码反码补码相同

                  

2. 负整数原码反码补码要计算

                     

3. 不管是正整数还是负整数都可以写出二进制原码

                     

4. 根据正负直接写出的二进制序列就是原码

         

5.移位移动的是二进制信息

                         

6.整数内存中存储的是补码

                    

7.计算的时候也是使用补码计算的

           

(演示代码:)
#include <stdio.h>
//不管是正整数还是负整数都可以写出二进制原码
//根据正负直接写出的二进制序列就是原码
int main()
{
	//1个整型是4个字节 = 32个bit位
	//符号位是 1 表示负数
	//符号位是 0 表示正数

	int a = 15;
	//正整数的原码、反码、补码是相同的
	//00000000000000000000000000001111 -- 原码
	//00000000000000000000000000001111 -- 反码
	//00000000000000000000000000001111 -- 补码

	int b = -15;
	//负整数的原码、反码、补码是要计算的
	//10000000000000000000000000001111 -- 原码
	//11111111111111111111111111110000 -- 反码(原码的符号位不变,其它位按位取反得到的就是反码)
	//11111111111111111111111111110001 -- 补码(反码 +1 就是补码)

	return 0;
}

                     


                            

<< :        左移操作符 -- 补码 左边丢弃,右边补0

               

//左移操作符:补码 左边丢弃,右边补0
#include <stdio.h>

int main()
{
//正整数:
	int a = 6;
	//   00000000000000000000000000000110 -- 正整数的 原码、反码、补码 相同
	//   00000000000000000000000000000110 -- 补码 (移位操作的是 补码)
					//左移后:
	// 0 0000000000000000000000000000110 0 -- 补码 (原码 、 反码)
	//   因为正整数的 原码、反码、补码 相同,所以这也是原码

	int b = a << 1;

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

	printf("%d\n", a);


//负整数:
	int c = -6;
	//   10000000000000000000000000000110 -- 原码
	//   11111111111111111111111111111001 -- 反码 (原码按位取反)
	//   11111111111111111111111111111010 -- 补码 (反码 +1 )

	int d = c << 1;
				//左移后:
	// 1 1111111111111111111111111111010 0 -- 补码
	//   11111111111111111111111111110011 -- 反码 (补码 -1)
	//   10000000000000000000000000001100 -- 原码 (符号位不变,反码按位取反)
	// -12

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

	printf("%d\n", c);

	a = a << 1;
	// 也可以写成:
	a <<= 1; //符合符号 <<= :左移等于 ,类似 a += 1
	
	return 0;
}

                    


                      

>> :        右移操作符

         

分为:

           

       1. 算术 右移 补码 右边 丢弃, 左边 补原来的符号位
           

2.逻辑 右移 补码 右边 丢弃, 左边 直接补0

                 

(C语言没有明确规定到底是算术右移还是逻辑右移,一般编译器上采用的是算术右移)

                

#include <stdio.h>

int main()
{
//正整数:
	int a = 15;
	//00000000000000000000000000001111 -- 补码
	int b = a >> 1;
	// 0 0000000000000000000000000000111 1 -- 补码(移位后)

	//C语言没有明确规定到底是算术右移还是逻辑右移,一般编译器上采用的是算术右移

	printf("%d\n", b); //算术右移
	// b 得到的是 a 移位后的结果
	// 算术右移(右边丢弃,左边补原来的符号位)
	// 从 二进制的1111 变成 二进制的111 ,从 十进制的 15 变成了 十进制的 7 
	
	printf("%d\n", a);
	// a >> 1,进行移位后,a没有发生变化,
	// 表达式不会影响 a 的 值


//负整数:
	int c = -15;
	//11111111111111111111111111110001 -- 补码 (符号位是 1 表示负数)
	int d = c >> 1;
	// 1 1111111111111111111111111111000 1 -- 补码 (移位后)
	// 空出的位置补的是 1 ,说明是算术右移(右边丢弃,左边补原来的符号位)

	

	// 实际值是用 原码 表示的

	printf("%d\n", d); //算术右移
	// 反码 +1 就是补码 ,补码 -1 就是反码:
	//  11111111111111111111111111110111 -- 反码

	// 原码的符号位不变,反码按位取反得 原码
	//  10000000000000000000000000001000 -- 原码 (-8)

	printf("%d\n", c);
	
	return 0;
}

                 

(有类似 除以2向下取整 的效果) 

                     


                     

4.位操作符

(注:位操作符的操作数必须是整数

                


                        

1.整数内存中存储的是补码

                    

2.计算的时候也是使用补码计算的

                          


                       

& :        按(二进制)位与 -- 对应二进制位有0则为0
两个
同时为1,才为1

                           

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

int main()
{
	int a = 3;
	// 00000000000000000000000000000011 -- 补码(正整数的原码、反码、补码相同)

	int b = -5;
	// 10000000000000000000000000000101 -- 原码
	// 11111111111111111111111111111010 -- 反码
	// 11111111111111111111111111111011 -- 补码

	int c = a & b;
	// & -- 对应二进制位有0则为0,两个同时为1,才为1
	// 00000000000000000000000000000011 -- a补码
	// 11111111111111111111111111111011 -- b补码
	//				得:
	// 00000000000000000000000000000011	-- 补码(两个补码计算出来的也是补码)
	// 这里计算出来的结果符号位是 0 ,说明是 正整数,三个码相同,所以这也是原码
	

	printf("%d\n", c); // 所以结果是3

	return 0;
}

                


              

| :        按(二进制)位或 -- 对应二进制位有1则为1,两个同时为0,才为0

                               

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

int main()
{
	int a = 3;
	// 00000000000000000000000000000011 -- 补码(正整数的原码、反码、补码相同)

	int b = -5;
	// 10000000000000000000000000000101 -- 原码
	// 11111111111111111111111111111010 -- 反码
	// 11111111111111111111111111111011 -- 补码

	int c = a | b;
	// | -- 按(二进制)位或 -- 对应二进制位有1则为1,两个同时为0,才为0
	// 00000000000000000000000000000011 -- a补码
	// 11111111111111111111111111111011 -- b补码
	//				得:
	// 11111111111111111111111111111011	-- 补码(两个补码计算出来的也是补码)
	// 
	// 这里计算出来的结果符号位是 1 ,说明是 负整数,还要计算 反码 和 补码
	// 11111111111111111111111111111010 -- 反码
	// 10000000000000000000000000000101 -- 原码

	printf("%d\n", c); // 所以结果是-5

	return 0;
}

                  


                    

^ :        按(二进制)位异或 -- 对应二进制位相同为0相异为1

                                 

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

int main()
{
	int a = 3;
	// 00000000000000000000000000000011 -- 补码(正整数的原码、反码、补码相同)

	int b = -5;
	// 10000000000000000000000000000101 -- 原码
	// 11111111111111111111111111111010 -- 反码
	// 11111111111111111111111111111011 -- 补码

	int c = a ^ b;
	// ^ -- 按(二进制)位异或 -- 对应二进制位相同为0, 相异为1
	// 00000000000000000000000000000011 -- a补码
	// 11111111111111111111111111111011 -- b补码
	//				得:
	// 11111111111111111111111111111000	-- 补码(两个补码计算出来的也是补码)
	// 
	// 这里计算出来的结果符号位是 1 ,说明是 负整数,还要计算 反码 和 补码
	// 10000000000000000000000000000111 -- 反码
	// 10000000000000000000000000001000 -- 原码

	printf("%d\n", c); // 所以结果是-8

	return 0;
}

                             


                            

^(按位异或) 的一些规律

                

1.         变量 ^ 变量 = 0

          

2.         0 ^ 变量 = 变量

          

3.         满足交换律

                  

实例:
//异或是支持交换律的
#include <stdio.h>

int main()
{
	int a = 3;
	int b = 5;

	printf("%d\n", a ^ a); // 0 (每一位都是相同的,32位全是0)
	printf("%d\n", a ^ 0); // a = 3
	// 011 - 3 (a)
	// 000 - 0
	// 011 -- 异或结果 :a = 3 

	printf("%d\n", a ^ b ^ a); // b = 5
	// 011 - 3 (a)
	// 101 - 5 (b)
	// 110 -- 异或结果
	// 011 - 3 (a)
	// 101 -- 异或结果 : b = 5

	printf("%d\n", a ^ a ^ b); // b = 5
	// a ^ a --> 0
	// 0 ^ b --> b = 5



	return 0;
}

                 


                   

一道变态面试题:不使用临时变量,交换两个数的值

                

(^异或 不会造成进位,所以不会产生栈溢出,但可读性不高,不易理解)

//一道变态面试题:不使用临时变量,交换两个数的值
#include <stdio.h>

int main()
{
	int a = 3;
	int b = 5;

	printf("交换前:a=%d b=%d\n", a, b);

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

	printf("交换后:a=%d b=%d\n", a, b);

	return 0;
}

                      


                             

练习:求一个整数存储在内存中的二进制中1的个数

                    

#include <stdio.h>

int main() {
    int input = 0;
    int count = 0; //统计 1 的个数
    //输入
    scanf("%d", &input);

    int i = 0;
    for (i = 0; i < 32; i++)
    { // 共32位,判断32次

        int test = input & 1; //按位与1 取出32位最低位
        //判断是不是1
        if (test == 1) {
            count++; //是的话,统计加1
        }
        //判断下一位:
        input >>= 1; //向右移一位,继续判断,使用循环

    }

    //输出:
    printf("%d", count);

    return 0;
}

                       


                          

5.赋值操作符

=赋值操作符 -- 可以让你得到一个你之前不满意的值。
也就是你可以
给自己重新赋值

                    

int weight = 120; //体重
weight = 89; //不满意就赋值换掉
double salary = 10000.0; 
salary = 20000.0 //使用赋值操作符赋值

                     

可以连续赋值(不推荐):

int a = 10;
int x = 0;
int y = 20;

a = x = y+1; //选择赋值
//先把 y+1 赋给 x ,再把 x 赋给 a

//更好的写法:
x = y + 1;
a = x;
//这样写更加清晰而且方便一步步调试 

                  


                                     

复合赋值符

               

+= :        加等

  

-= :        减等

  

*= :        乘等

  

/= :        除等

  

%= :        模等于      

  

>>= :        右移等

  

<<= :        左移等

  

&= :        按位与等

   

|= :        按位或等

   

^= :        按位异或等

                       


                      

6.单目操作符

操作符只有一个操作数

                    


             

:        逻辑反操作

(if语句、while语句中、表达式中)

         

  

- :       

(改变数字正负)

         

  

+ :       

(正数默认自带+,常省略,不常用)

         

  

& :        取地址

         (应用于指针)

         

  

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

         (不是函数,是操作符;计算的是类型创建变量的大小

         

  

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

(对补码32个bit位按位取反,1变成00变成1,取反后还是补码,要转换为原码)

         

  

-- :        前置、后置--

常用

         

  

++ :        前置、后置++

常用

         

  

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

(应用于指针,告诉我们哪个变量是指针变量;

解引用:通过指针变量中存放的地址,找到指向的 空间/内容 )

         

  

(类型) :        强制类型转换

              


            

sizeof数组

//sizeof 和 数组
#include <stdio.h>

void test1(int arr[]) 
//传过来的首元素指针 ,一个 int*类型 大小是 4个字节
{
	printf("%d\n", sizeof(arr));
}

void test2(char ch[])
//传过来的首元素指针 ,一个 char*类型 大小是 4个字节
{
	printf("%d\n", sizeof(ch));
}

int main()
{
//整型数组:
	int arr[10] = { 0 };

	printf("%d\n",sizeof(arr)); //使用 数组名
	//40 - 计算整个数组的大小、单位字节
	printf("%d\n", sizeof(int[10])); //使用数组的字符类型,包括[]和元素个数

	test1(arr);

//字符数组
	char ch[10] = { 0 };

	printf("%d\n", sizeof(ch)); //使用 数组名
	//10 -- 字符数组中 sizeof(数组名) 计算 的是 字符个数
	printf("%d\n", sizeof(int[10])); //使用数组的字符类型,包括[]和元素个数
	//40 -- 一个 char类型元素 是 4个字节,10个就是40

	test2(ch);

	return 0;
}

                     


                         

~ 按位取反的运用,结合 & (按位与)  和  | (按位或)

                         

 (将 13 的 32bit位从右往左第5位换为 1 再换为 0

//~按位取反的运用,结合 & (按位与)和  | (按位或)
//将 13 的 32bit位从右往左第5位,换为 1 ,再换为 0
#include <stdio.h>

int main()
{
	int a = 13;

	// 00000000000000000000000000001101 - 13 的 原、反、补码
	//			按位或:有1就是1
	// 00000000000000000000000000010000 - 1 << 4
	// 00000000000000000000000000011101 -- 29 的 原、反、补码
	a |= (1 << 4);
	// 这样就把第五位换成了 1 , 13 变成了 29 

	printf("%d\n", a); 


	// 再把第五位换成 0
	// 00000000000000000000000000011101 - 29 的 原、反、补码
	// 00000000000000000000000000010000 - 1 << 4
	// 11111111111111111111111111101111 - ~(1 << 4) 进行按位取反
	//			按位或:两个都是 1 才是 1
	// 00000000000000000000000000001101 -- 13 的 原、反、补码
	a &= (~(1 << 4));

	printf("%d", a);

	return 0;
}

                                   


                                  

运用 ~按位取反 实现 多组输入

//运用 ~按位取反 实现 多组输入
#include <stdio.h>

int main()
{
	int a = 0;

	// scanf 读取失败返回的是EOF
	// 假设 scanf 读取失败,返回EOF ,即 -1
	// -1 的补码:
	// 11111111111111111111111111111111
	//			~ 按位取反后:
	// 00000000000000000000000000000000
	//			得0,条件即为假
	
	while (~scanf("%d", &a))
	{
		printf("%d\n", a);
	}

	return 0;
}

                       


                         

7. 关系操作符

不能比较字符串、自定义类型

            


              

> :        大于

        

   

 >= :        大于等于

        

   

< :        小于

        

   

<= :        小于等于

        

   

!= :        不等于(用于测试“不相等”)

        

   

== :        相等(用于测试“相等”)

=:是赋值        ;        ==:是相等

               


                        

8. 逻辑操作符

             

&& :        逻辑与(并且:两个为真,才为真)

            

  

   

|| :        逻辑或(或者:多个中一个为真,就为真)

                 

  

   

:        逻辑非

    


                

演示:判断闰年

//判断闰年
#include <stdio.h>

int main()
{
	int y = 0;
	scanf("%d", &y);

	//1. 能被4整除,并且不能被100整除
	//2. 能被400整除是闰年
	if ( ((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0))
	{
		printf("闰年\n");
	}
	else
	{
		printf("不是闰年");
	}

	return 0;
}

                   

========================================================================= 

 (下回揭晓:)

9. 条件操作符

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
在信号处理领域,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算法的工作原理。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

高高的胖子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值