C语言教程——操作符详解(2)

目录

前言

1.sizeof和数组(补充)

2.关系操作符

3.逻辑操作符

4.条件操作符

5.逗号表达式

6.下标引用、函数调用和结构成员

6.1 [ ] 下表引用操作符

6.2( ) 函数调用操作符

6.3访问一个结构的成员

7.表达式求值

7.1隐式类型转换

7.2算数转换

7.3操作符的属性

总结


前言

这里接着上一篇的文章内容接着学习和讲解,基本上本篇就把操作符这一部分全部讲解完毕。


这里我们接着再讲一下sizeof和数组之间的关系和使用方法。

1.sizeof和数组(补充)

#include <stdio.h>
void test1(int arr[])
{
    printf("%d\n",sizeof(arr));
}
void test2(char ch[])
{
    printf("%d\n",sizeof(ch));
}

int main( )
{
    int arr[10]={0};
    char ch[10]={0};
    printf("%d\n",sizeof(arr));
    printf("%d\n",sizeof(ch));
    test1(arr);
    test(ch);
    return 0;
}

这里创建了两个不同类型的数组,一个是整形,一个是字符型,同时这里写了两个函数,在主函数里面调用这两个函数。

第一条打印数据因为sizeof里面是数组名,这里计算的是数组的大小,因为arr是十个整形元素,一个整形元素是4个字节,所以打印出来就是40,下一条打印也是计算数组的大小,因为ch里面有十个字符型元素,字符型元素占1个字节,所以这里打印的就是10。

而test1和test2是函数,函数传参传入的是数组名,而数组名是首元素的地址,也就是传入的是个指针,sizeof里面计算的是指针的大小,也就是4或者8,因为32位平台下指针位是4个字节,64位平台下指针位是8个字节,test2传入的也是指针,所以输出也是4或者8个字节。

只要是地址(指针)就是4或者8个字节,VS中X86就是32位环境,X64是64位环境。

2.关系操作符

>

>=

<

<=

!=                          用来测试“不相等”

==                         用于测试“相等”

这些关系运算符都比较简单,没有什么可以进行讲解的,但是我们要注意一些运算符使用的时候的陷阱。

注意:在编程的时候==和=不小心写错,导致的错误。

C语言中两个相等是用来判断的,比较的,但是一个等号就是赋值。

3.逻辑操作符

逻辑符号:

&&                逻辑与

| |                  逻辑或

逻辑与又称为并且,逻辑或又称为或者,之前的按位与(&)和按位或(|)是通过二进制进行运算的。但这里的逻辑与和逻辑或是针对于真假来进行的。

对于逻辑与(&&),两边都为真才为真,有假就为假。

对于逻辑或(|   |),有真就为真,两个为假才为假。

这里可以用自己的方法来记住,只要理解为并且和或者就可以,中文语义并且就是两个都的意思,或者就是只要有一个就行。

这里来看下列代码:

int main( )
{
    int a=10;  //int a=0;
    int b=5;   //int b=0
    if(a && b)
    {
        pritnf("ok\n");
    }
    return 0;
}

这里两个都为真,才会打印ok,如果把其中的一个数改成0,那么就不会打印ok。

int main( )
{
    int a=10;  //int a=0;
    int b=5;   //int b=0
    if(a || b)
    {
        pritnf("ok\n");
    }
    return 0;
}

这里就是只要两个有一个非0,也就是有一个为真,那么就会打印ok,只有两个都为假的时候才不会打印ok。

这里我们就可以通过这两个运算符来写一个判断是否为闰年的程序了,就不用套好几层 if 了:

int main( )
{
    int year=0;
    scanf("%d\n",&year);
    if(((year % 4 == 0) && ( year % 100 !=0)) || ( year % 400 ==0))
    {
        printf("Yes\n");
    }
    return 0;
}

逻辑与(并且)操作符来说,有一个特点,这里用一个程序来进行讲解:

int main( )
{
    int i = 0, a = 0, b = 3, d = 4;
    i = a++ && ++b && d++;
    pritnf("a = %d\nb = %d\nc = %d\nd = %d\n",a,b,c,d);
    return 0;
} 

这里可以自己先思考一下,之后再看解析。

这里我们都以为输出是1 3 3 4,但是错了,正确答案是1 2 3 4 ,这里因为a++,因为是后置加加,所以这里还是0,0后面跟上逻辑与,由于逻辑与的特点就是里面要是有假的话,那么就为假。而逻辑与的特点就是左边(前面)只要有假了,后面就不需要进行运算了,只有左面(前面)为真的时候,才会继续往后进行判断。

逻辑或(或者)操作符来说:

int main( )
{
    int i = 0, a = 1, b = 2, c = 3, d = 4;
    i = a++ || ++b || d++;
    pritnf("a = %d\nb = %d\nc = %d\nd = %d\n",a,b,c,d);
    return 0;
} 

对于逻辑或来说,左边要是为真,右边就无需进行计算,所以最后的结果就是2 2 3 4。因为在a++的时候因为是后置加加,所以只判断a,a这里是真,a判断完成之后再进行加1,因为第一个就为真了,所以后面无需进行计算,最后输出也就是 2 2 3 4。

4.条件操作符

exp1 exp2 exp3

这里exp叫做表达式,上面就是三个表达式,如果表达式1的结果为真,那么表达式2就计算,表达式3不计算;如果表达式1为假,那么表达式2不计算,表达式3计算。

条件操作符也叫做三目操作符,涉及到了三个操作数。

可以看下面的例子:

int main( )
{
    int a =10;
    int b =20;
    int m =0;
    if(a>b)
        m =a;
    else
        m =b;

    m=(a>b?a:b);
    return 0;
}

这里上面的一对代码就可以用下面的一行来搞定,如果a>b就执行a,如果不符合,就是b,所以这就是三目操作符。

5.逗号表达式

exp1, exp2, exp3, .... expN

逗号表达式,就是用逗号隔开的多个表达式。

逗号表达式,从左到右依次执行。整个表达式的结果就是最后一个表达式的结果。

看下面的一个例子:

int a =1;
int b =2;
int c =(a>b, a=b+10, a, b=a+1);

表达式从左到右依次运算,最后的结果就是最后的一个表达式的结果。上述代码从左到右,第一个表达式没有影响,第二个表达式a被赋值为b+10,也就是赋值了12,表达式3就是a+1,也就是13。

if(a =b+1, c=a/2, d>0)

这里也是从左到右依次运算,最后的结果还是表达式3的最后的结果,所以这里if语句真正判断的就是表达式3(d>0)的条件。

接着再看一个例子:

a = get_val( );
count_val(a);
while(a>0)
{
    //业务处理
    a =get_val( );
    count_val(a);
}
    

这里是先对a进行赋值,之后再调用这个函数,再判断循环执行循环里面的语句。则这几行语句就可以通过逗号表达式改成下面的语句:

while(a =get_val( ), count_val(a), a>0)
{
    //业务处理
}

这里就可以用这一行来代替上面的这一堆代码。

6.下标引用、函数调用和结构成员

6.1 [ ] 下表引用操作符

操作数:一个数组名+一个索引值

int main( )
{
    int arr[10]={1,2,3,4,5};
    printf("%d\n",arr[4]);
    return 0;
}

这个[ ]就是下标引用操作符,这里面操作数就是arr和4,这里就是打印下标为4的数,也就是5。

6.2( ) 函数调用操作符

接受或者多个操作数:第一个操作数是函数名,剩余的操作数就是传给函数的参数。

int main( )
{
    int len = strlen("abcdef");
    return 0;
}

这里就是用了string库里面的strlen函数,()就是函数的调用操作符,操作数就是:strlen,"abcdef",函数调用操作符至少有一个操作数。

6.3访问一个结构的成员

.            结构体.成员名

->          结构体指针->成员名

这里简单的讲一下结构体,后续还会进行深入讲解:

结构体(自定义类型)(聚合类型),生活中有些对象要被描述的话,就不能简单的使用内置类型,比如我们要描述一本书,那不可能只描述一个信息,例如:书名,作者,出版社,定价等等。

例如下列代码:

//类型
struct Book
{
    char name[20];//书名
    char author[30];//作者
    int price;
};

int main( )
{
    struct Book b1={"C语言教程","张三",1000};
    struct Book b2={"这是书","李四",999};
    print("《%s》 %s %d\n",b1.name,b1.author,b1.price);
    return 0;
}

通过定义一个类型结构体,在结构体里面加入成员,定义每个成员,之后在主函数里面进行初始化,初始化过后就可以通过 . 来访问,分别访问里面的成员。

如果是结构体指针的话:

void printf(struct Book * p)
{
    printf("%s %s %d\n",(*p).name,(*p).author,(*p).price);
    printf("%s %s %d\n",p->name,p->author,p->price);
}

 就用->后面接成员名来进行访问。结构体变量就用 . 进行访问。

7.表达式求值

表达式求值的顺序一部分是由操作符的优先级和结合性决定。同样,有些表达式的操作数在求值的过程中可能需要转换为其它类型。

7.1隐式类型转换

C的整形算数运算总是至少以缺省整形类型的精度来进行的。为了获得这个精度,表达式中的字符和短整形操作数在使用之前被转换为普通整形,这种转换称为整形提升。

整形类型是用于类型小于整形的。

我们知道char 1个字节  short 2个字节  int 4个字节

整型提升就是把char或者是short类型变成int类型,之后再参与运算。

就是偷偷的进行着类型转换,它是在一直运行着,只是我们看不见。

int main( )
{
    char a =3;
    char b=127;
    char c= a + b;
    printf("%d\n",c);
    return 0;   
}

上述代码就是一个例子。a,b都要提升为整形,之后才会进行运算。

整形提升的意义:

表达式的整形运算要在CPU的相应运算器件内执行,CPU内整形运算器(ALU)的操作数的字节长度一般就是int的字节长度,同时也是CPU的通用寄存器的长度。

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

通用CPU(general-purpose CPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int 长度的整形值,都必须先转换为int或者unsigned int,然后才能送入CPU去执行运算。

如何进行整型提升?

假如是负数,那么提升整形的时候,高位补充符号位,负数的符号位是1,所以补的是1,如果有一个字符类型的负数,字符类型是一个字节,一个字节八个比特位,所以高位补充1也就是还需要前面补上24个1。

假如是正数的话,那么提升整形的时候,高位补充符号位,正数的符号位是0,所以高位补0。之后的与负数一样。

对于无符号整形的提升,高位补的是0。

提升之后是补码,还要转化成原码,也就是减一取反。(或者先取反再加1)

char 有符号取值范围:-128~127

        无符号的取值范围:0~255

假设这里是有符号的char,因为char是一个字节(八个比特位),八个比特位的所有可能是总共256种,最高位是符号位,符号位为0的是正数,符号位为1的是负数,正数就按补码来读就行,也就到01111111,也就是127,所以正数就是0~127,负数因为是补码,所以先减去一,再按位取反,11111111就是-1,10000001就是-127,10000000直接被解析成-128,所以范围就是-129~127。

假设这里是无符号的char,所以就没有符号了,也就是0~255。

一般不同类型进行整型提升后它也就与之前的不同了,也就是不相等了,但是如果之前就是整形,那么就不会发生整形提升,比较后不变任然相等。

7.2算数转换

如果某个操作符的各个操作数属于不同类型,那么除非其中一个操作数的转换为另一个操作数的类型,否则操作就无法进行。下面的层次体系称为寻常算数转换:

long double

double

unsigned long int

long int

unsigned int

int

例如如果计算一个int和一个float类型,那么就先把int转换成float类型,之后再进行计算,按照上面的顺序向上转换,这也是一个隐式转换。

如果某个操作数的类型在上面这个列表中排名较低,那么就要先转换成那个排名高的那个类型,之后再进行执行运算。

算数转换要合理,要不然会出现一些潜在问题:

float f =3.15;
int num = f;

这里发生隐式转换,但是发生了精度缺失。

7.3操作符的属性

复杂表达式求值有三个影响的因素:

1.操作符的优先级

2.操作符的结合性

3.是否控制求值顺序

两个相邻的操作符先执行哪一个,取决于它们的优先级,如果两者的优先级相同,取决于它们的结合性。

下面就是操作符优先级的表:

优先级

运算符名称或含义使用形式结合方向说明

       1

   []

数组下标数组名[长度]

  从左往右

()小括号(表达式)或
函数名(形参表)
  .取成员结构体名.成员
  ->指针结构体指针->成员

       2

  -负号运算符-表达式

  从右往左

单目运算符
()强制类型转换(数据类型)表达式
  ++自增运算符++变量或变量++单目运算符
  --自减运算符--变量或变量--单目运算符
  *取内容*指针变量单目运算符
  &取地址&变量名单目运算符
  !逻辑非!表达式单目运算符
  ~按位取反~整型表达式单目运算符
  sizeof求长度sizeof(表达式)单目运算符

       3 

 /表达式 / 表达式

  从左往右

双目运算符
 *表达式 * 表达式双目运算符
  %取余表达式 / 表达式双目运算符

       4

 +表达式+表达式

  从左往右

双目运算符
  -表达式-表达式双目运算符

       5

<<左移变量<<表达式

  从左往右

双目运算符
>>右移变量<<表达式双目运算符

       6

>大于表达式>表达式

  从左往右

双目运算符
>=大于或等于表达式>=表达式双目运算符
<小于表达式<表达式双目运算符
<=小于或等于表达式<=表达式双目运算符

       7

 ==等于表达式==表达式

  从左往右

双目运算符
 !=不等于表达式!=表达式双目运算符
       8  &按位与表达式&表达式  从左往右双目运算符
       9  ^按位异或表达式^表达式  从左往右 双目运算符
      10  |按位或表达式|表达式  从左往右双目运算符
      11&&逻辑与表达式&&表达式  从左往右双目运算符
      12||逻辑或表达式||表达式  从左往右双目运算符

      13

?:条件运算符表达式1? 表达式2: 表达式3

  从右往左

三目运算符

      14

=赋值运算符变量=表达式

  从右往左

双目运算符

/=除后再赋值变量/=表达式
*=乘后再赋值变量*=表达式
%=取余后再赋值变量%=表达式
+=加后再赋值变量+=表达式
-=减后再赋值变量-=表达式
<<=左移再赋值变量<<=表达式
>>=右移再赋值变量>>=表达式
&=按位与再赋值变量&=表达式
^=按位异或再赋值变量^=表达式
|=按位或再赋值变量|=表达式
      15,逗号表达式表达式,表达式,…  从左往右

如果优先级不知道,就可以加括号。

只有相邻符号才讨论优先级。优先级高的先算,优先级低的后算。优先级相同的时候取决于结合性。有一些操作符会影响求值顺序,逻辑与、逻辑或、条件逻辑符、逗号表达式,都可以控制求值顺序。

注意在写代码时候的时候,不要写问题代码,那样的话编译器会不知道该如何运行,最后的结果就会出现错误,有些问题代码而且在不同的编译器下结果都不同。

我们写出的表达式如果不能通过操作符的属性确定唯一的计算路径,那么这个表达式就是存在问题的。


总结

这就是所有操作符的讲解和分析。

标题“51单片机通过MPU6050-DMP获取姿态角例程”解析 “51单片机通过MPU6050-DMP获取姿态角例程”是一个基于51系列单片机(一种常见的8位微控制器)的程序示例,用于读取MPU6050传感器的数据,并通过其内置的数字运动处理器(DMP)计算设备的姿态角(如倾斜角度、旋转角度等)。MPU6050是一款集成三轴加速度计和三轴陀螺仪的六自由度传感器,广泛应用于运动控制和姿态检测领域。该例程利用MPU6050的DMP功能,由DMP处理复杂的运动学算法,例如姿态融合,将加速度计和陀螺仪的数据进行整合,从而提供稳定且实时的姿态估计,减轻主控MCU的计算负担。最终,姿态角数据通过LCD1602显示屏以字符形式可视化展示,为用户提供直观的反馈。 从标签“51单片机 6050”可知,该项目主要涉及51单片机和MPU6050传感器这两个关键硬件组件。51单片机基于8051内核,因编程简单、成本低而被广泛应用;MPU6050作为惯性测量单元(IMU),可测量设备的线性和角速度。文件名“51-DMP-NET”可能表示这是一个与51单片机及DMP相关的网络资源或代码库,其中可能包含C语言等适合51单片机的编程语言的源代码、配置文件、用户手册、示例程序,以及可能的调试工具或IDE项目文件。 实现该项目需以下步骤:首先是硬件连接,将51单片机与MPU6050通过I2C接口正确连接,同时将LCD1602连接到51单片机的串行数据线和控制线上;接着是初始化设置,配置51单片机的I/O端口,初始化I2C通信协议,设置MPU6050的工作模式和数据输出速率;然后是DMP配置,启用MPU6050的DMP功能,加载预编译的DMP固件,并设置DMP输出数据的中断;之后是数据读取,通过中断服务程序从DMP接收姿态角数据,数据通常以四元数或欧拉角形式呈现;再接着是数据显示,将姿态角数据转换为可读的度数格
MathorCup高校数学建模挑战赛是一项旨在提升学生数学应用、创新和团队协作能力的年度竞赛。参赛团队需在规定时间内解决实际问题,运用数学建模方法进行分析并提出解决方案。2021年第十一届比赛的D题就是一个典型例子。 MATLAB是解决这类问题的常用工具。它是一款强大的数值计算和编程软件,广泛应用于数学建模、数据分析和科学计算。MATLAB拥有丰富的函数库,涵盖线性代数、统计分析、优化算法、信号处理等多种数学操作,方便参赛者构建模型和实现算法。 在提供的文件列表中,有几个关键文件: d题论文(1).docx:这可能是参赛队伍对D题的解答报告,详细记录了他们对问题的理解、建模过程、求解方法和结果分析。 D_1.m、ratio.m、importfile.m、Untitled.m、changf.m、pailiezuhe.m、huitu.m:这些是MATLAB源代码文件,每个文件可能对应一个特定的计算步骤或功能。例如: D_1.m 可能是主要的建模代码; ratio.m 可能用于计算某种比例或比率; importfile.m 可能用于导入数据; Untitled.m 可能是未命名的脚本,包含临时或测试代码; changf.m 可能涉及函数变换; pailiezuhe.m 可能与矩阵的排列组合相关; huitu.m 可能用于绘制回路图或流程图。 matlab111.mat:这是一个MATLAB数据文件,存储了变量或矩阵等数据,可能用于后续计算或分析。 D-date.mat:这个文件可能包含与D题相关的特定日期数据,或是模拟过程中用到的时间序列数据。 从这些文件可以推测,参赛队伍可能利用MATLAB完成了数据预处理、模型构建、数值模拟和结果可视化等一系列工作。然而,具体的建模细节和解决方案需要查看解压后的文件内容才能深入了解。 在数学建模过程中,团队需深入理解问题本质,选择合适的数学模
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

九离十

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

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

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

打赏作者

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

抵扣说明:

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

余额充值