C语言第二章:C语言基本知识
第一节:计算机知识了解
CPU,内存条,硬盘,显卡,主板之间的关系:
- CPU不能直接处理硬盘上的数据,首先会把硬盘的数据加载到内存条(内存)中,然后进行数据的处理(主板是负责连接各个组件的地方,在上面有多个插槽,可以插上CPU,内存条硬盘等其他硬件设备),数据处理完成之后把处理结果通过显卡显示到显示器上。
Hello,World程序是怎么运行起来的:
- 首先VC++软件通过请求操作系统内存分配获取到自己的空间,然后加载本地硬盘的.c源文件到内存中,然后CPU进行处理,数据处理完成之后,通过操作系统返回给VC++软件,该软件再通过操作系统的请求操作完成处理结果的显示
第二节:常量
定义: 常量就是在程序中不可变化的量,常量在定义的时候必须要给一个初始值,因为常量一旦定义就无法改变其值
常量在C语言中如何表示:
- 整数
- 十进制:23
- 十六进制:0x17
- 八进制:027
- 浮点数
- 传统写法:3.2,C语言中默认是double类型的,在数据后面加F或者f表示float类型,例如:2.3f
- 科学计数法:3.2e3 //值是3200
- 字符
- 字符必须是用单引号括起来的,例如:'A',但是不能放一个汉字(汉字是两个字节,而char是一个字节)
常量以什么样的二进制形式存储在计算机中:
- 整数是以二进制的补码形式存储在计算机中
- 浮点数是以IEEE754标准转换成二进制存储在计算机中
- 字符的本质也是和整数的存储方式一样的
宏常量:
- 是用# define进行定义的,如下
# include <stdio.h> # define MAX 100 // 定义宏常量,注意不是分号结尾,而且常量名称大写 int main(void) { // MAX = 90; 此处是报错的,因为常量一旦定义好就无法再改变 printf("%d",MAX); return 0; }
const常量:
- 用const来进行定义常量,如下
# include <stdio.h> int main(void) { const int max = 100; // 定义一个const常量 // max = 90; 此处是报错的,因为常量一旦定义好就无法再改变 printf("%d",max); return 0; }
第三节:整数常量
什么是字节:
- 字节是存储数据的单位,也是硬件所能访问的最小单位,内存中存储的最小单位就是字节。
- 一个位只能表示0或者1,简称bit
- 一个字节为8个二进制位,称为8位,简称byte
- 一个字为两个字节,简称word
- 两个字为双字,简称dword
二进制:
- 二进制在C语言中没有表示的。二进制在计算机中是以补码的形式存在的。二进制不存在负数概念。
- 此处关于8位二进制补码问题:1000 0000如果为原码表示是-0,如果为补码呢? 因为补码其符号位为1,所以表示是负数,则该二进制的真实值应为负数,所以应该逆过来将补码减1,然后取反,就是原码。1000 0000减1为0111 1111,但是最高位为符号位,所以这里应该是1111 1111,再取反得1000 0000,此时的值表示的是该二进制实际值的绝对值,又因为该数为负数,所以为-128;
二进制转换成八进制方法:
- 将三个二进制组成一位,不够左边补0。
二进制 | 八进制 |
---|---|
011 110 111 | 0367 |
100 011 101 | 0435 |
二进制转换成十进制的方法:
- 采用1248的方法,累加起来即可。
二进制(补码) | 十进制 |
---|---|
0000 0000 | 0 |
0000 0001 | 1 |
0000 0010 | 2 |
.... .... | ... |
0111 1111 | 127 |
1000 0000 | -128 |
二进制转换成十六进制的方法:
- 将四个二进制位组成一位,不够左边补0。
二进制 | 十六进制 |
---|---|
0111 1111 | 0x7F |
0001 1011 | 0x1B |
十进制转换成二进制的方法:
- 将十进制作为被除数,2作为除数,知道被除数商为0,将每次商后的余数倒过来就是二进制。
八进制:
- 八进制在C语言中以0开头。八进制不存在负数概念。
# include <stdio.h> int main(void) { printf("%d", 0100); // 输出结果为64 ,将8进制的0100转换成十进制为64 }
八进制转换成二进制:
- 将八进制的每一位拆分成三位二进制数即可。
八进制 | 二进制 |
---|---|
0367 | 011 110 111 |
0435 | 100 011 101 |
八进制转换成十进制:
- 从右到左,按照权重,从0开始,每次底数为8,将和累加即可。
0435 = 4*8^2 + 3*8^1 + 5*8^0 = 256 + 24 + 5 = 285
八进制转换成十六进制:
- 将八进制转换成二进制,然后4位组合成一个数。
十六进制:
- 十六进制在C语言中以0x或者0X表示,十六进制不存在负数概念,只有十进制有。
# include <stdio.h> int main(void) { printf("%d", 0x7F); // 输出结果为127 return 0; }
十六进制转换成二进制:
- 将十六进制的每一位数转换成4个为一组的二进制即可。
十六进制 | 二进制 |
---|---|
0x7F | 0111 1111 |
十六进制转换成八进制:
- 将十六进制先转换成二进制,然后每3位为一组再进行组合即可。
十六进制 | 二进制 | 八进制 |
---|---|---|
0x7F | 0111 1111 | (001 111 111) 0177 |
十六进制转换成十进制:
- 将十六进制的每一位作为乘数,乘以8的权重(从0开始,从左到右,权重依次增加)。
0x7F = 7*16^1 + 15*16^0 = 112 + 15 = 127
原码: 将最高位作为符号位(0代表正数,1代表负数),其余位为数值位,是该数值的绝对值
十进制 | 二进制(原码) |
---|---|
+7 | 0000 0111 |
-7 | 1000 0111 |
+0 | 0000 0000 |
-0 | 1000 0000 |
127 | 0111 1111 |
-127 | 1111 1111 |
反码:
- 一个数如果值为正数,那么原码和反码相同。
- 一个数如果值为负数,那么符号位不变,数值位相反。
数值 | 原码 | 反码 |
---|---|---|
+7 | 0000 0111 | 0000 0111 |
-7 | 1000 0111 | 1111 1000 |
-0 | 1000 0000 | 1111 1111 |
127 | 0111 1111 | 0111 1111 |
-127 | 1111 1111 | 1000 0000 |
补码:
- 一个数如果值为正数,那么原码和补码相同。
- 一个数如果值为负数,那么符号位不变,数值位取反加1(相当于反码加1)。
数值 | 原码 | 补码 |
---|---|---|
+7 | 0000 0111 | 0000 0111 |
-7 | 1000 0111 | 1111 1000 |
-0 | 1000 0000 | 1000 0000 |
127 | 0111 1111 | 0111 1111 |
-127 | 1111 1111 | 1000 0001 |
第三节:字符常量
字符常量:
- 字符常量用单引号括起来,字符常量占一个字节,也就是8bit,所以不能存放汉字,字符常量是根据ASCII码来进行运算的。
什么是ASCII码:
- ASCII码是一种标准,它规定了字符和整数之间的对应关系,常见的ASCII如下:
- 'A' -- 65
- 'a' -- 97
- '0' -- 48
第四节:浮点数常量
浮点数常量:
- 浮点数常量就是小数,分为单精度和双精度,例如:4.3就是浮点数常量
第五节:常见数据类型
什么是数据类型:
- 数据类型就是数据的分类,常见的分类如下:
- 基本数据类型:
- 整型:
- int 整型 4个字节
- short int 短整型 2个字节
- long int 长整型 8个字节
- 浮点型:
- float 单精度浮点类型 4个字节
- double 双精度浮点类型 8个字节
- 字符型:
- char(C语言中是没有字符串的,char类型遵从ASCII码标准,不能存放汉字) 1个字节
- 整型:
- 复合数据类型:
- 结构体
- 枚举
- 共用体
什么是变量:
- 变量的本质就是内存中的一段存储空间
变量为什么要初始化:
- 所谓的初始化就是赋值的意思
- 在一个软件运行的过程中,会到内存中申请空间,并进行一些操作,会把一些数据放入到对应的内存空间中,当该软件运行结束后,该软件所占用的内存就会释放掉,但是其内存中的数据并不会被清理掉(内存释放掉就是简单的修改一个数据标志位,而不是再去清空内存,这样做节约了时间),这时候的数据,我们称之为“垃圾数据”,当我们用VC++写好C语言程序的时候,变量所对应的内存地址所在的空间很有可能指向的内存空间中包含了垃圾数据,因此不初始化会将垃圾数据引入到程序中,会造成一些无法计算的结果!所以变量必须初始化(如果不存在垃圾数据,也未被初始化的话,C语言的编译器会分配随机值或者固定值)
如何定义变量:
- 数据类型 变量名称 = 数据值;
- 数据类型 变量名称; 变量名称 = 数据值;
- 例如:int i = 0;或者int j; j = 0;
第六节:整型数据类型
整型具体介绍:
- short
- 短整型,在32位操作系统下占两个字节。
- int
- 整型,无论在32还是64位都是占4个字节。
- long
- 长整型,无论是32位还是64位都是占4个字节,在Unix占8个字节。
- long long
- 长长整型,无论是32位还是64位都是占64个字节。计算效率低,一般不适用。
- unsigned 数据类型
- 无符号整数,无符号的含义是没有符号位,即最高位符号位不再当做符号位,而是当做数值位。因此无符号数是没有负数的。当unsigned关键字修饰int,short,long,longlong的时候,所占的字节数不变。(C语言中还有个关键字signed,是表示有符号,但是通常不写就表示有符号,所以这个关键字基本不用)。
整数溢出:
# include <stdio.h> int main(void) { short i = -2; int j = i; /* 此时将一个2个字节赋值到4个字节,那么,计算机会自动将最高位填充为符号 位,如果i为正数,则前面的2个字节填充的是0,如果是负数,前面两个字节填 充的是1 */ return 0; }
大端对齐与小端对齐:
对于ARM,Intel这种X86架构的复杂指令集CPU,整数在内存中是倒着存放的,低地址放低位,高地址放高位。这种称为小端对齐
对于Unix服务器的CPU而言,更多的是采用大端对齐方式,即低地址放高位,高地址放低位。
例如存放:int i = 0x12345678;
地址 | 大端模式 | 小端模式 |
0x00 | 12 | 78 |
0x01 | 34 | 56 |
0x02 | 56 | 34 |
0x03 | 78 | 12 |
第七节:字符类型
char 类型变量是占一个字节的,8位,是C语言中占用空间最小的了(因为C语言不允许存在bit)。char的本质就是一个整数,只有一个字节大小的整数,其输出%c的时候是用ASCII来进行解析输出的。当输出%d的时候,就会输出这个字符的ASCII码值。
# include <stdio.h> int main() { char c; // 定一个一个char类型变量 c = 'a'; // char常量用单引号括起来 printf("%c", c); // %c 输出一个字符是 return 0; }
第八节:浮点数类型
float在32位操作系统下是4个字节,double在32位操作系统下是8个字节。小数的效率很低,避免使用。
浮点数是以IEEE745标准在计算机内存存储的。
# include <stdio.h> int main(void) { // 定义一个单精度类型的浮点数变量f1 float f1 = 32.1; // 定义一个双精度类型的浮点数变量d1 double d1 = 4.5; // 如何实现浮点数的四舍五入呢? // 只需要将浮点数加0.5然后把结果存入int类型变量即可 int i1 = f1 + 0.5; double d2 = 3 / 2; printf("%d", d2); // 输出结果为1.00,而不是1.5?因为在3/2的时候是两个int整数相除,会自动转换 // 成整数,因此我们可以在被除数或者除数变成3.0或者2.0即可。或者强转 return 0; }
第九节:字符串的格式化输入和输出
字符串在计算机内部的存储方式
- 字符串是在内存中一段连续的char空间,以'\0'结尾。
- 字符串常量用双引号括起来
printf函数
格式化字符 | 数据类型 | 含义 |
---|---|---|
%d | int | 输出为有符号的十进制整数 |
%hd | short int | 输出短整数 |
%ld | long | 输出长整数 |
%u | unsigned int | 输出无符号十进制整数 |
%hu | unsigned short int | 输出无符号十进制短整数 |
%o | unsigned int | 输出无符号八进制整数 |
%x / %X | unsigned int | 输出无符号十六进制整数 |
%f | float | 输出单精度浮点数 |
%lf | double | 输出双精度浮点数 |
%e/%E | double | 科学计数法表示双精度浮点数 |
%c | char | 输出字符,如果要输出的数为整数,则会根据ASCII码表进行转换成相应的字符 |
%s | char * str | 输出字符串 |
%p | void * | 以十六进制形式输出指针地址 |
%% | % | 输出百分号 |
%- | - | 表示左对齐 |
%n | - | 宽度至少为n位,不够前面用0补充 |
putchar函数
- 输出一个字符的函数,只能放入整数或者单个字符。如果是整数则输出该整数对应的ASCII码值,如果为字符,则输出该字符。
# include <stdio.h> int main(void) { // 输出字符A putchar('A'); // 输出97对应的ASCII码值 putchar(97); return 0; }
scanf函数
- 获取用户从标准输入设备输入的数据,必须使用取地址符&,将获取到的数据放置到该地址。
# include <stdio.h> int main(void) { int i = 0; // %d 是获取输入数据并得到相应数据类型 // &i 是获取i的地址,并将获取的数据放到i中 scanf("%d",&i); return 0; }
getchar函数
- 获取一个键盘输入的ASCII码值。这里获取的是char类型的。
# include <stdio.h> int main(void) { char a; a = getchar();// 得到一个ASCII码值 char b; a = getchar();// 得到一个ASCII码值 // 这里灵活的将ASCII码值转换成整数 printf("a + b = %d",(a-'0')+(b-'0')); return 0; }
sizeof关键字:
- sizeof关键字是可以通过传递一个数据类型,变量为参数,然后返回该数据类型或者变量的所占内存的字节大小。
# include<stdio.h> int main(void) { int i = 0; sizeof(i); // 输出为4 sizeof(int); // 输出为4 return 0; }
第十节:类型限定
const:
- const代表一个不能改变值得常量。
volatile:
- volatile表示变量是一个可能被CPU指令之外的地方改变的。编译器就不会针对这个变量进行代码优化。
# include <stdio.h> int main(void) { int i = 100; i = i + 10; // ①外部设备在此对i进行赋值为0 i = i + 20; i = i + 30; // 上述计算CPU会认为我们编写的代码太傻,擅自给我们优化成i = i + 60; // 倘若我们在上述代码①处进行了某些数据的改变,而CPU又擅自给我们更改了, // 则会破坏正确的代码,因此我们加入volatile,不让CPU自作主张 volatile int i = 100; i = i + 10; i = i + 20; i = i + 30; return 0; }
register:
- 让变量在寄存器(寄存器就是在CPU里面的内存,执行速度最快)里,而不是在内存里。register是建议型的,而不是命令型的。(CPU只会在寄存器里有空间的时候才会给分配到寄存器,否则不理会)
第十一节:运算符
运算符:
- 算数运算符:
- 加(+) , 减(-) , 乘(*) , 除(/) , 取余(%)
- 关系运算符:
- > ,>= ,< ,<= ,== , !=
- 逻辑运算符:
- ! 非 , && 并且 ,|| 或
- 赋值运算符:
- = , += ,-= ,/= ,*=
- C语言对于真假的处理:
- 假是用0表示
- 真是用1表示(非零就是真)
- 三目运算符:
- 条件表达式?表达式1:表达式2;
- 如果条件表达式的结果为true,则返回表达式1的结果,如果为false,返回表达式2的结果
- 例如:char ch = 4>2?'a':'b';,返回结果为:a
- 自增/自减运算符:
- i++:
- 先把i的值进行运算或者赋值,i++运算结束后或者赋值结束后,i的值加1
- 例如:
- int i = 1 ; int a = i++; // a的值为:1
- int i = 1; int a= (i++)+(i++); // a的值为:2
- ++i
- 先把i的值进行加1,然后再进行运算或者赋值
- 例如:
- int i = 1; int a = ++i; //a的值为:2
- int i = 1; int a = (i++) + (++i); // a的值为:4
- i++:
C语言中的除法和取余运算:
- 除法:除法的运算结果和运算对象的数据类型有关,倘若运算对象都是int,则结果为int类型(如果有小数会被舍弃掉),如果运算对象中有一个是浮点数,则运算结果则为浮点数
- 取余:取余的运算对象必须是整数,运算结果的符号和被除数的符号相同!(取余结果的正负只与被除数有关)
# include <stdio.h> int main(void) { int i = 42342; int i1 = i%10;// 取得个位 int i2 = i/10%10;// 取得十位 int i3 = i/10/10%10; //取得百位 // ...... return 0; }