C语言的模型玩具:结构体的使用以及操作符优先级

简介: C语言的模型玩具:结构体的使用以及操作符优先级

各位少年,大家好,我是博主那一脸阳光,今天分享结构体的使用,声明以及操作符的使用。

前言:

如果你想做一块月饼,你要学习它的制作方法,需要准备面粉,白砂糖,奶粉,干果、坚果,冰皮月饼等材料,但我们还需要一个摸具来造成出原型的月饼。

根据我上面的描述大家明白了,做月饼不仅材料和馅料还需要个工具叫做容具,看上图半成品是到了容具之后出现了对应图形,那我们介绍这个有什么用呢?当然有用struct就是相当于一个容具的作用。

struct tag\\(结构体名字)
{
 member-list;\\(成员列表)
}variable-list;\\(变量列表)

结构体的意义

C语⾔已经提供了内置类型,如:char、short、int、long、float、double等,但是只有这些内置类

型还是不够的,假设我想描述学⽣,描述⼀本书,这时单⼀的内置类型是不⾏的。描述⼀个学⽣需要

名字、年龄、学号、⾝⾼、体重等;描述⼀本书需要作者、出版社、定价等。C语⾔为了解决这个问

题,增加了结构体这种⾃定义的数据类型,让程序员可以⾃⼰创造适合的类型。

结构是⼀些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量,如:
标量、数组、指针,甚⾄是其他结构体

代码例子

大家看到了结构体肯定想到了一个项目,叫做学生管理系统,今天我给大家分享其中较为重要一点和其制作的思路,好,我们先制作一个模板叫做学生,一个得有名字,学号,年龄,性别。

struct Stu
{
 char name[20];//名字
 int age;//年龄
 char sex[5];//性别
 char id[20];//学号
}; //分号不能丢
#include<stdio.h>
struct Stu
{
  char name[20];
  int age;
  float score;
}s3={"wangwu",33,66.0f),s4={"翠华",18,100.0};
int main()
{
  struct Stu s1 = { "aoao",20,95.5f};//局部变量
  return 0;
}

上面代码中,我们成功的从Stu这个摸具 来创建了一个我们这个代码,但是我们惊讶的发现,我们通过结构体创建了类似数组的东西,这东西就叫做结构体变量

结构体成员的直接访问

结构体成员的直接访问是通过点操作符(.)访问的。点操作符接受两个操作数。比如说我们要先输入学生的成绩或者学好,怎么办呢?这时候我们就需要(.)操作符了。使用如下

`

#include<stdio.h>
struct Stu
{
  char name[20];
  int age;
  float score;
}s3={"wangwu",33,66.0f),s4={"翠华",18,100.0};
int main()
{
  struct Stu s1 = { "aoao",20,95.5f};//局部变量
  struct Stu s5=(.score=98.5f, .name"hehe", .age=100.0);
  return 0;
}

结构体嵌套

#include<stdio.h>
struct Point
{
int x;
int y;
};
struct Data
{
int num;
struct Point p;
};
int main()
{
struct Data d={200,{10,11}};//这块的大括号都是需要用的
printf("numb=%d p.x=%d p.y=%d\n",d.num,d.p.x,d.p.y);//嵌套打印的方法
return 0;
}
.结构成员访问操作符
结构体变量成员名

结构体成员的指针

有时候我们得到的不是一个结构体变量,而是得到一个结构体的指针,如下展示。

struct Stu* ps = &s1;
  printf("%s %d %f", ps->name, ps->age, ps->score);

指针是什么?

指针另外一个名字叫做地址,和现实中的地址有异曲同工之妙,

比如说某个学校在长江路307号,这很显然就是个地址,哪计算机中的地址也是一样的。

上面代码中:取了s1的地址 &(取地址操作符),然后通过->打印出结构体出来。

#include <stdio.h>
#include <string.h>
struct Stu
{
 char name[15];//名字
 int age; //年龄
};
void print_stu(struct Stu s)
{
 printf("%s %d\n", s.name, s.age);
}
void set_stu(struct Stu* ps)接收参数
{
 strcpy(ps->name, "李四");
 ps->age = 28;
}
int main()
{
 struct Stu s = { "张三", 20 };
 print_stu(s);
 set_stu(&s);//传参
 print_stu(s);
 return 0;
}

上面代码中,通过指针传参的情况下传递了结构体,

哪自然打印的时候也需要->这个操作符来进行打印以及使用。

操作符的优先级结合性

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

例子

3+4*5

上⾯⽰例中,表达式 3 + 4 * 5 ⾥⾯既有加法运算符( + ),⼜有乘法运算符( * )。由于乘法

的优先级⾼于加法,所以会先计算 4 * 5 ,⽽不是先计算 3 + 4 。

优先级:(相邻操作符),优先级高的先执行,优先级低的后执行!
结合性

如果两个运算符优先级相同,优先级没办法确定先计算哪个了,这时候就看结合性了,则根据运算符

是左结合,还是右结合,决定执⾏顺序。⼤部分运算符是左结合(从左到右执⾏),少数运算符是右

结合(从右到左执⾏),⽐如赋值运算符( =

奉上一个C/C++中文版的官网,上面有函数和操作符优先级:https://zh.cppreference.com/w/c/language/operator_precedence

  • 和 / 的优先级相同,它们都是左结合运算符,所以从左到右执⾏,先计算 5 * 6 ,
    再计算 6 / 2 。
    运算符的优先级顺序很多,下⾯是部分运算符的优先级顺序(按照优先级从⾼到低排列),建议⼤概
    记住这些操作符的优先级就⾏,其他操作符在使⽤的时候查看下⾯表格就可以了。
    • 圆括号( () )
    • ⾃增运算符( ++ ),⾃减运算符( – )
    • 单⽬运算符( + 和 - )
    • 乘法( * ),除法( / )
    • 加法( + ),减法( - )
    • 关系运算符( < 、 > 等)
    • 赋值运算符( = )
    由于圆括号的优先级最⾼,可以使
    ⽤它改变其他运算符的优先级

表达式求值

整型提升

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

(简单意思就是说int类型大小是四个字节,char类型大小一个字节,short类型两个字节,但是编译器都会按int类型大小进行计算)就好比没成年不能进网吧道理一样,最后即使你进去会不会因为不够年龄被踢出来呢?我们接着往下看。

整形提升的意义

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

般就是int的字节⻓度,同时也是CPU的通⽤寄存器的⻓度。

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

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

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

int,然后才能送⼊CPU去执⾏运算。

b和c的值被提升为普通整型,然后再执⾏加法运算。

加法运算完成之后,结果将被截断,然后再存储于a中。

如何进⾏整体提升呢?

1. 有符号整数提升是按照变量的数据类型的符号位来提升的
2. ⽆符号整数提升,⾼位补0
char c1 = -1;
变量c1的⼆进制位(补码)中只有8个⽐特位:
1111111
因为 char 为有符号的 char
所以整形提升的时候,⾼位补充符号位,即为1
提升之后的结果是:
11111111111111111111111111111111
//正数的整形提升
char c2 = 1;
变量c2的⼆进制位(补码)中只有8个⽐特位:
00000001
因为 char 为有符号的 char
所以整形提升的时候,⾼位补充符号位,即为0
提升之后的结果是:
00000000000000000000000000000001
//⽆符号整形提升,⾼位补0

算术转换

如果某个操作符的各个操作数属于不同的类型,那么除⾮其中⼀个操作数的转换为另⼀个操作数的类

型,否则操作就⽆法进⾏。下⾯的层次体系称为寻常算术转换。

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

如果某个操作数的类型在上⾯这个列表中排名靠后,那么⾸先要转换为另外⼀个操作数的类型后执⾏

运算。

问题表达式解析

a*b + c*d + e*f

表达式1在计算的时候,由于 * ⽐ + 的优先级⾼,只能保证, * 的计算是⽐ + 早,但是优先级并不

能决定第三个 * ⽐第⼀个 + 早执⾏。

所以表达式的计算机顺序就可能是:

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

`

//表达式2

c + --c; 
``

同上,操作符的优先级只能决定⾃减 – 的运算在 + 的运算的前⾯,但是我们并没有办法得知, + 操

作符的左操作数的获取在右操作数之前还是之后求值,所以结果是不可预测的,是有歧义的。`

表达式3

``

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

表达式3在不同编译器中测试结果:⾮法表达式程序的结果

表达式4
#include <sdtio.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:
//表达式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环境下都执⾏,看结果。

gcc编译器执⾏结果:

VS2022运⾏结果:

看看同样的代码产⽣了不同的结果,这是为什么?

简单看⼀下汇编代码,就可以分析清楚.

这段代码中的第⼀个 + 在执⾏的时候,第三个++是否执⾏,这个是不确定的,因为依靠操作符的优先

级和结合性是⽆法决定第⼀个 + 和第三个前置 ++ 的先后顺序。

总结

即使有了操作符的优先级和结合性,我们写出的表达式依然有可能不能通过操作符的属性确定唯⼀的

计算路径,那这个表达式就是存在潜在⻛险的,建议不要写出特别负责的表达式。

相关文章
|
2月前
|
存储 C语言 索引
【C语言篇】操作符详解(下篇)
如果某个操作数的类型在上⾯这个列表中排名靠后,那么⾸先要转换为另外⼀个操作数的类型后执⾏运算。
|
2月前
|
程序员 编译器 C语言
【C语言篇】操作符详解(上篇)
这是合法表达式,不会报错,但是通常达不到想要的结果, 即不是保证变量 j 的值在 i 和 k 之间。因为关系运算符是从左到右计算,所以实际执⾏的是下⾯的表达式。
|
26天前
|
存储 C语言
C语言程序设计核心详解 第九章 结构体与链表概要详解
本文档详细介绍了C语言中的结构体与链表。首先,讲解了结构体的定义、初始化及使用方法,并演示了如何通过不同方式定义结构体变量。接着,介绍了指向结构体的指针及其应用,包括结构体变量和结构体数组的指针操作。随后,概述了链表的概念与定义,解释了链表的基本操作如动态分配、插入和删除。最后,简述了共用体类型及其变量定义与引用方法。通过本文档,读者可以全面了解结构体与链表的基础知识及实际应用技巧。
|
1月前
|
网络协议 安全 网络安全
C语言 网络编程(四)常见网络模型
这段内容介绍了目前被广泛接受的三种网络模型:OSI七层模型、TCP五层模型以及TCP/IP四层模型,并简述了多个网络协议的功能与特性,包括HTTP、HTTPS、FTP、DNS、SMTP、TCP、UDP、IP、ICMP、ARP、RARP及SSH协议等,同时提到了ssh的免费开源实现openssh及其在Linux系统中的应用。
|
1月前
|
C语言
c语言中的结构体
本文档详细介绍了C语言中结构体的使用方法,包括结构体的基本定义、变量声明与赋值、数组与指针的应用,以及结构体嵌套、与`typedef`结合使用等内容。通过示例代码展示了如何操作结构体成员,并解释了内存对齐的概念。
|
2月前
|
C语言
C语言结构体赋值的四种方式
本文总结了C语言结构体的四种赋值方式,并通过示例代码和编译运行结果展示了每种方式的特点和效果。
58 6
|
2月前
|
编译器 程序员 Linux
【C语言篇】结构体和位段详细介绍
跟结构相⽐,位段可以达到同样的效果,并且可以很好的节省空间,但是有跨平台的问题存在。
|
29天前
|
网络协议 数据处理 C语言
利用C语言基于poll实现TCP回声服务器的多路复用模型
此代码仅为示例,展示了如何基于 `poll`实现多路复用的TCP回声服务器的基本框架。在实际应用中,你可能需要对其进行扩展或修改,以满足具体的需求。
48 0
|
2月前
|
存储 C语言
C语言------结构体和共用体
这篇文章是关于C语言中结构体和共用体的实训,通过示例代码演示了结构体的定义、赋值、使用,以及如何使用结构体变量进行数据的组织和操作,包括输入、排序、求平均分和查找学生信息等功能。
C语言------结构体和共用体
|
2月前
|
C语言
C语言操作符(补充+面试)
C语言操作符(补充+面试)
39 6