1 计算机中的数据
计算机实质是在完成数据的输入、存储、处理和输出的功能;
1.1 数据的进制
在计算机中的所有数据(文本数据、图像数据、音频数据、视频数据等)都是以二进制的形式存储,也就是说所有的数据都是以0和1构成的有效序列。
- 二进制编码数据(机器编码:计算机思维分析数据)
数据只有两种状态(0 = 低电平 和 1 = 高电平)构成,数据运算和存储的过程中满足逢2进1;
特点:
便于表述计算机中数据某位的状态(0 = 低电平,1 = 高电平);
由于数据由若干位构成,有效数据位比较多,不便于人的阅读。
2.十进制编码数据(人思维分析数据)
数据由0 - 9构成,数据运算和存储处理的过程中满足逢10进1
特点:
数据的阅读比较方便;
在使用十进制数据不便于计算机表示数据某位的状态;
数据进制之间的转换:
换位思考的过程(计算机思维和人思维的切换):更好的实现计算机思维和人思维的切换。
a.十进制数据转换为二进制数据
整数:循环对2取余数,同时对2取整数,在整数为0的时候结束循环,倒序序列即为二进制编码序列;
小数:循环乘以2取整,同时取小数,在小数为0的时候结束循环,正序序列即为二进制编码序列;
b.二进制数据转换为十进制数据
整数编码:位编码 * 2的n(位数)次方并累加;
由于在使用二进制和十进制的情况下,存在问题不能同时方便数据的阅读和数据某些状态的阅读,为了更好的实现数据的阅读(人思维数据的读写)和数据某位状态的获取(计算机思维读写),使用十六进制数据编码和八进制数据编码
3.十六进制编码数据
数据由0-9和a-f的符号构成,并以0x开头,在数据运算和存储处理的过程中满足逢16进1;
数据的每4个位的值(0 - 15)只需要使用1个符号表示,读取的时候是以4位1个单元,便于人思维的读写;同时将单元数据以(8421编码)将其转换为二进制编码形式,便于计算机思维读写某位状态;
4.八进制编码数据
数据由0-7构成,并以0开头,在数据运算和存储处理的过程中满足逢8进1;
1.2 计算机数据存储
所有的数据在计算机中,都是以二进制编码形式存储
- 正数的存储编码是数据源码
正数的源码:正数的二进制编码
实例:123的存储编码
123的二进制编码: 0111 1011 ==> 123的存储编码(123的源码):0111 1011
2.负数的存储编码是数据补码
负数的补码:正数部分的源码取反 + 1;
实例:-123的存储编码:
1) 123的源码: 0111 1011
2) 反码:将源码的所有位取反(包含符号位): 1000 0100
3) 补码:反码+1 1000 0101
-124的存储编码: 1000 0100
如果正数和负数相加结果为-1,存储编码所有位相反:1000 0100。
2 数据类型关键字
在C语言中数据由基本数据类型数据和构造数据类型数据,其中构造数据类型的数据是由基本数据类型数据构造而成。
具体包含12个:
整型数据类型关键字:char、short、int、long
符号说明关键字:unsigned、signed
浮点数据类型关键字:float、double
构造数据类型关键字:struct、union、enum
特殊数据类型关键字:void
2.1 基本数据类型数据
对于基本数据类型数据,需要关心数据所占内存空间大小,以及存储空间存储数据的值域范围
2.1.1 整型数据
char 字符(字节型)数据
存储空间大小:1个字节(8位);
值域范围:由于数据存储分为有符号数据和无符号数据存储。
unsigned char 无符号字节数据
所有的位都表示数据位,数据位一共有8位:
值域范围:0 ~ 255
signed char 有符号字节数据
最高位表示数据的符号位(1 = 负数,0 = 正数) + 低7位标识数据位;
正数: 最高位符号位 = 0; 值域范围: 0 ~ 127
负数: 最高位符号位 = 1; 值域范围:-1 ~ -128
值域范围: -128 ~ 127
PS:在数据存储的时候,不要超过值域范围,在超过值域范围的时候,空间会溢出,存储空间中的数据会出现异常。
实例:由unsigned char存储300,由于空间溢出,输出结果会出现异常(不等于300).
#include <stdio.h>
int main()
{
unsigned char c = 300; /* 定义了一个无符号char类型变量,并初始化值为300,空间会溢出 */
printf("c = %d\n", c); /* 输出的结果不等于300 */
return 0;
}
会出现重新开始计数的情况 300 - 256 = 44;
c=44
short 短整型数据 存储空间2字节(16位)
值域范围:
unsigned short 0 ~ 65535;
signed short -32768 ~ 32767
int 整型数据类型
存储空间大小: 2字节(16位系统) 4字节(32位系统或者64位系统)
long 长整型数据类型
存储空间大小: 4字节(16位系统或者32位系统) 8字节(64位系统)
2.1.2 浮点型数据
浮点型数据,其实质是数学中的小数存储,由于在计算机中存储空间位数有限,对于小数存储可能会存在误差,所以称为浮点型数据。
float 单精度浮点型数据 存储空间大小:4字节(32位)
注意:由于浮点型数据在存储的时候,底数部分存储的位数为23位,所以数据的精度范围在小数点后面的6位。
#include <stdio.h>
int main()
{
float a = 34.625; /* 定义float变量 */
int *p = (int *)&a; /* 定义int指针存储空间 */
printf("%x\n", *p); /*0x420a8000*/
}
double 双精度浮点型数据 存储空间是8字节(64位);
#include <stdio.h>
int main()
{
double d = 34.625;
long *q = (long *)&d; /* 使用long类型指针,是为了能够取64位空间数据 */
printf("%lx\n", *q); /* 输出0x4041 5000 0000 0000 */
}
2.2 构造数据类型
- struct 用于构造结构体数据类型,可以将多个基本数据类型或者构造数据类型的数据构造为一个新的数据类型数据。
- union 联合体(共用体)数据类型,用于多个数据共享同一内存空间。空间大小由最大成员决定。
- enum 枚举数据类型,其成员是由数据常量构成。
2.3 特殊的数据类型
void 数据类型;在使用的过程,void不能用来定义数据类型变量,可以用来修饰函数的返回值数据类型和修饰指针指向数据类型。
3 常量和变量
在计算机中的数据,主要包含数据常量和数据变量
3.1 数据常量
所谓的数据常量,指的是在程序运行过程,数值不能被修改的量,将其称为数据常量。在C语言中具体的常量:
- 字符常量
所谓的字符常量,指的是其值为字符的常量,使用单引号来包含的字符('值'),其值可以是字符也可以是0-9的数字。在内存中存储的是字符的Ascii码值。
例如:'a'、'A'、'0';具体字符存储编码可以参考Ascii编码表
2.整型常量
所谓的整型常量,指的是值为整型数据的常量,可以使用十进制、八进制或者十六进制表示:
实例:十进制数据(123),对应的十六进制(0x7b)和八进制(0173);
3.字符串常量
所谓的字符串常量,指的是由多个连续字符,使用双引号包含的常量,称为字符串常量。在字符串常量中使用\0作为字符串常量的结束。
实例:"hello"、"kunkun"
4.浮点型常量
所谓的浮点型常量,指的是其值为浮点型数据的常量
实例:3.1415926、 23.456
5.指数常量
所谓的指数常量,其实质是浮点型常量的一种特殊表示形式,数值本身依然是浮点型数据。但是它可以用于较大或者较小浮点型数据的表示
具体表示语法格式:(+/-)ME(+/-)N
第1个(+/-):表示指数常量数据的符号。正数用+,可以省略,负数用-。
第2个(+/-):表示指数常量的指数符号。
M:表示的是指数常量的底数,是浮点型数据;
E:特殊符号表示指数的意思,可以使用e表示。
N:表示的是指数常量的指数值;
浮点型常量:0.0000000011 可以使用指数常量表示:1.1E-9
#include <stdio.h>
int main()
{
printf("%c\n", 'a'); //"a"
printf("%d\n", 'a'); //"97"
printf("%d\n", 123); // "123"
printf("0x%x\n", 123); //"0x7b"
printf("0%o\n", 123); //"0173"
printf("%s\n", "hello"); // "hello"
printf("%s\n", "kunkun"); //"kunkun"
printf("%f\n", 3.1415926); //"3.141593"
printf("%f\n", 23.456); //"23.456000"
printf("%f\n", 0.0000000000000000000001); //"0.000000"
printf("%e\n", 0.0000000000000000000001); //"1.000000e-22"
}
6.标识符常量
所谓的标识符常量,使用特殊的标识符来存储常量的值。值可以是任意常量。
a.标识符常量的定义语法:
#define 标识符 常量值或者常量表达式
b.标识符号的定义语法规则
i.严格区分大小写,在定义的时候做到见名知意的效果。具体可以采用下划线式smart_home或者驼峰式SmartHome
ii.不能和关键字相同;
iii.不能以数字开头,可以以字符或下划线开头;
iiii.以字母、数字和下划线构成;
c. 常量值或者常量表达式
可以是任意类型的常量;或者多个常量构成的常量表达式;
d.标识符常量是在程序预处理阶段实现替换,在替换的过程中不会做语法检测。
e.在定义标识符常量的时候,可以设置参数,此时的标识符常量称为宏函数
i.只做代码的替换,不做函数的调用;
ii.在实现宏函数的时候,要避免因为代码的替换而产生的歧义。使用括号减少替换的歧义。
#include <stdio.h>
#define ONE 1
#define TOW ONE+ONE
#define MUL(a, b) ((a)*(b))
int main()
{
printf("%d\n", ONE); //1
printf("%d\n", TOW); //2
printf("%d\n", MUL(3,4)); //12
printf("%d\n", MUL(1+2, 3+1)); //12
}
f.对于标识符常量,可以做代码语句的替换
常用DEBUG调试语句的替换
#include <stdio.h>
#define TEST 0 /* 定义调试宏 TEST = 1调试版本;TEST=0发行版本 */
/* #if TEST是预处理指令:满足条件的是语句会编译执行,否则不做编译执行 */
#if TEST /* TEST=1,DEBUG替换printf语句,在调试版本中输出调试语句 */
/* __FILE__:C库中的标识符常量,表示调用该标识符常量的文件名称
__LINE__:C库中的标识符常量,表示调用该标识符常量文件的行
__FUNCTION__:C库中的标识符常量,表示调用该标识符常量文件中的函数名称,也可以使用__func__
__TIME__:C库中的标识符常量,表示程序的编译时间(例如:16:30:23)
*/
#define DEBUG printf("%s-%d-%s\n", __FILE__, __LINE__, __FUNCTION__) /* DEBUG替换printf语句 */
#else /* TEST=0,DEBUG替换空语句,在发现版本中不做输出 */
#define DEBUG /* DEBUG替换空语句 */
#endif
void test()
{
DEBUG;
}
int main()
{
DEBUG;
test();
}
练习:
题目: 1个水分子的质量约为3.0×10−23 克。1夸脱水大约是950克。编写一个程序,提示用户输入水的夸脱数,并显示水分子的数量、使用常量表达式实现。
#include <stdio.h>
#define M 3.0E-23
#define KT 950
#define FUNC(n) ((KT)/(M)*(n)) /* n夸脱水分子个数 */
int main()
{
printf("%e\n", KT/M);
printf("%e\n", FUNC(2.1));
printf("%e\n", FUNC(2));
}
3.2 数据变量
所谓的数据变量,指的是在程序执行过程中,数据值可以被修改的量称为数据变量。
数据变量的定义语法形式:
存储类型 数据类型 变量名称;
存储类型:修饰变量所占内存空间的存储属性;
数据类型:修饰变量所占存储空间的大小,以及数据的表示类型;数据类型可以是基本数据类型也可以是构造数据类型;
变量名称:表示存储空间的访问名称;
在C语言中变量的定义主要分为全局变量和局部变量;
所谓的全局变量,在模块外部进行定义,作用域为整个程序域,存储在内存空间的静态存储区中,其生命周期为程序执行到程序结束。
所谓的局部变量:在模块内部进行定义,作用域为模块域,在没有特殊说明情况下存储在栈区,其生命周期为语句执行到模块结束。
#include <stdio.h>
int a; /* 在所有函数模块外部来定义全局变量的a,a的作用域为整个程序域(在程序中都可以访问到a) */
int main()
{
int b; /* 定义了局部变量b,作用域为main函数模块域,在main模块内都可访问,在其它模块中不可以访问 */
{
int c; /* 定义了局部变量c,作用域为{}模块域,在其它模块中不可以访问 */
printf("%d:c = %d\n", __LINE__, c);
} /* c定义的模块结束,c的作用域结束 */
printf("%s:a = %d\n",__func__, a);
printf("%s:b = %d\n", __func__, b);
// printf("%d:c = %d\n", __LINE__, c); /*error:在访问c的时候,c的作用域已经结束,所以不能访问 */
} /* b定义的模块结束,b的作用域结束 */
void test()
{
printf("%s:a = %d\n",__func__, a);
// printf("%s:b = %d\n", __func__, b); /* error:在访问b的时候,b的作用域已经结束,不能在访问 */
}
4. 存储类型关键字
4.1 虚拟内存管理
在Linux系统中,程序的运行至少会产生一个进程,进程是内存管理的最小单位,在32位的Linux系统中,每一个进程都会有一个独立的虚拟4G内存空间。完成代码和数据的存储,具体分布
4.2 具体存储类型关键字
4.2.1 auto 自动存储类型
- auto修饰的变量称为自动存储类型变量;
- auto只能用来修饰局部变量,不能修饰全局变量;
- 在定义局部变量的时候,如果存储类型关键字省略,默认为auto类型变量;
- auto修饰的变量一定存储在栈区
#include <stdio.h>
int global_a;
//auto int global_b; /* error, auto自动存储类型只能修饰局部变量,不能修饰全局变量*/
int main()
{
int a; /* 在定义局部变量的时候,省略存储类型关键字,则默认为auto类型变量 */
auto int b; /* auto类型 修饰的局部变量 */
}
4.2.2 register寄存器变量
所谓的寄存器变量,指的是在定义变量的时候,使用register存储类型关键字修饰的变量,称为寄存器变量;
- 寄存器变量不存储在虚拟内存中,存储在寄存器中,不能取地址运算;
- register只能用来修饰局部变量,不能修饰全局变量;
- 由于寄存器变量存储在寄存器中,数据处理的效率较高;对于效率有要求的数据可以定义寄存器变量存储;
- 由于寄存器数量有限,不能将所有的局部变量都定义为寄存器变量,并且寄存器变量的定义有可能失败,定义失败的寄存器变量默认为auto类型变量;
#include <stdio.h>
int c;
//register int d; /* error,register只能修饰局部变量,不能修饰全局变量 */
int main()
{
int a; /* auto自动存储类型变量,存储在栈区 */
register int b; /* register修饰的寄存器变量,存储在寄存器中 */
printf("&a = %p\n", &a); /* %p表示输出地址值,&为取地址编号运算符,&a表示取变量a存储空间的地址 */
// printf("&b = %p\n", &b); /* error,寄存器变量存储在寄存器中,不能取地址运算 */
}
4.2.3 extern外部声明引用
extern用来修饰全局变量和函数声明,作为外部声明引用。意义在于对其它文件中定义的全局变量和函数在当前文件中做声明;在完成外部声明和引用之后可以直接在当前文件中访问全局变量的值和函数进行调用。
实例:test.c
#include <stdio.h>
int a = 34;
void showVal(void)
{
printf("a = %d\n", a);
}
实例:main.c
#include <stdio.h>
extern int a; /* 在对test.c文件中的全局变量a的外部声明引用 */
extern void showVal(void); /* 在对test.c文件中定义函数做外部声明引用 */
int main()
{
showVal(); /* 在外部声明引用之后,可以直接访问在其它文件中定义的函数 */
printf("a : %d\n", a); /* 可以直接访问在其它文件中定义的全局变量 */
a = 123;
showVal();
}
在程序编译的时候,由于是多个文件数据:gcc test.c main.c ==> 生成可执行文件a.out
4.2.4 static修饰静态存储变量
- static修饰全局变量和函数
改变了全局变量或者函数的作用域(或者链接属性);
对于未被static修饰的全局变量和函数作用域为程序域;被static修饰的全局变量和函数作用域为文件域(只能在定义的文件中访问,不能在其它文件中对其进行外部声明引用访问)。
实例test.c
#include <stdio.h>
int a = 34;
static int b = 32; /* 使用static修饰的全局变量只能在当前文件中访问 */
void showVal(void)
{
printf("a = %d\n", a);
printf("b = %d\n", b);
}
/* 使用static修饰的函数只能在当前文件中访问 */
static void test(void)
{
}
实例main.c
#include <stdio.h>
extern int a; /* 使用extern对其它文件中定义的全局变量做外部声明引用 */
extern void showVal(void); /* 使用extern对其它文件中定义的函数做外部声明引用 */
extern int b;
extern void test(void);
int main()
{
showVal();
printf("a : %d\n", a); /* 直接访问全局变量 */
printf("b = %d\n", b); /* error,在test.c文件中定义的变量b使用static修饰。 */
a = 123;
showVal(); /* 直接访问函数 */
test(); /* error,在test.c文件中定义的函数test使用static修饰 */
}
2.static修饰局部变量
改变变量的存储位置和生命周期
未被static修饰的局部变量存储在栈区,生命周期是语句执行到模块结束;被static修饰的局部变量存储在静态存储区,生命周期是执行到程序结束。static修饰的变量在多次访问的时候,下一次访问的数据是上一次的结果。
4.3 存储类型关键字
存储类型关键一共4个:auto、register、extern、static
5 数据类型转换
在计算机数据的存储和运算过程中,经常会遇到不同数据类型之间的运算,在运算的过程中,实现数据类型之间的强制转换。
所谓的数据类型之间的转换,其实质是对于内存空间的数据编码形式的表示类型转换。
- 自动转换
所谓的自动转换,是按照数据类型的隐式转换规则来实现数据类型的转换过程。
默认转换规则:
1) 占用内存空间大小转换:将小数据类型向大数据类型转换。(char->short->int->long)
2) 按照数据精度转换:低精度数据向高精度数据转换 (float -> double)
3) 有符号数据向无符号数据转换 (signed ->unsigned)
2.显示转换
所谓显示转换,指设计者根据数据的需求,按照指定转换规则实现数据类型的转换,打破原有的默认转换规则。
具体的语法格式:
(类型说明符)(表达式) /* 将表达式数据按照类型说明符的数据类型进行转换 */
#include <stdio.h>
int main()
{
int a = 4;
float f = 3.5;
int b;
float c;
printf("b = %d, c = %f\n", a+f, a+f); /* 隐式规则转换 */
printf("b = %d, c = %f\n", a+(int)f, (float)a+f); /* 显示规则转换 */
return 0;
}
3.数据类型转换注意事项:
a.在数据强制转换过程中,数据在内存中编码不会被修改,只会改变数据的表示形式;
b.在隐式转换过程中,由于转换规则不明确,可能会导致结果异常,谨慎使用。在转换规则不明确的情况下可以采用显示规则做强制转换。
6 运算符
在C语言中有很多运算符,其中包含算术运算符、逻辑运算符和关系运算符、位运算符、赋值运算符和复合赋值运算符、条件运算符、逗号运算符、sizeof运算符等。
6.1 算术运算符
+、-、*、/、%、++、--
/ :
1) 操作数都为整数的时候,表示的是取整运算符,
2) 操作有一个是浮点数据的时候,表示的除法运算符,实质是将两个浮点型数据相除;
% :
操作数都为整数才有意义,表示的是取余数运算符;
++ :
前++:++a,先将变量的值自加1(a = a + 1),在取变量a的值
后++:a++,先取变量a的值,在将变量a的值自加1(a = a + 1)
-- :自减1运算符,和++运算符一致。
#include <stdio.h>
int main()
{
int a = 32;
int b = 6;
float c = 31.1;
float f = 5.1;
int d;
printf("a/b = %d\n", a/b); /* 由于两个操作数都是整数,/表示取整数运算符 */
printf("a/f = %f\n", a/f);
/* 有操作数是浮点数据,/表示除法运算符 */
printf("c/f = %f\n", c/f);
printf("%d\n", a%b); /* 操作数都是整数才有意义,表示取余数运算符 */
// printf("a%f = %f\n", a%f); /* 操作数有浮点数没有意义 */
// printf("c%f = %f\n", c%f); /* 操作数有浮点数没有意义 */
a = 1;
d = a++; /* 后++, 先取值,在自加1;d = a; a = a+1 */
printf("a = %d, d = %d\n", a, d); /* a = 2, d = 1 */
a = 1;
d = ++a; /* 前++,先自加1,在取值;a = a+1, d = a */
printf("a = %d, d = %d\n", a, d); /* a = 2, d = 2 */
return 0;
}
6.2 关系运算符和逻辑运算符
不管是关系运算符,还是逻辑运算符,其运行结果只有两种状态:分别为真(true)和假(false);
- bool数据类型的使用
bool是一种数据类型,值域只有true和false;在使用的过程中可以用来定义变量;需要包含有文件#include <stdbool.h>
#include <stdio.h>
#include <stdbool.h> /* bool类型的头文件,其中有true和false */
int main()
{
bool test = true;
if (test == true) {
printf("true\n");
}
else
{
printf("false\n");
}
return 0;
}
2.关系运算符
>、=、>=、<=、==、!=
3.逻辑运算符:
!、&&、||
! :逻辑非 其结果:输入为真,输出为假;输入为假,输出为真;
&& :逻辑与
表达式1 && 表达式2 其结果:当两个表达式都为真结果为真,否则(只要有一个表达式结果为假)结果为假;
如果表达式1为假的时候,结果为假;表达式2不在继续执行。
|| :逻辑或
表达式1 || 表达式2 其结果:当两个表达式都为假结果为假,否则(只要有一个表达式结果为真)结果为真;
如果表达式1为真的时候,结果为真;表达式2不在继续执行。
#include <stdio.h>
int main()
{
int a = 3;
int b = 4;
if (!a++ && b++) { /* !a++ 为假,整个结果为假,表达式b++不执行*/
printf("-------------\n");
}
printf("a = %d, b = %d\n", a, b); /* a = 4, b = 4 */
if (a++ || b++) { /* a++ 为真,整个结构为真,表达式b++不执行 */
printf("-------------\n");
}
printf("a = %d, b = %d\n", a, b); /* a = 5, b = 4 */
return 0;
}
6.3 位运算符
在实际数据运算的过程中,需要读写数据的某位或者某些位的状态,此时可以通过位运算符实现;
~ :按位取反运算符,将数据的所有位都取反,包括符号位都要取反(0 -> 1; 1 -> 0);
实例:0x12 0001 0010 ==> ~0x12 1110 1101 0xed
& :按位相与运算符,将两个数据编码对应的位相与,其位相与之后的结果:都为1结果为1,否则为0;用于将数据的某位清零(给数据位按位相与上0)。
实例:0x12 & 0x34 0001 0010 & 0011 0100 ==> 0001 0000 0x10;
| :按位相或运算符,将两个数据编码对应的位相或,其位相或之后的结果:都为0结果为0,否则为1;用于将数据的某位置1(给数据位按位相或上1)。
实例:0x12 | 0x34 0001 0010 & 0011 0100 ==> 0011 0110 0x36
^ :按位异或运算符,将两个数据编码对应的位异或,其位异或之后的结果:不同结果为1,相同结果为0,
实例:0x12 | 0x34 0001 0010 & 0011 0100 ==> 0010 0110 0x26
#include <stdio.h>
int main()
{
printf("0x%x\n", ~0x12); //0xed
printf("0x%x\n", 0x12 & 0x34);
//0x10
printf("0x%x\n", 0x12 | 0x34);
//0x36
printf("0x%x\n", 0x12 ^ 0x34);
//0x26
return 0;
}
移位运算符:左移位运算符(<<)和右移位运算符(>>)
<< : 左移位运算符
a<<n : 将数据a的二进制存储编码中的每一位依次向左移动n位(左边的n位被移除),右边的n位使用0补充。
0x34 << 2 0x34 (0011 0100) = 在左移2位之后的编码=>(1101 0000) 0xd0
>> :右移位运算
a >> n :将数据a的二进制存储编码中的每一位依次向右移动n位(右边的n位移除),左边的n位用0补充。
逻辑右移运算:
左边的位始终使用0补充;
0x34 >> 2 0x34(0011 0100) == 在右移2位之后的编码==> (0000 1101) 0x0d
算术右移运算
左边的位使用符号位补充:正数使用0补充,负数使用1补充
0x34 >> 2 0x34(0011 0100) == 在右移2位之后的编码==> (0000 1101) 0x0d
在右移位的时候,具体使用的逻辑右移还是算术右移,由系统决定,程序设计者不能决定。
练习:
1. 将变量a的3,7,21,25位清零;4,8,15, 31位置1;
数据位清零:a & 其它位为1,第3位为0; 0xffff fff7 a & (0xffff fff7 <=> ~0x8 <=> ~(0x1<<3)
将变量的第3位清零: a = a & (~(0x1 << 3));
将变量的第7位清零:a = a & (~(0x1 << 7)) ;
将变量的第21位清零:a = a & (~(0x1 << 21));
将变量的第25位清零:a = a & (~(0x1 << 25));
等价于:a = a & (~(0x1 << 3)) & (~(0x1 << 7)) & (~(0x1 << 21)) & (~(0x1 << 25));
数据位置1:
第4位置1;
a | 其它位为0,第4位为1, a | 0x10 a | (0x10 <=> (0x1 << 4));
将数据a的第4位置1: a = a | (0x1 << 4);
将数据a的第8位置1: a = a | (0x1 << 8);
将数据a的第15位置1: a = a | (0x1 << 15);
将数据a的第31位置1: a = a | (0x1 << 31);
等价:a = a | (0x1 << 4) | (0x1 << 8) | (0x1 << 15) | (0x1 << 31);
2. 将变量a的20-27位设置0x55;
如果需要给数据的连续多个位置位;
1 将数据需要设置位的连续多个位清零;
a = a & (~(0xff << 20));
2 将数据需要设置位的连续多个位设置指定的值;
a = a | (0x55 << 20);
等价: a = a & (~(0xff << 20)) | (0x55 << 20);
6.4 赋值运算符和复合赋值运算符
- 赋值运算符:=
- 复合赋值运算符
a.算术复合赋值运算符
+=、-=、/=、%= a += b; a = a+b;
b.位运算复合赋值运算符
&=、|=、^=、>=
#include <stdio.h>
int main()
{
int a = 0x34;
int b = 0x12;
// a += b;
a = a+b;
printf("a = 0x%x\n", a);
a &=b; /* a = a & b 0100 0110 & 0001 0010*/
printf("a = 0x%x\n", a);
b >>= 2;
printf("b = 0x%x\n", b);
return 0;
}
6.5 条件运算符(?:)
语法格式:
条件表达式 ? 表达式1 : 表达式2
1) 执行条件表达式进行条件判断,得到结果;
2) 表达式1和表达式2只会执行其中的一个,另外一个不会执行;当条件表达式结果为true执行表达式1,当条件表达式结果为false执行表达式2;
条件表达式可以使用if …… else 语句替换
#include <stdio.h>
int main()
{
int a = 4;
int b = 6;
a > b ? b++ : a++; /* 条件表达式 a > b 不成立,执行表达式2 a++ 完成a自加1 */
printf("a = %d, b = %d\n", a, b); /* a = 5, b = 6 */
a < b ? b++ : a++; /* 条件表达式 a < b 成立,执行表达式1 b++ 完成b自加1 */
printf("a = %d, b = %d\n", a, b); /* a = 5, b = 7 */
return 0;
}
6.6 逗号运算符
所谓的逗号运算符,指的是使用逗号将多个表达式分隔,每个表达式会顺序执行,但是最终的结果会由最后一个表达式决定。
语法格式:
(表达式1, 表达式2, ……);
注意:在逗号运算符使用的时候,需要整个逗号运算符分隔的多个表达式使用圆括号包含
#include <stdio.h>
int main()
{
int a = 3;
int b = 5;
int c = 7;
int d;
d = (a++, b++, c++);
printf("a = %d, b = %d, c = %d, d = %d\n", a, b, c, d);
/* a = 4, b = 6, c = 8, d = 7 */
return 0;
}
6.7 sizeof运算符
sizeof运算符,是用于计算数据(数据可以是常量、数据类型、变量、还可以是指针)所占存储空间大小。
语法格式:
sizeof(数据);
#include <stdio.h>
int main()
{
int a;
printf("sizeof(34) : %ld\n", sizeof(34)); /* 计算常量所占空间大小:4 */
printf("sizeof(char) : %ld\n", sizeof(char));
/* 计算char数据类型所占空间大小:1 */
printf("sizeof(a) : %ld\n", sizeof(a));
/* 计算int类型变量所占空间大小:4 */
printf("sizeof(&a) : %ld\n", sizeof(&a));
/* 计算指针所占空间大小:32位系统(4),64位系统(8) */
return 0;
}
6.8 运算符优先级
在做表达式的运算过程中,按照运算符的优先级进行运算:在相同优先级从左向右依次运算;在不同优先级的运算会先运行高优先级,在运行低优先级。
优先级总结:
优先级 | 名称或含义 | 使用形式 | 结合方向 | 说明 | |
1 | [] | 数组下标 | 数组名[ 常量表达式] | 左到右 | |
() | 圆括号 | (表达式)/ 函数名( 形参表) | |||
. | 成员选择(对象) | 对象. 成员名 | |||
-> | 成员选择(指针) | 对象指针-> 成员名 | |||
2 | - | 负号运算符 | - 表达式 | 右到左 | 单目运算符 |
( 类型) | 强制类型转换 | ( 数据类型) 表达式 | |||
++ | 自增运算符 | ++ 变量名/ 变量名++ | 单目运算符 | ||
-- | 自减运算符 | -- 变量名/ 变量名-- | 单目运算符 | ||
* | 取值运算符 | * 指针变量 | 单目运算符 | ||
& | 取地址运算符 | & 变量名 | 单目运算符 | ||
! | 逻辑非运算符 | ! 表达式 | 单目运算符 | ||
~ | 按位取反运算符 | ~ 表达式 | 单目运算符 | ||
sizeof | 长度运算符 | sizeof( 表达式) | |||
3 | / | 除 | 表达式/ 表达式 | 左到右 | 双目运算符 |
* | 乘 | 表达式* 表达式 | 双目运算符 | ||
% | 余数(取模) | 整型表达式/ 整型表达式 | 双目运算符 | ||
4 | + | 加 | 表达式+ 表达式 | 左到右 | 双目运算符 |
- | 减 | 表达式- 表达式 | 双目运算符 | ||
5 | 左移 | 变量 | 左到右 | 双目运算符 | |
>> | 右移 | 变量>> 表达式 | 双目运算符 | ||
6 | > | 大于 | 表达式> 表达式 | 左到右 | 双目运算符 |
>= | 大于等于 | 表达式>= 表达式 | 双目运算符 | ||
< | 小于 | 表达式< 表达式 | 双目运算符 | ||
小于等于 | 表达式 | 双目运算符 | |||
7 | == | 等于 | 表达式== 表达式 | 左到右 | 双目运算符 |
!= | 不等于 | 表达式!= 表达式 | 双目运算符 | ||
8 | & | 按位与 | 表达式& 表达式 | 左到右 | 双目运算符 |
9 | ^ | 按位异或 | 表达式^ 表达式 | 左到右 | 双目运算符 |
10 | | | 按位或 | 表达式| 表达式 | 左到右 | 双目运算符 |
11 | && | 逻辑与 | 表达式&& 表达式 | 左到右 | 双目运算符 |
12 | || | 逻辑或 | 表达式|| 表达式 | 左到右 | 双目运算符 |
13 | ?: | 条件运算符 | 表达式1? 表达式2: 表达式3 | 右到左 | 三目运算符 |
14 | = | 赋值运算符 | 变量= 表达式 | 右到左 | |
/= | 除后赋值 | 变量/= 表达式 | |||
*= | 乘后赋值 | 变量*= 表达式 | |||
%= | 取模后赋值 | 变量%= 表达式 | |||
+= | 加后赋值 | 变量+= 表达式 | |||
-= | 减后赋值 | 变量-= 表达式 | |||
左移后赋值 | 变量 | ||||
>>= | 右移后赋值 | 变量>>= 表达式 | |||
&= | 按位与后赋值 | 变量&= 表达式 | |||
^= | 按位异或后赋值 | 变量^= 表达式 | |||
|= | 按位或后赋值 | 变量|= 表达式 | |||
15 | , | 逗号运算符 | 表达式, 表达式,… | 左到右 | 从左向右顺序运算 |
优先级 | 名称或含义 | 使用形式 | 结合方向 | 说明 | |
1 | [] | 数组下标 | 数组名[ 常量表达式] | 左到右 | |
() | 圆括号 | (表达式)/ 函数名( 形参表) | |||
. | 成员选择(对象) | 对象. 成员名 | |||
-> | 成员选择(指针) | 对象指针-> 成员名 | |||
2 | - | 负号运算符 | - 表达式 | 右到左 | 单目运算符 |
( 类型) | 强制类型转换 | ( 数据类型) 表达式 | |||
++ | 自增运算符 | ++ 变量名/ 变量名++ | 单目运算符 | ||
-- | 自减运算符 | -- 变量名/ 变量名-- | 单目运算符 | ||
* | 取值运算符 | * 指针变量 | 单目运算符 | ||
& | 取地址运算符 | & 变量名 | 单目运算符 | ||
! | 逻辑非运算符 | ! 表达式 | 单目运算符 | ||
~ | 按位取反运算符 | ~ 表达式 | 单目运算符 | ||
sizeof | 长度运算符 | sizeof( 表达式) | |||
3 | / | 除 | 表达式/ 表达式 | 左到右 | 双目运算符 |
* | 乘 | 表达式* 表达式 | 双目运算符 | ||
% | 余数(取模) | 整型表达式/ 整型表达式 | 双目运算符 | ||
4 | + | 加 | 表达式+ 表达式 | 左到右 | 双目运算符 |
- | 减 | 表达式- 表达式 | 双目运算符 | ||
5 | 左移 | 变量 | 左到右 | 双目运算符 | |
>> | 右移 | 变量>> 表达式 | 双目运算符 | ||
6 | > | 大于 | 表达式> 表达式 | 左到右 | 双目运算符 |
>= | 大于等于 | 表达式>= 表达式 | 双目运算符 | ||
< | 小于 | 表达式< 表达式 | 双目运算符 | ||
小于等于 | 表达式 | 双目运算符 | |||
7 | == | 等于 | 表达式== 表达式 | 左到右 | 双目运算符 |
!= | 不等于 | 表达式!= 表达式 | 双目运算符 | ||
8 | & | 按位与 | 表达式& 表达式 | 左到右 | 双目运算符 |
9 | ^ | 按位异或 | 表达式^ 表达式 | 左到右 | 双目运算符 |
10 | | | 按位或 | 表达式| 表达式 | 左到右 | 双目运算符 |
11 | && | 逻辑与 | 表达式&& 表达式 | 左到右 | 双目运算符 |
12 | || | 逻辑或 | 表达式|| 表达式 | 左到右 | 双目运算符 |
13 | ?: | 条件运算符 | 表达式1? 表达式2: 表达式3 | 右到左 | 三目运算符 |
14 | = | 赋值运算符 | 变量= 表达式 | 右到左 | |
/= | 除后赋值 | 变量/= 表达式 | |||
*= | 乘后赋值 | 变量*= 表达式 | |||
%= | 取模后赋值 | 变量%= 表达式 | |||
+= | 加后赋值 | 变量+= 表达式 | |||
-= | 减后赋值 | 变量-= 表达式 | |||
左移后赋值 | 变量 | ||||
>>= | 右移后赋值 | 变量>>= 表达式 | |||
&= | 按位与后赋值 | 变量&= 表达式 | |||
^= | 按位异或后赋值 | 变量^= 表达式 | |||
|= | 按位或后赋值 | 变量|= 表达式 | |||
15 | , | 逗号运算符 | 表达式, 表达式,… | 左到右 | 从左向右顺序运算 |
初等单目一二级, // 初等运算符和单目运算符分别是第1、2优先级
乘除求余加减移, // 这句里面的运算符全归为算术运算符,移表示移位
关系等于不等于, // 关系运算符(< <= > >=)
按位与来异或或, // 位运算符优先级顺序: & -> ^ -> |
逻辑与或条件弱, // 逻辑运算符优先级顺序: && -> ||,后面跟着优先级比较低(弱)的条件运算符
赋值逗号一点破。 // 赋值,逗号最低