1.操作符的分类
2.二进制和进制转换
2,8,10,16进制都是数值的不同表示形式而已
16进制的数值之前写:0x
8进制的数值之前写:0
十进制满十进一,二进制满二进一
2进制转10进制
十进制的每一位都有权重,10进制的数字从右向左是个位、⼗位、百位....,分别每⼀位的权重是 10 , 10 , 10 ...
10进制转2进制
2进制转8进制和16进制
2进制转8进制
2进制转16进制
3.原码,反码,补码
整数的二进制表示方法有三种,即原码,反码和补码
4.移位操作符
移动的是二进制位,且操作数只能是整数
4.1 <<左移操作符
#include<stdio.h>
int main()
{
int a = 6;
int b = (a << 1);
printf("%d\n", b);
printf("%d\n", a);
return 0;
}
左移补0
左移一位有乘二的效果
4.2 >>右移操作符
右移规则取决于编译器的实现,常见编译器都是算数右移
移位不能移动负数位,这个是标准未定义的
右移一位有除以2的效果(大部分情况,-1就不行)
移位规则:分两种
逻辑右移:左边⽤0填充,右边丢弃
算数右移:左边⽤原该值的符号位填充,右边丢弃
5.位操作符:&,|,^,~
操作的是二进制的位,操作数必须是整数
按位与:
算出补码,有0为0,都是1为1
按位或:
算出补码,有1为1,都是0为0
打印的是原码
按位异或:
相同为0,相异为1
按位取反:
1变0,0变1
练习:不创建第三个变量,交换a和b
法一:更优
法二:
a^a=0,a^0=a
练习:编写代码实现,求一个整数存储在内存中的二进制中1的个数
法一:
法二:
练习:编写代码,将13二进制序列的第五位修改为1,然后再改回0
6.单目操作符
7.逗号表达式
从左向右依次执行,整个表达式的结果是最后一个表达式的结果
8.下标访问[ ],函数调用()
[ ]下标引用操作符
数组使用
函数调用操作符
接受⼀个或者多个操作数:第⼀个操作数是函数名,剩余的操作数就是传递给函数的参数。
9.结构成员访问操作符
9.1结构体
9.1.1结构的声明
最后花括号后的分号不能少
9.1.2结构体变量
创建变量时也可以初始化
//代码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结构体成员的直接访问
结构体成员的直接访问是通过操作符(.)访问的,点操作符接受两个操作数
#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结构体成员的间接访问
有时候我们得到的不是一个结构体变量,而是得到了一个指向结构体的指针
#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;
}
使用方式:结构体指针->成员名
综合举例:
#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.操作符的属性:优先级,结核性
这两个属性决定了表达式的求值计算顺序
10.1优先级
包含多个运算符时,哪个应该优先执行
如果表达式优先级相同,看结合性
10.2结合性
优先级相同时,要看结合性,有的从左向右,有的从又向左
11.表达式求值
11.1整型提升
C语言中的整型算数运算总是,至少以缺省整型类型的精度来进行的
为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升
#include<stdio.h>
int main()
{
char a = 3;
char b = 127;
char c = a + b;
printf("%d\n", c);
return 0;
}
a和b计算时,先要被转换为普通整型int,称为整型提升,转换后的计算精度会更高
//负数的整形提升
char c1 = -1;
变量c1的⼆进制位(补码)中只有8个⽐特位:
1111111
因为 char 为有符号的 char
所以整形提升的时候,⾼位补充符号位,即为1
提升之后的结果是:
11111111111111111111111111111111
//正数的整形提升
char c2 = 1;
变量c2的⼆进制位(补码)中只有8个⽐特位:
00000001
因为 char 为有符号的 char
所以整形提升的时候,⾼位补充符号位,即为0
提升之后的结果是:
00000000000000000000000000000001
//⽆符号整形提升,⾼位补0
char是signed char还是unsigned char,取决于编译器,常见编译器是signed char
11.2算数转换
如果某个操作符的操作数属于不同类型,那么除非其中一个操作数转换为另一个操作数的类型,否则操作就无法进行,下面的层次体系称为寻常算数转换
long double
double
float
unsigned long int
long int
unsigned int
int
字节不同就无法计算
11.3 问题表达式解析
表达式1
//表达式的求值部分由操作符的优先级决定。
//表达式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
//表达式2
c + --c;
操作符的优先级只能决定自减--的运算在+的运算前面,但是没有办法得知,+操作符的左操作数c的获取是在右操作数运算之前还是之后求值,所以结果是不可预测的
表达式3
//表达式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;
}
输出结果为-10
count每次加都
建议不要写复杂表达式 一