C语言操作符详解2(含结构体、整型提升、算术转换)

news2024/9/21 21:44:29

文章目录

  • 一、逗号表达式
  • 二、下标访问操作符[]、函数调用操作符()
    • 1.下标访问操作符[]
    • 2.函数调用操作符
  • 三、结构成员访问操作符
    • 1.结构体
    • 2.结构的声明
    • 3.结构体变量的定义和初始化
      • (1)结构体变量的定义
      • (2)结构体变量的初始化
    • 4.结构成员访问符
      • (1)结构成员的直接访问
      • (2)结构成员的间接访问
  • 四、操作符的属性:优先性、结合性
    • 1.优先级
    • 2.结合性
  • 五、整型提升和算术转换
    • 1.整型提升
    • 2.算术转换
  • 六、表达式求值举例
    • 举例1
    • 举例2
    • 举例3
    • 举例4
    • 举例5
    • 6.总结

一、逗号表达式

    逗号表达式就是由逗号隔开的表达式,如:

 exp1, exp2, exp3 ,···expN

    可以有n个表达式,中间由逗号隔开,它的特点就是每一个表达式都要进行运算,但是整个逗号表达式的结果是最后一个表达式的结果,如图下代码:
在这里插入图片描述

    根据它的特点,首先我们要将每个表达式依次进行运算,然后取最后一个表达式的值作为最后的结果,那可能就有同学要问了,为什么要一个一个把前面表达式的值算出来,再取最后表达式的值作为结果,而不是直接算出最后表达式的结果
    在我们上面举的例子就进行了很好的说明,后面表达式的b = a +1,而前面的表达式a = b +10,对a的值产生了影响,也就对最后的表达式表达式的b = a +1产生了影响,所以在计算逗号表达式时,我们要依次算出每个表达式的值,然后将最后一个表达式的值作为整个逗号表达式的结果
    接着我们来看看上面所示代码的结果,首先第一个表达式是a > b,它的结果为假,也就是0,但是没有用变量存起来什么的,没有什么作用,第二个表达式的结果为12,然后将12赋值给了a,现在a就是12,后面那个表达式a单独存在也没有用,直接看最后一个表达式,b = a +1,表达式的结果为13,然后将13赋值给了b,所以由此得出a的值为12,b的值13,c的值为13,代码运行结果如图:
在这里插入图片描述

二、下标访问操作符[]、函数调用操作符()

    这两个操作符我们都很熟悉了,这里简单再次介绍一下

1.下标访问操作符[]

    它是一个双目操作符,它的两个操作数是数组名和一个索引值(下标),相信大家对它已经很熟了,在数组中已经讲过,这里举个例子:

int arr[10];//创建数组
arr[9] = 10;//使⽤下标访问操作符。
[ ]的两个操作数是arr和9

2.函数调用操作符

    接受⼀个或者多个操作数:第⼀个操作数是函数名,剩余的操作数就是传递给函数的参数,但是至少会接受一个操作数,就是函数名,因为函数可能没有参数,比如我们之前在猜数字游戏和扫雷游戏中的菜单,menu()函数,它就没有参数,这里的函数调用操作符()就只有一个操作数函数名menu
    再比如我们之前写的函数Add,它可以实现两个整型的相加,如图:
在这里插入图片描述
    这里的函数调用操作符()就有三个操作数,分别是函数名Add、以及参数2、3,

三、结构成员访问操作符

    这里我们简单介绍一下结构体和结构成员访问操作符,后期会专门出一篇博客来讲解结构体

1.结构体

    C语⾔已经提供了内置类型,如:char、short、int、long、float、double等,但是只有这些内置类型还是不够的,假设我想描述学⽣,描述⼀本书,这时单⼀的内置类型是不⾏的。
    描述⼀个学⽣需要名字、年龄、学号、⾝⾼、体重等;
    描述⼀本书需要作者、出版社、定价等。C语⾔为了解决这个问题,增加了结构体这种⾃定义的数据类型,让程序员可以⾃⼰创造适合的类型
在这里插入图片描述

2.结构的声明

    需要使用关键字struct,具体格式如下:

struct 结构体名
{
 成员列表
}变量列表;
//变量列表可以省略,但是最后的分号不能丢

    以上是语法规定的格式,成员列表就是这个结构体有哪些具体的结构体变量,变量列表就是,可以直接在那里创建结构体变量,也可以省略不创建,但是分号不能丢,看了是不是还是有点懵,我们具体举例来看看如何用结构体来描述一个学生:

struct Stu//结构体名
{
//成员列表:
 char name[20];//名字
 int age;//年龄
 char sex[5];//性别
 char id[20];//学号
}; //分号不能丢

3.结构体变量的定义和初始化

(1)结构体变量的定义

    对结构体变量的定义有两种方式,一种是直接在创建结构体时,在最后直接写出结构体变量,注意分号不能丢,如下:

struct Point
{
 int x;
 int y;
}p1; //声明时定义变量p1

    第二种就是像定义变量那样定义结构体变量,只要清楚一点:struct Point相当于就是它的类型,然后就跟普通的变量定义相同了,如下:

struct Point
{
 int x;
 int y;
};//分号不能少

//将struct Point当作
struct Point P2;

(2)结构体变量的初始化

    结构体变量的初始化和数组有点相似,用大括号括起来,最简单的方法就是直接按顺序初始化,如:

struct Stu //类型声明
{
 char name[15];//名字
 int age; //年龄
};
struct Stu s1 = {"zhangsan", 20};//初始化

    如果我不想按顺序初始化,比如我想先初始化年龄,然后再初始化名字,就要写出成员名,并且在具体成员名前加一个.,然后初始化,如下:

struct Stu
{
  char name[15];
  int age;
};
struct Stu s1 = {.age=20 , .name="zhangsan"};

    当然,也会有特殊情况,比如当结构体当中嵌套了一个结构体,如下:

struct Point
{
 int x;
 int y;
};

struct Node
{
 int data;
 struct Point p;
 }

    现在结构体Node的成员包含了一个结构体,这种情况怎么初始化呢?这个时候就要使用大括号中的大括号,如下:

struct Point
{
 int x;
 int y;
};

struct Node
{
 int data;
 struct Point p;
 }n1 = {10, {4,5}};

struct Node n = {20, {5, 6}};

4.结构成员访问符

(1)结构成员的直接访问

    结构体成员的直接访问是通过点操作符(.)访问的,点操作符接受两个操作数,如下所⽰:
在这里插入图片描述
    我们定义了一个结构体,并且创建了一个结构体变量n,我们如何访问结构体变量n中的成员呢,比如我要打印n中的x,这个时候就要用到点操作符,如下图所示:
在这里插入图片描述

(2)结构成员的间接访问

    有时候我们得到的不是⼀个结构体变量,⽽是得到了⼀个指向结构体的指针,这个时候我们就可能会使用间接访问的方式来访问结构体,就会使用(->)符号,但是由于涉及到指针,这里只做简单介绍,在后续指针再讲,而结构体这个知识后面也会专门写博客讲解

四、操作符的属性:优先性、结合性

    C语⾔的操作符有2个重要的属性:优先级、结合性,这两个属性决定了表达式求值的计算顺序

1.优先级

    优先级指的是,如果⼀个表达式包含多个运算符,哪个运算符应该优先执⾏。各种运算符的优先级是不⼀样的,举一个简单的例子:

3 + 4 * 5;

    上⾯⽰例中,表达式 3 + 4 * 5 ⾥⾯既有加法运算符( + ),⼜有乘法运算符( * )。由于乘法的优先级⾼于加法,所以会先计算 4 * 5 ,⽽不是先计算 3 + 4
    由于运算符的优先级顺序很多,下⾯是部分运算符的优先级顺序(按照优先级从⾼到低排列),建议⼤概记住这些操作符的优先级就⾏,其他操作符在使⽤的时候查看下⾯表格就可以了

  • 圆括号( () )
  • ⾃增运算符( ++ ),⾃减运算符( – )
  • 单⽬运算符( + 和 - )
  • 乘法( * ),除法( / )
  • 加法( + ),减法( - )
  • 关系运算符( < 、 > 等)
  • 赋值运算符( = )
    在这里插入图片描述
    参考:https://zh.cppreference.com/w/c/language/operator_precedence

2.结合性

    如果两个运算符优先级相同,优先级没办法确定先计算哪个了,如:

 5 * 6 / 2;

    这时候就看结合性了,则根据运算符是左结合,还是右结合,决定执⾏顺序。而乘除属于左结合,就从左到右计算,⼤部分运算符是左结合(从左到右执⾏),少数运算符是右结合(从右到左执⾏),⽐如赋值运算符( = )下面是运算符的优先级和结合性表,只需要记住最常用的那些,不常用的可以等需要时再查找
在这里插入图片描述
参考:https://zh.cppreference.com/w/c/language/operator_precedence

五、整型提升和算术转换

1.整型提升

    C语⾔中整型算术运算总是⾄少以缺省(默认)整型类型的精度来进⾏的,为了获得这个精度,表达式中的字符和短整型操作数在使⽤之前被转换为普通整型,这种转换称为整型提升
    整型提升的意义:表达式的整型运算要在CPU的相应运算器件内执⾏,CPU内整型运算器(ALU)的操作数的字节⻓度⼀般就是int的字节⻓度,同时也是CPU的通⽤寄存器的⻓度。因此,即使两个char类型的相加,在CPU执⾏时实际上也要先转换为CPU内整型操作数的标准⻓度
    通⽤CPU(general-purpose CPU)是难以直接实现两个8⽐特字节直接相加运算(虽然机器指令中可能有这种字节相加指令)。所以,表达式中各种⻓度可能⼩于int⻓度的整型值,都必须先转换为int或unsigned int,然后才能送⼊CPU去执⾏运算,如:

/实例1
char a,b,c;
...
a = b + c;

    这里的b和c都是char类型,只有一个字节,要被提升为普通整型,然后再执⾏加法运算,加法运算完成之后,这个时候会有四个字节,要重新存储进char类型的变量a中,所以结果将被截断,也就是将前面3个字节去掉,然后再存储于a中
    如何进行整形提升呢?

1. 有符号整数提升是按照变量的数据类型的符号位来提升的,高位补符号位
2. ⽆符号整数提升,⾼位补0

    下面举一个实例说明:
在这里插入图片描述

    这里的c1,c2变量都是char类型,现在要对它们进行相加,就要先进行整形提升,然后再相加,最后把相加的值赋给c3,由于进行了整型提升,所以最后要进行截断,我们首先算出c1的补码,然后进行整型提升,由于这里c1是有符号整数,填充符号位补齐4个字节,过程如下:
在这里插入图片描述
    char 1的提升类似,只是符号位变成了0,如下:
在这里插入图片描述
    随后我们将整型提升得到的结果相加:
在这里插入图片描述
    我们来看看程序运行结果:
在这里插入图片描述

2.算术转换

    如果某个操作符的各个操作数属于不同的类型,那么除⾮其中⼀个操作数转换为另⼀个操作数的类型,否则操作就⽆法进⾏。下⾯的层次体系称为寻常算术转换

long double
double
float
unsigned long int
long int
unsigned int
int

    如果某个操作数的类型在上⾯这个列表中排名靠后,那么⾸先要转换为另外⼀个操作数的类型后执⾏运算,比如整型和长整型相加,那么就要将整型转换为长整型

六、表达式求值举例

举例1

//表达式的求值部分由操作符的优先级决定
a*b + c*d + e*f

    在计算的时候,由于 * ⽐ + 的优先级⾼,只能保证, * 的计算是⽐ + 早,但是优先级并不能决定第三个 * ⽐第⼀个 + 早执⾏,所以上述表达式的计算顺序可能是:

a*b
c*d
a*b + c*d
e*f
a*b + c*d + e*f

或者

a*b
c*d
e*f
a*b + c*d
a*b + c*d + e*f

举例2

int main()
{
	int a = 2;
	int b = a + --a;
	printf("%d\n", b);
	return 0;
}

    同上,操作符的优先级只能决定⾃减 – 的运算在 + 的运算的前⾯,但是我们并没有办法得知, + 操作符的左操作数的获取在右操作数之前还是之后求值,所以结果是不可预测的,是有歧义的
(1)如果先获取+的左操作数,那么左边就是2,右边就是1,加起来就是3
(2)如果先获取+的右操作数,那么右边就是1,此时左边也变成了1,加起来就是2
    在不同环境下,不同编译器上,可能结果不同,比如VS2022上,结果就为2,所以可以得出,即使规定了优先级和结合性,也不能够保证一个表达式计算的唯一路径,所以为了保证唯一的计算路径,我们可以将一个复杂表达式拆成多个,这样才没有歧义,比如我想得到(1)的结果,我就可以这样写:

int main()
{
	int a = 2;
	int b = a;
	a--;
	b+=a;
	printf("%d\n", b);
	return 0;
}

    虽然更加麻烦,但是确保了计算的唯一路径

举例3

//表达式3
int main()
{
 int i = 10;
 i = i-- - --i * ( i = -3 ) * i++ + ++i;
 printf("i = %d\n", i);
 return 0;
}

    这个可以不用算了,实在太复杂了,不仅我们无从下手,连编译器都很懵,在不同的编译器上,结果基本上都不同,如下:
在这里插入图片描述
    所以一定要规范的写表达式,避免歧义

举例4

#include <stdio.h>
int fun()
{
 static int count = 1;
 return ++count;
}

int main()
{
 int answer;
 answer = fun() - fun() * fun();
 printf( "%d\n", answer);//输出多少?
 return 0;
}

    这个代码有没有实际的问题?有问题!虽然在⼤多数的编译器上求得结果都是相同的,但是上述代码 answer = fun() - fun() * fun(); 中我们只能通过操作符的优先级得知:先算乘法,再算减法,但是函数的调⽤先后顺序⽆法通过操作符的优先级确定

举例5

#include <stdio.h>
int main()
{
 int i = 1;
 int ret = (++i) + (++i) + (++i);
 printf("%d\n", ret);
 printf("%d\n", i);
 return 0;
}
//尝试在linux 环境gcc编译器,VS2013环境下都执⾏,看结果

在这里插入图片描述
在这里插入图片描述
    看看同样的代码产⽣了不同的结果,这是为什么?
    简单看⼀下汇编代码,就可以分析清楚,这段代码中的第⼀个 + 在执⾏的时候,第三个++是否执⾏,这个是不确定的,因为依靠操作符的优先级和结合性是⽆法决定第⼀个 + 和第三个前置 ++ 的先后顺序

6.总结

    即使有了操作符的优先级和结合性,我们写出的表达式依然有可能不能通过操作符的属性确定唯⼀的计算路径,那这个表达式就是存在潜在⻛险的,建议不要写出特别复杂的表达式

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2107060.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据结构(三)——双向链表,循环链表,内核链表,栈和队列

双链表 产生原因&#xff1a;单链表只有一个指向后继的指针&#xff0c;如果要访问某节点的前驱结点&#xff0c;只能从头遍历&#xff0c;也就是访问后继节点的时间复杂度为1&#xff0c;访问前驱结点的时间复杂度为n。 而引入双链表使得在插入、删除的…

C语言代码练习

今日练习&#xff1a; 34、通过指向结构体变量的指针变量变量输出结构体变量中的信息。 35、建立一个简单链表&#xff0c;它由3个学生数据的结点组成&#xff0c;要求输出各结点中的数据。36、从键盘输入一些字符&#xff0c;逐个把他们送到磁盘上去&#xff0c;直到用户输入一…

【电机控制】永磁同步电机FOC控制—电流环PI参数整定

0. 前言 在了解电流环PI参数整定之前我们需要有以下几点基础知识&#xff1a; 1、了解什么是传递函数、传递函数的零点和极点。 2、了解极点对系统稳定性的影响。 有基础的小伙伴可以跳着看。 1. 传递函数与零点极点 1.1 传递函数 系统传递函数G(s)的定义是&#xff1a;在指零…

Pytorch – YOLOv9自定义资料训练

本篇将讲解目前最新推出的YOLOv9搭配Roboflow进行自定义资料标注训练流程&#xff0c;透过Colab上进行实作说明&#xff0c;使大家能够容易的了解YOLOv9的使用。 ►YOLO框架下载与导入 ►Roboflow的资料收集与标注 进入Roboflow官网&#xff0c;点选右上Sign up注册自己的帐号…

网站设计公司设计费用

网站设计公司的设计费用往往是一个让人头疼的问题。设计费用的高低涉及到多个因素&#xff0c;包括公司规模、项目复杂性、设计师经验等。本文将深入探讨网站设计公司的设计费用&#xff0c;并提供一些建议&#xff0c;帮助您在有限预算下实现最佳性价比。 1. 项目规模与复杂性…

GitHub 上 Stars 数量最多的 8 个开源 CRUD 项目

继续我们的 GitHub Star 系列&#xff01;这是本系列的第四篇文章&#xff0c;之前的内容&#xff1a; GitHub Star 数量前 12 的开源无代码工具GitHub Star 数量前 15 的开源低代码项目GitHub Star 数量前 11 的开源内部工具 本期我们来盘点 CRUD 项目。在软件开发中&#x…

爬虫练习(猫眼电影解密)

问题 随便拿一篇电影做样例。我们发现猫眼的页面数据在预览窗口中全是小方框。在当我们拿到源码以后&#xff0c;数据全是加密后的。所以我们需要想办法破解加密&#xff0c;拿到数据。 破解过程 1.源码获取问题与破解 分析 在我们刚刚请求url的时候是可以得到数据的&#xff…

[Linux] 操作系统 入门详解

标题&#xff1a;[Linux] 操作系统 水墨不写bug 目录 一、冯 . 诺依曼体系结构 1.冯诺依曼体系结构简介 2.对冯诺依曼体系结构的理解 二、操作系统定位 1.为什么需要操作系统&#xff1f; 2.操作系统是什么&#xff1f; 三、系统调用和库函数 正文开始&#xff1a; …

可交互、会学习、自成长机器人——李德毅院士

在以“农业无人农场”为主题的中国工程科技论坛上&#xff0c;中国工程院院士、欧亚科学院院士、中国人工智能学会和中国指挥与控制学会名誉理事长&#xff0c;中科原动力首席科学家李德毅院士应邀做题为《机器具身交互智能》的演讲。李德毅院士表示&#xff0c;智能机器不但把…

CentOS7单机环境安装k8s集群

目录 1、环境准备 2、安装依赖工具 3、配置 Kubernetes 的国内 Yum 源 4. 安装 Kubernetes 组件 5、初始化 Kubernetes 集群 1. 容器运行时没有正常运行 1.1. 可能的原因 1.2. 解决办法 2. 初始化拉取镜像卡住 2.1. 使用国内的镜像源&#xff08;无法解决问题&#x…

机器学习之监督学习(二)二元逻辑回归

机器学习之监督学习&#xff08;二&#xff09;逻辑回归(二元分类问题&#xff09; 1. 分类 classification2.二元分类逻辑回归 binary-classified logistic regression模块1: sigmoid 激活函数 sigmoid function模型公式模块2: 决策边界 decision boundary代价函数梯度下降欠拟…

【Redis】Redis 主从复制原理与配置详解:解决单点故障与性能瓶颈的最佳方案

目录 主从复制配置建立复制断开复制安全性只读传输延迟 拓扑⼀主⼀从结构一主多从结构树形主从结构 原理复制过程数据同步 psync全量复制部分复制实时复制 小结 主从复制 这部分相关操作不需要记忆!!! 后续⼯作中如果⽤到了能查到即可. 重点理解流程和原理. 单点问题&#xff1…

onvif应用--IPC鉴权(认证)

一、鉴权原理 1&#xff09;onvif的用户验证&#xff0c;是基于WS_UsernameToken&#xff0c;所谓的WS_UsernameToken加密&#xff0c;就是将用户名、密码、Nonce、Created都包含在了header里面 参数 意义 username待认证的用户名Nonce客户端随机产生的字符串Created请求认证…

arXiv风评被害?

arXiv “风评被害”&#xff1f; arXiv是一个在学术界具有重要影响力的开放预印本论文网站&#xff0c;自三十多年前创立以来&#xff0c;它已经成为了物理学、计算机科学、统计学等科学论文最重要的发布平台之一&#xff0c;同时也是众多科研人员分享和交流研究成果的重要渠道…

单片机工程师:创新与挑战之路

摘要&#xff1a;本文全面深入地探讨了单片机工程师这一职业角色。详细阐述了单片机工程师的职责范围、所需技能&#xff0c;包括硬件设计、软件编程、调试与测试等方面。分析了单片机在不同领域的应用&#xff0c;如工业控制、消费电子、智能家居等。同时&#xff0c;探讨了单…

【计算机组成原理】计算机系统的基本组成

文章目录 计算机硬件的基本组成早期的冯诺依曼机冯诺依曼结构冯诺依曼机的特点 现代计算机现代计算机的结构 各硬件的工作原理主存储器主存储器的基本组成 运算器运算器的基本组成 控制器控制器的基本组成 计算机软件 计算机硬件的基本组成 早期的冯诺依曼机 冯诺依曼在研究 …

图纸文件怎么加密?2024六款图纸加密软件推荐,个个好用不踩雷!

想象一下&#xff0c;公司的设计图纸被无意间泄露&#xff0c;结果对手提前推出了相似的产品。为了避免这种令人头疼的情况发生&#xff0c;图纸加密就显得尤为重要。 别担心&#xff0c;今天我们就带你了解2024年六款超好用的图纸加密软件&#xff0c;让你的图纸“安全感”爆…

业务复杂度治理方法论--十年系统设计经验总结

一、复杂度综述 1、什么是复杂度 软件设计的核心在于降低复杂性。 --《软件设计的哲学》 业界对于复杂度并没有统一的定义&#xff0c;斯坦福教授John Ousterhout从认知负担和工作量方面给出了一个复杂度量公式 &#xfeff; &#xfeff;&#xfeff; 子模块的复杂度cp乘…

快充协议方案的工作原理及场景应用

快充协议芯片是支持各种快充快充协议的芯片&#xff0c;它们能智能识别插入的设备类型&#xff0c;并根据设备的需求调整充电电压和电流&#xff0c;从而实现快速充电。 XSP08Q芯片是内置快充功能的协议芯片&#xff0c;它基于先进的充电技术&#xff0c;通过协商电压和电流&a…

【数据结构】二叉树顺序存储结构堆的应用以及解决TOP-K问题

文章目录 前言1. 堆的应用1.1 堆排序1.2 TOP-K问题 2. 结语 前言 前面我们学习了堆这个数据结构&#xff0c;这种数据结构是一种顺序结构存储的完全二叉树&#xff0c;现在我们来看一看堆的应用。 1. 堆的应用 1.1 堆排序 版本一&#xff1a;基于已有数组建堆、取堆顶元素完…