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运⾏结果:

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

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

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

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

总结

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

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

相关文章
|
30天前
|
存储 C语言
如何在 C 语言中实现结构体的深拷贝
在C语言中实现结构体的深拷贝,需要手动分配内存并逐个复制成员变量,确保新结构体与原结构体完全独立,避免浅拷贝导致的数据共享问题。具体方法包括使用 `malloc` 分配内存和 `memcpy` 或手动赋值。
42 10
|
1月前
|
存储 缓存 C语言
【c语言】简单的算术操作符、输入输出函数
本文介绍了C语言中的算术操作符、赋值操作符、单目操作符以及输入输出函数 `printf` 和 `scanf` 的基本用法。算术操作符包括加、减、乘、除和求余,其中除法和求余运算有特殊规则。赋值操作符用于给变量赋值,并支持复合赋值。单目操作符包括自增自减、正负号和强制类型转换。输入输出函数 `printf` 和 `scanf` 用于格式化输入和输出,支持多种占位符和格式控制。通过示例代码详细解释了这些操作符和函数的使用方法。
39 10
|
29天前
|
存储 大数据 编译器
C语言:结构体对齐规则
C语言中,结构体对齐规则是指编译器为了提高数据访问效率,会根据成员变量的类型对结构体中的成员进行内存对齐。通常遵循编译器默认的对齐方式或使用特定的对齐指令来优化结构体布局,以减少内存浪费并提升性能。
|
1月前
|
编译器 C语言
共用体和结构体在 C 语言中的优先级是怎样的
在C语言中,共用体(union)和结构体(struct)的优先级相同,它们都是用户自定义的数据类型,用于组合不同类型的数据。但是,共用体中的所有成员共享同一段内存,而结构体中的成员各自占用独立的内存空间。
|
1月前
|
存储 C语言
C语言:结构体与共用体的区别
C语言中,结构体(struct)和共用体(union)都用于组合不同类型的数据,但使用方式不同。结构体为每个成员分配独立的内存空间,而共用体的所有成员共享同一段内存,节省空间但需谨慎使用。
|
1月前
|
编译器 C语言 C++
C语言结构体
C语言结构体
28 5
|
1月前
|
编译器 Linux C语言
C语言 之 结构体超详细总结
C语言 之 结构体超详细总结
21 0
|
1月前
|
存储 编译器 Linux
深入C语言:探索结构体的奥秘
深入C语言:探索结构体的奥秘
|
1月前
|
存储 编译器 C语言
c语言回顾-结构体(2)(下)
c语言回顾-结构体(2)(下)
31 0
|
1月前
|
存储 编译器 程序员
c语言回顾-结构体(2)(上)
c语言回顾-结构体(2)(上)
30 0