目录
六、单目操作符
!、 ++ 、 -- 、 & 、 * 、 + 、 - 、 ~ 、 sizeof 、 ( 类型 )
上述操作符中,我们在前面都说过,只有&和*没有提及,这两个操作符我们会在指针章节详细介绍。
七、逗号表达式
a1,a2,a3,.....an
逗号表达式,就是用多个逗号隔开的多个表达式。
它是按从左到右 的顺序依次执行。整个表达式的结果是最后一个表达式的结果。
eg:
cpp
int a = 1;
int b = 2;
int c = (a>b, a=b+10, a, b=a+1);
首先,从左到右依次执行 ,先是a>b,然后把b+10赋给a,a就变成了12,最后再将a+1的值赋给b,这个表达式的值就是整个表达式的值,也就是13,所以c为13。
八、下标引用以及函数调用
8.1.下标引用
我们在数组中曾见过这对中括号,是的,它的名字叫下标引用。
操作数:数组名+索引值
eg:
cpp
int arr[10];//创建数组
arr[9] = 10;//实⽤下标引⽤操作符。
[]的两个操作数是arr和9。
8.2.函数调用
这个相信大家也不陌生。
操作数:函数名+参数
问:函数调用最少有几个操作数?
答:一个,只需要一个函数名即可。
cpp
#include <stdio.h>
void test1()
{
printf("hehe\n");
}
void test2(const char *str)
{
printf("%s\n", str);
}
int main()
{
test1(); //这⾥的()就是作为函数调⽤操作符。
test2("hello bit.");//这⾥的()就是函数调⽤操作符。
return 0;
}
接下来讲点没见过的。
九、结构体
我们今天只是简单介绍,后面还会继续详细介绍结构体(又挖坑)。
9.1.结构体
我们之前学过许多数据类型结构,像short、char、int、double......但只有这些其实远远不够,比如说我想描述一个学生的信息,身高体重各科成绩等等。C语言为了解决这个问题,内置了结构体这种自定义类型,从此之后,我们可以创造出自己想要的类型。
结构是⼀些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量,如:
标量、数组、指针,甚⾄是其他结构体。
9.1.1结构的声明
cpp
struct tag
{
member-list;//成员列表
}variable-list //变量列表
其中tag表述结构体名,花括号里面放着成员列表,也就是要描述对象的各种属性。变量列表用来存放定义为该结构体类型的变量。
cpp
struct Stu
{
char name[20];//名字
int age;//年龄
char sex[5];//性别
char id[20];//学号
}; //分号不能丢
9.1.2结构体的定义和初始化
cpp
//代码1:变量的定义
struct Point
{
int x;
int y;
}p1; //声明类型的同时定义变量p1
struct Point p2; //定义结构体变量p2
//代码2:初始化。
struct Point p3 = {10, 20};
struct Stu //类型声明
{
char name[15];//名字
int age; //年龄
};
struct Stu s1 = {"zhangsan", 20};//初始化
struct Stu s2 = {.age=20, .name="lisi"};//指定顺序初始化
//代码3
struct Node
{
int data;
struct Point p;
struct Node* next;
}n1 = {10, {4,5}, NULL}; //结构体嵌套初始化
struct Node n2 = {20, {5, 6}, NULL};//结构体嵌套初始化
9.2.结构成员访问操作符
9.2.1直接访问
使用方式:结构体变量 **.**成员名
cpp
#include <stdio.h>
struct Point
{
int x;
int y;
}p = {1,2};
int main()
{
printf("x: %d y: %d\n", p.x, p.y);
return 0;
}
这个点很小,但是很有用!
9.2.2间接访问
有的时候,我们得到的是结构体的地址,
使用方式:结构体指针(地址)->成员名
举例如下:
cpp
#include <stdio.h>
struct Point
{
int x;
int y;
};
int main()
{
struct Point p = {3, 4};
struct Point *ptr = &p;
ptr->x = 10;
ptr->y = 20;
printf("x = %d y = %d\n", ptr->x, ptr->y);
return 0;
}
综合举例如下:
cpp
#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;
}
十、操作符的属性
10.1.优先性
参考链接:C 运算符优先级 - cppreference.com
圆括号( () )
• ⾃增运算符( ++ ),⾃减运算符( -- )
• 单⽬运算符( + 和 - )
• 乘法( * ),除法( / )
• 加法( + ),减法( - )
• 关系运算符( < 、 > 等)
• 赋值运算符( = )
由于圆括号的优先级最⾼,可以使⽤它改变其他运算符的优先级。
大概记住这些就够了,其它可以现查表。
10.2.结合性
如果两个运算符优先级相同,优先级没办法确定先计算哪个了,这时候就看结合性了,则根据运算符 是左结合,还是右结合,决定执⾏顺序。⼤部分运算符是左结合(从左到右执⾏),少数运算符是右结合(从右到左执⾏),⽐如赋值运算符( = )。
十一、整形提升
C语⾔中整型算术运算总是⾄少以缺省整型类型的精度来进⾏的。
而为了获得这种精度,表达式中的字符型和短整型在使用之前就会被转换为整形,这种转换叫整形提升。
表达式的整型运算要在CPU的相应运算器件内执⾏,CPU内整型运算器(ALU)的操作数的字节长度⼀般就是int的字节⻓度,同时也是CPU的通⽤寄存器的⻓度。
因此,即使两个char类型的相加,在CPU执⾏时实际上也要先转换为CPU内整型操作数的标准长度。
通⽤CPU(general-purpose CPU)是难以直接实现两个8⽐特字节直接相加运算(虽然机器指令中可能有这种字节相加指令)。所以,表达式中各种⻓度可能⼩于int⻓度的整型值,都必须先转换为int或unsigned int,然后才能送⼊CPU去执⾏运算。
cpp
char a,b,c;
...
a = b + c;
首先,b和c被提升为整形然后运算赋给a。
如何进行整形提升呢?
- 有符号整数提升是按照变量的数据类型的符号位来提升的
- ⽆符号整数提升,⾼位补0
cpp
//负数的整形提升
char c1 = -1;
变量c1的⼆进制位(补码)中只有8个⽐特位:
1111111
因为 char 为有符号的 char
所以整形提升的时候,⾼位补充符号位,即为1
提升之后的结果是:
11111111111111111111111111111111
cpp
//正数的整形提升
char c2 = 1;
变量c2的⼆进制位(补码)中只有8个⽐特位:
00000001
因为 char 为有符号的 char
所以整形提升的时候,⾼位补充符号位,即为0
提升之后的结果是:
00000000000000000000000000000001
cpp
//⽆符号整形提升,⾼位补0
结语
有很多朋友问为什么以武器大师来做标题,这里结尾给大家解释一下,因为我觉得它就像不同人手中不同的工具,比如算数操作符像是数学家手中的计算器,关系操作符像是侦探手中的证据对比工具,逻辑操作符类似于法官手中的判决书,位操作符更像电路工程师的开关和转换器,赋值操作符像是建筑师手中的蓝图和材料......
其实我们学习每样东西都是,虽然有时很抽象,但是我们总能找到解决办法。
"细想全是问题,去做全是答案"。