学习目标
1.【掌握】include预处理指令web
2.【掌握】多文件开发数组
3.【了解】认识进制编辑器
4.【掌握】进制之间的互相转换ide
5.【掌握】原码,反码,补码函数
6.【掌握】位运算学习
7.【掌握】int类型的修饰符spa
1、include预处理指令
其实咱们早就有接触文件包含这个指令了, 就是#include,它能够将一个文件的所有内容拷贝另外一个文件中。操作系统
使用语法:3d
第一种:#include <文件名>code
直接到C语言库函数头文件所在的目录中寻找文件
第二种:#include "文件名"
系统会先在源程序当前目录下寻找,若找不到,再到操做系统的path路径中查找,最后才到C语言库函数头文件所在目录中查找
使用注意:
#include指令容许嵌套包含,好比a.h包含b.h,b.h包含c.h,可是不容许递归包含,好比 a.h 包含 b.h,b.h 包含 a.h。下面是错误的用法:
使用#include指令可能致使屡次包含同一个头文件,下降编译效率,好比下面的状况:
在one.h中声明了一个one函数;在two.h中包含了one.h,顺便声明了一个two函数。(这里就不写函数的实现了,也就是函数的定义)
假如我想在main.c中使用one和two两个函数,并且有时候咱们并不必定知道two.h中包含了one.h,因此可能会这样作:
编译预处理以后main.c的代码是这样的:
1
2
3
4
5
6
|
void
one
(
)
;
void
one
(
)
;
void
two
(
)
;
int
main
(
)
{
return
0
;
}
|
第1行是由#include "one.h"致使的,第二、3行是由#include "two.h"致使的(由于two.h里面包含了one.h)。能够看出来,one函数被声明了2遍,根本就没有必要,这样会下降编译效率。
为了解决这种重复包含同一个头文件的问题,通常咱们会这样写头文件内容:
大体解释一下意思,就拿one.h为例:当咱们第一次#include "one.h"时,由于没有定义_ONE_H_,因此第9行的条件成立,接着在第10行定义了_ONE_H_这个宏,而后在13行声明one函数,最后在15行结束条件编译。当第二次#include "one.h",由于以前已经定义过_ONE_H_这个宏,因此第9行的条件不成立,直接跳到第15行的#endif,结束条件编译。就是这么简单的3句代码,防止了one.h的内容被重复包含。
这样子的话,main.c中的:
1
2
|
#include "one.h"
#include "two.h"
|
就变成了:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
// #include "one.h"
#ifndef _ONE_H_
#define _ONE_H_
void
one
(
)
;
#endif
// #include "two.h"
#ifndef _TWO_H_
#define _TWO_H_
// #include "one.h"
#ifndef _ONE_H_
#define _ONE_H_
void
one
(
)
;
#endif
void
two
(
)
;
#endif
|
第2~第7行是#include "one.h"致使的,第10~第23行是#include "two.h"致使的。编译预处理以后就变为了:
1
2
|
void
one
(
)
;
void
two
(
)
;
|
这才是咱们想要的结果
2、多文件开发
你们都知道咱们的C程序是由1个1个的函数组成的,当咱们的程序很大的时候,将这些函数代码写在写在同1个文件之中是绝对不科学。函数太多,不方便管理,而且不利于团队开发。
咱们的程序实际上都是分为1个1个的模块的,不管多大的程序都是由1个1个的小功能组成的。模块就是功能相同或者类似的一些函数。在实际开发中,不一样的人负责开发不一样的功能模块,要使用模块中的功能的话,直接调用就能够了。
如何写模块?
写模块的人,通常状况下要写两个文件。.c文件 .h文件. header 头文件。
.h文件之中,写上函数的声明。
.c文件之中,写上函数的实现。
想要调用模块之中的函数,只须要包含这个模块的头文件就能够了。好比咱们使用printf函数须要包含stdio.h头文件同样,只要包含了函数的声明,咱们就能直接使用函数了。
例如:
咱们还能给函数分组,例如:
右键,选择New Group能够建立组,进行源文件分组管理。放在组里的源文件其实他的路径是不会改变的:
3、认识进制
什么是进制?
进制是记数的一种方式,侧重点在于记数的时候,是逢多少进一。好比咱们平常生活中用的十进制,逢10进1。C语言中也有进制,C语言能识别的进制有二进制,十进制,八进制,十六进制。多少多少进制就是逢多少进1。
二进制:
逢二进一,每1位用0和1表示。
在C语言的代码中,若是要写1个二进制的数,那么就必需要在这个二进制的数的前面加1个0b的前缀。
C语言没有提供1个个是控制符来将1个整形变量中的数据以二进制的形式输出。
八进制:
逢八进一,每1位 0、一、二、三、四、五、六、7中的任意1位来表示。
在C语言之中,若是要写1个八进制的数,那么就必需要在这个八进制的数的前面加1个前缀0。
%o 会将整形变量中的数据以八进制的形式输出。
十进制:
逢十进一,每1位 0 1 2 3 4 5 6 7 8 9 中的任意一位,逢十进一。
在C语言之中直接写1个整数,默认就是十进制。
%d 是将整形变量中的数据以十进制的形式输出。
十六进制:
逢十六进以,每1位 0 1 2 3 4 5 6 7 8 9 a b c d e f 中的任意1位来表示。
若是咱们要在C语言中写1个十六进制的数 那么就必需要在这个数的前面加1个前缀0x。
使用%x 将整形变量中的数据以十六进制的形式输出。
例如:
1
2
3
4
5
6
7
8
9
10
|
#include <stdio.h>
int
main
(
int
argc
,
const
char
*
argv
[
]
)
{
int
num
=
0x13adf0
;
//加0x表示十六进制
printf
(
"num = %x\n"
,
num
)
;
//%x以十六进制形式打印
return
0
;
}
|
4、进制直接的互相转换
咱们先来引入一个概念,固然,C语言中没有规定这些,是便于学习者进行按位运算而本身定义的概念。
数码:一个数的每一位数字,就叫作数码。
数位:数码在这个数中的位置,从右到左,从0开始增加。
基数:每一位数码最多能够由多少个数字来表示,多少进制就是多少基数。
位权 = 数码 * (基数的数位次方)
进制之间的转换:
十进制转二进制:除2取余,直到商为0,再余数倒序
十进制转八进制:除8取余,直到商为0,再余数倒序
十进制转十六进制:除16取余,直到商为0,再余数倒序
二进制转十进制:每一位的位权相加
八进制转十进制:每一位的位权相加
十六进制转十进制:每一位的位权相加
二进制转换八进制:3合1,低位到高位,每3位分红一组,高位不够补0,求出每一组的10进制,再相连
八进制转二进制:3拆1,将八进制的每1个数码,拆成1个三位的二进制,再将这些二进制连起来
二进制转十六进制:4合1,低位到高位,每四位分红1组,高位不够补0,求出每1组的10进制,再相连
十六进制转二进制:1拆4,将十六进制的每1个数码,拆成1个四位的二进制1再将这些二进制连起来
八进制转十六进制:八进制 -> 二进制 ->十六进制
打印二进制的函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
#include <stdio.h>
//传入一个整数,打印他的二进制
void
printBinary
(
int
num
)
{
//定义一个临时变量temp,储存位移后的数据
int
temp
=
0
;
//定义一个临时变量temp1,储存按位与后的二进制最低位数值
int
temp1
=
0
;
for
(
int
i
=
0
;
i
<
32
;
i
++
)
{
//先位移,截取数据
temp
=
num
>>
(
31
-
i
)
;
//再与1按位与,由于任何数与1与都能获得那个任何数的二进制的最低位
temp1
=
temp
&
1
;
//取出一位打印一位
printf
(
"%d"
,
temp1
)
;
}
printf
(
"\n"
)
;
}
int
main
(
int
argc
,
const
char
*
argv
[
]
)
{
//调用函数打印出整数的二进制
printBinary
(
100
)
;
return
0
;
}
|
本身随意写的,网上还有不少功能更多的进制转换函数,须要的本身去谷歌吧。
5、原码,反码,补码
声明1个变量,其实就是在内存之中申请指定字节数的空间,用来存储数据。不管任何数据在内存之中都是以其二进制的形式存储的,而且是以这个数据的二进制的补码的形式存储的。那什么是补码呢?原码、反码、补码 都是二进制,只不过是二进制的不一样的表现形式。
强调:全部的数据都是以其二进制的补码的形式存储在内存之中的。
原码:
最高位用来表示符号位,0表明正,1表明负。其余叫数值位,数值位是这个数的绝对值的二进制位。
1
2
3
|
9的原码:
00000000
00000000
00000000
00001001
-
3的原码:
10000000
00000000
00000000
00000011
|
反码:
正数的反码就是其原码。负数的反码,是在其原码的基础之上,符号位不变,数值位取反。
1
2
3
4
5
6
7
|
9的原码:
00000000
00000000
00000000
00001001
9的反码:
00000000
00000000
00000000
00001001
-
3的原码:
10000000
00000000
00000000
00000011
-
3的反码:
11111111
11111111
11111111
11111100
|
补码:
正数的补码就是,其原码。负数的补码,是在其反码的基础之上加1。
1
2
3
4
5
6
7
8
9
10
11
|
9的原码:
00000000
00000000
00000000
00001001
9的反码:
00000000
00000000
00000000
00001001
9的补码:
00000000
00000000
00000000
00001001
-
3的原码:
10000000
00000000
00000000
00000011
-
3的反码:
11111111
11111111
11111111
11111100
-
3的补码:
11111111
11111111
11111111
11111101
|
为何要用补码来储存数据,由于计算机之中只有加法,没有减法。为了更低成本的计算出结果,因此使用补码来存储数据。以下例子:3 + (-2) = 1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
原码计算
:
00000000
00000000
00000000
00000011
10000000
00000000
00000000
00000010相减
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
10000000
00000000
00000000
00000101
这个结果不对
.已经变成负数了
.
反码计算
:
00000000
00000000
00000000
00000011
11111111
11111111
11111111
11111101相减
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
-
00000000
00000000
00000000
00000000
0这也是错的
.
补码计算
:
00000000
00000000
00000000
00000011
11111111
11111111
11111111
11111110相减
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
-
00000000
00000000
00000000
00000001
1
1结果是对
.
|
6、位运算
什么叫作位运算?
1个二进制数的每1位来参与运算,参与位运算的前提,是这个数必须是二进制数。而且参与运算的二进制数据必须是补码的形式,而且算出来的结果也是补码。
按位与 &
指的是两个数的二进制的补码 按位进行与运算. 若是都为1 结果就为1 不然就为0.
1
2
3
4
5
6
|
3
&
2
;
00000000
00000000
00000000
00000011
00000000
00000000
00000000
00000010
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
00000000
00000000
00000000
00000010
|
注意:任何数按位与1,结果是这个数的最低位
1
2
3
4
5
|
3
&
1
;
00000000
00000000
01001000
10010001
00000000
00000000
00000000
00000001
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
00000000
00000000
00000000
00000001
|
偶数的最低位必定是0,奇数的最低位必定是1。用1个数去按位与1,若是结果为0那么这个数必定是1个偶数,若是结果为1,那么这个数必定是1个奇数。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
#include <stdio.h>
int
main
(
)
{
int
num
=
100
;
//任意数按位与1,都能获得他的二进制位的最低位,若是最低位是1,则是奇数,是0则是偶数。
if
(
num
&
1
==
0
)
{
printf
(
"偶数\n"
)
;
}
else
{
printf
(
"奇数\n"
)
;
}
return
0
;
}
|
按位或 |
参与按位或的二进制补码,只要有1位为1,那么结果就为1,只有都为0的时候才为0。
1
2
3
4
5
6
|
3
|
2
;
00000000
00000000
00000000
00000011
00000000
00000000
00000000
00000010
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
-
00000000
00000000
00000000
00000011
|
按位取反 ~
这是1个单目运算符,只须要1个数据参与,将1变0,0变1
1
2
3
4
5
6
|
~
3
;
00000000
00000000
00000000
00000011
11111111
11111111
11111111
11111100补码
11111111
11111111
11111111
11111011反码
10000000
00000000
00000000
00000100
-
4
|
按位异或 ^
参与按位异或的二进制补码,每一位,相同为0,不一样为1。
1
2
3
4
5
6
|
3
^
5
;
00000000
000000000
00000000
00000011
00000000
000000000
00000000
00000101
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
00000000
000000000
00000000
00000110
+
6
|
实现交换两个变量的值:
1
2
3
4
5
6
7
8
9
10
11
|
#include <stdio.h>
int
main
(
)
{
int
num1
=
10
,
num2
=
20
;
num1
=
num1
^
num2
;
num2
=
num1
^
num2
;
num1
=
num1
^
num2
;
printf
(
"num1 = %d,num2 = %d\n"
,
num1
,
num2
)
;
return
0
;
}
|
按位左移 <<
1个二进制补码按位左移,就是将这个二进制位,向左移动指定的位数,溢出部分丢弃 低位补零。
1
2
3
4
5
|
3
<<
2
;
3的补码按位左移
2位
.
00000000
00000000
00000000
00000011
000000
00000000
00000000
0000001100
+
12
|
注意:
1个数按位左移,有可能改变其正负性。
1个数按位左移n位,至关于这个数乘以2的n次方。
16 << 3; 至关于16 * 2的3次方。就是16*8=128
按位右移 >>
参与按位右移的二进制的补码,向右移动指定的位数,溢出部分补齐,高位补符号位。
1
2
3
4
|
3
>>
2
;
00000000
00000000
00000000
00000011
0000000000
00000000
00000000
000000
0
|
注意:
1一个数按位右移,不会改变1个数的正负性。
1个数按位右移n位,至关于这个数除以2的n次方。
6、int类型的修饰符
咱们声明1个int类型的变量,会在内存之中申请4个字节的空间,能够存储的数据-2147483647到+2147483648之间的整数。但是有的时候,数据要不了那么大,4个字节就显得很浪费。而有的时候,数据太大,4个字节又不够。这个时候,咱们就可使用int类型的修饰符来解决这个问题了。
int类型的修饰符有short,long,long long。他们能够限定int类型的变量在内存之中占据多少个字节。
short
被short修饰的int变量,在内存之中占据2个字节。在不考虑正负的状况能够表示65536个数。最高位表示符号位能够储存-32767到+32768之间的整数。咱们可使用%hd来输出short int 变量的值,若是要声明short int变量的话. 能够省略int。好比:short num = 12;
long
被long修饰的int变量,在内存之中占据8个字节(64位编辑器),使用%ld,输出long int变量的值。而且若是要声明1个long int变量,能够省略int。好比: long num = 100;
long long
被long long 修饰的int变量不管是多少位的系统都占据8个字节,使用%lld来输出long long int 变量的值。而且也能够省略int 。好比: long long num = 100;
unsigned
咱们声明1个int类型的变量,占据4个字节,最高位用来表示符号位。可是咱们可使用1个关键字,让这个变量的最高位不表示符号位,所有位数都用来表示数据。这样最小值就只能存储0,可是最大值能够翻番。
signed
要求变量的最高位用来表示符号位,默认就是这样的。因此这个关键词通常没啥用。