1.C语言的概述
1.1什么是C语言
我们都知道人与人之间可以通过汉语、英语等自然语言进行交流沟通。
那么人们用什么方式可以和计算机做最直接的交流?
C语言是一门面向过程的计算机编程语言,与C++、C#、Java等面向对象编程语言有所不同。C语言的设计目标是提供一种能以简易的方式编译、处理低级存储器、仅产生少量的机器码以及不需要任何运行环境支持便能运行的编程语言。C语言描述问题比汇编语言迅速、工作量小、可读性好、易于调试、修改和移植,而代码质量与汇编语言相当。C语言一般只比汇编语言代码生成的目标程序效率低10%-20%。因此,C语言可以编写系统软件。
当前阶段,在编程领域中,C语言的运用非常之多,它兼顾了高级语言和汇编语言的优点,相较于其它编程语言具有较大优势。计算机系统设计以及应用程序编写是C语言应用的两大领域。同时,C语言的普适较强,在许多计算机操作系统中都能够得到适用,且效率显著。
1.2C语言的特点
- 代码量小
- 执行速度快
- 功能强大
- 编程自由
- 可移植性较差
- 实现代码周期较长
- 过于自由
- 对平台库依赖较多
.dil文件就是封装好的库函数文件
C语言的应用领域
- 网站后台程序
- 大型游戏引擎
- 编写另一种语言(py)
- 编写操作系统和驱动程序
- 配置微处理器
C语言仅有32个关键字,9种控制语句,34种运算符,却能完成无数功能。
32个关键字
auto | break | case | char | const | continue | default | do |
---|---|---|---|---|---|---|---|
double | else | enum | extern | float | for | goto | if |
int | long | register | return | short | signed | sizeof | static |
struct | switch | typedef | union | unsigned | void | volatile | while |
9种控制语句
- if( )~else 条件语句
- for( )~ 循环语句
- while( )~ 循环语句
- do~while( ) 循环语句
- continue 结束本次循环语句
- break 中止执行switch或循环语句
- switch 多分支选择语句
- goto 转向语句
- return 从函数返回语句
C语言中一共有34个运算符,大致的优先级别是:初等运算符、单目运算符、算数运算符、关系运算符、逻辑运算符、条件运算符、赋值运算符、逗号运算符。
第一优先级
运算符 | 名称(含义) | 使用形式 | 结合方式 | 说明 |
---|---|---|---|---|
[] | 数组下标 | 数组名[常量表达式] | 从左到右 | |
() | 圆括号 | (表达式)/函数名(表达式) | 从左到右 | |
. | 结构体运算符 | 结构体.成员名 | 从左到右 | |
-> | 指向结构体运算符(指针) | 结构体->成员名 | 从左到右 |
第二优先级
运算符 | 名称(含义) | 使用形式 | 结合方式 | 说明 |
---|---|---|---|---|
- | 符号运算符 | -表达式 | 从右到左 | 单目运算符 |
(类型) | 强制类型转化 | (数据类型)表达式 | 从右到左 | 单目运算符 |
++ | 自增运算符 | ++表达式/表达式++ | 从右到左 | 单目运算符 |
– | 自减运算符 | –表达式/表达式++ | 从右到左 | 单目运算符 |
* | 取值运算符 | *指针变量 | 从右到左 | 单目运算符 |
& | 取地址运算符 | &变量名 | 从右到左 | 单目运算符 |
! | 逻辑非 | !表达式 | 从右到左 | 单目运算符 |
~ | 逻辑取反 | ~表达式 | 从右到左 | 单目运算符 |
sizeof | 长度运算符 | sizeof(表达式) | 从右到左 | 单目运算符 |
第三优先级
运算符 | 名称(含义) | 使用形式 | 结合方式 | 说明 |
---|---|---|---|---|
/ | 除 | 表达式/表达式 | 从左到右 | 双目运算符 |
* | 乘 | 表达式*表达式 | 从左到右 | 双目运算符 |
% | 取余 | 整型表达式%整型表达式 | 从左到右 | 双目运算符 |
第四优先级
运算符 | 名称(含义) | 使用形式 | 结合方式 | 说明 |
---|---|---|---|---|
+ | 加 | 表达式+表达式 | 从左到右 | 双目运算符 |
- | 减 | 表达式-表达式 |
第五优先级
运算符 | 名称(含义) | 使用形式 | 结合方式 | 说明 |
---|---|---|---|---|
<< | 左移 | 变量<<表达式 | 从左到右 | 双目运算符 |
>> | 右移 | 变量>>表达式 |
第六优先级
运算符 | 名称(含义) | 使用形式 | 结合方式 | 说明 |
---|---|---|---|---|
> | 大于 | 表达式>表达式 | 从左到右 | 双目运算符 |
>= | 大于等于 | 表达式>=表达式 | ||
< | 小于 | 表达式<表达式 | ||
<= | 小于等于 | 表达式<=表达式 |
第七优先级
运算符 | 名称(含义) | 使用形式 | 结合方式 | 说明 |
---|---|---|---|---|
== | 等于 | 表达式==表达式 | 从左到右 | 双目运算符 |
!= | 不等于 | 表达式!=表达式 |
第八优先级
运算符 | 名称(含义) | 使用形式 | 结合方式 | 说明 |
---|---|---|---|---|
& | 按位与 | 表达式&表达式 | 从左到右 | 双目运算符 |
第九优先级
运算符 | 名称(含义) | 使用形式 | 结合方式 | 说明 |
---|---|---|---|---|
^ | 按位异或 | 表达式^表达式 | 从左到右 | 双目运算符 |
第十优先级
运算符 | 名称(含义) | 使用形式 | 结合方式 | 说明 |
---|---|---|---|---|
| | 按位或 | 表达式|表达式 | 从左到右 | 双目运算符 |
第十一优先级
运算符 | 名称(含义) | 使用形式 | 结合方式 | 说明 |
---|---|---|---|---|
&& | 逻辑与 | 表达式&&表达式 | 从左到右 | 双目运算符 |
第十二优先级
运算符 | 名称(含义) | 使用形式 | 结合方式 | 说明 |
---|---|---|---|---|
|| | 逻辑或 | 表达式||表达式 | 从左到右 | 双目运算符 |
第十三优先级
运算符 | 名称(含义) | 使用形式 | 结合方式 | 说明 |
---|---|---|---|---|
?: | 条件运算符 | 表达式1?表达式2:表达式3 | 从右到左 | 三目运算符 |
第十四优先级
运算符 | 名称(含义) | 使用形式 | 结合方式 | 说明 |
---|---|---|---|---|
= | 赋值 | 变量=表达式 | 从右到左 | 双目运算符 |
/= | 除后赋值 | 变量/=表达式 | ||
*= | 乘后赋值 | 变量*=表达式 | ||
%= | 取余后赋值 | 变量*=表达式 | ||
+= | 加后赋值 | 变量+=表达式 | ||
-= | 减后赋值 | 变量-=表达式 | ||
<<= | 左移后赋值 | 变量<<=表达式 | ||
>>= | 右移后赋值 | 变量>>=表达式 | ||
&= | 按位与后赋值 | 变量&=表达式 | ||
^= | 按位异或后赋值 | 变量^=表达式 | ||
|= | 按位或否赋值 | 变量|=表达式 |
第十五优先级
运算符 | 名称(含义) | 使用形式 | 结合方式 | 说明 |
---|---|---|---|---|
, | 逗号运算符 | 表达式,表达式,表达式,… | 从左到右 |
1.3第一个C语言程序
编写helloWorld.c程序
#include<stdio.h>//导入一个文件:stdio是标准输入输出库
int main() {//int 表示函数的返回值为整形
//main 程序的主函数,
int a = 123;
printf("hello world!!\n");
printf("%d\n", a);// %d是一个占位符,表示输出一个整型数据
return 0;
}
gcc编译程序
gcc -o hello.exe hello.c
gcc -o hello *.c
arm-linux-gcc -o test.out *.c
1.4system函数
system是一个C/C++的函数。 在windows操作系统下system () 函数主要是在C语言中的应用,system函数需加头文件#include<stdlib.h>
后方可调用。 系统命令就是一些基于cmd的命令。
通过system命令打开计算器
#include<stdlib.h>
int main(void) {
system("calc");
return 0;//如果命令正确执行了。返回值为0 如果命令执行错误,返回值为其他值。
}
常见cmd命令
命令 | 描述 |
---|---|
ipconfig | 查看ip地址 |
netstat | 查看网络连接状态 |
netstat -ano | 查看网络连接、状态以及对应的进程id |
shutdown | 关机 -f强制关闭、-p无超时或警告的关机、-s -t 60 定时关机 |
shutdown -r | 关机并重启 |
shutdown -l | 注销当前用户 |
shutdown -r -t 秒数 | 一段时间后重启 |
shutdown -a | 解除命令 |
cls | 清除屏幕 |
exit | 退出cmd |
notepad | 打开记事本 |
dxdiag | 检查DirectX信息 |
winver | 检查Windows版本 |
wmimgmt.msc | 打开windows管理体系结构(WMI) |
wscript | windows脚本设置 |
write | 写字板 |
wiaacmgr | 扫描仪和相机 |
calc | 计算器 |
mspaint | 画图板 |
mstsc | 远程桌面连接 |
mmc | 打开控制台 |
devmgmt.msc | 设备管理器 |
taskmgr | 任务管理器 |
explorer | 文件资源管理器 |
regedit.exe | 注册表 |
perfmon.msc | 计算机性能监测 |
eventvwr | 事件查看器 |
net user | 查看用户 |
whoami | 查看当前用户 |
net user %username% 123456 | 将电脑用户密码修改为123456,%%中填写用户名称(慎用) |
ESC | 清除当前命令行 |
电脑快捷键
快捷键 | 描述 |
---|---|
win+E | 打开文件管器 |
win+D | 显示桌面 |
win+L | 锁计算机 |
alt+F4 | 关闭当前程序\文件 |
ctrl+shift+Esc | 打开任务管理器(或者ctrl+alt+delete) |
ctrl+F | 在一个文本或者网页里面查找,相当实用(退出一般按ESC) |
ctrl+A | 选中所有文本,或所有文件 |
crtl+alt+tab | 选中窗口但不打开,使用回车打开。按tab或←→切换 |
alt+tab | 选中窗口并打开 |
win+tab | 任务视图 |
ctrl+tab | 切换窗口(仅同一软件内多个窗口有效,如浏览器开了许多个网页) |
1.5C语言程序编译过程
C代码编译成可执行程序经过4步:
- 预处理:宏定义展开、头文件展示、条件编译等,同时将代码中的注释删除,这里并不检查语法。
- 编译:检查语法,将预处理后的文件编译生成汇编文件。
- 汇编:将汇编文件生成目标文件(二进制文件)。
- 链接:C语言写的程序是需要依赖各种库文件的,所以编译之后还需要把库链接到最终的可执行程序中去。
文件大小单位换算
单位 | 描述 |
---|---|
bit | 比特位,是计算机表示数据最小的单位 |
byte | 字节,1byte=8bit |
B(字) | 1byte=1B(Unicode编码中,一个英文为一个子节,一个中文为两个子节) |
KB | 1KB=1024B |
MB | 1MB=1024KB |
GB | 1GB=1024MB |
TB | 1TB=1024GB |
PB | 1PB=1024TB |
EB | 1EB=1024PB |
… | … |
常见编码方式
ASCII 码
美国信息交换标准代码是基于拉丁字母的一套电脑编码系统,主要用于显示现代英语和其他西欧语言。它是最通用的信息交换标准,并等同于国际标准 ISO/IEC 646。ASCII第一次以规范标准的类型发表是在1967年,最后一次更新则是在1986年,到目前为止共定义了128个字符 。
Bin(二进制) | Oct(八进制) | Dec(十进制) | Hex(十六进制) | 缩写/字符 | 解释 |
---|---|---|---|---|---|
0000 0000 | 00 | 0 | 0x00 | NUL(null) | 空字符 |
0000 0001 | 01 | 1 | 0x01 | SOH(start of headline) | 标题开始 |
0000 0010 | 02 | 2 | 0x02 | STX (start of text) | 正文开始 |
0000 0011 | 03 | 3 | 0x03 | ETX (end of text) | 正文结束 |
0000 0100 | 04 | 4 | 0x04 | EOT (end of transmission) | 传输结束 |
0000 0101 | 05 | 5 | 0x05 | ENQ (enquiry) | 请求 |
0000 0110 | 06 | 6 | 0x06 | ACK (acknowledge) | 收到通知 |
0000 0111 | 07 | 7 | 0x07 | BEL (bell) | 响铃 |
0000 1000 | 010 | 8 | 0x08 | BS (backspace) | 退格 |
0000 1001 | 011 | 9 | 0x09 | HT (horizontal tab) | 水平制表符 |
0000 1010 | 012 | 10 | 0x0A | LF (NL line feed, new line) | 换行键 |
0000 1011 | 013 | 11 | 0x0B | VT (vertical tab) | 垂直制表符 |
0000 1100 | 014 | 12 | 0x0C | FF (NP form feed, new page) | 换页键 |
0000 1101 | 015 | 13 | 0x0D | CR (carriage return) | 回车键 |
0100 0001 | 0101 | 65 | 0x41 | A | 大写字母A |
0110 0001 | 0141 | 97 | 0x61 | a | 小写字母a |
… |
UTF-8编码
字节 | 格式 | 实际编码位 | 码点范围 |
---|---|---|---|
1字节 | 0xxxxxxx | 7 | 0 ~ 127 |
2字节 | 110xxxxx 10xxxxxx | 11 | 128 ~ 2047 |
3字节 | 1110xxxx 10xxxxxx 10xxxxxx | 16 | 2048 ~ 65535 |
4字节 | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx | 21 | 65536 ~ 2097151 |
2.数据类型
基本数据类型、构造类型、指针类型
基本类型
类型 | 存储大小 | 值范围 |
---|---|---|
char | 1 字节 | -128 到 127 或 0 到 255 |
unsigned char | 1 字节 | 0 到 255 |
signed char | 1 字节 | -128 到 127 |
int | 2 或 4 字节 | -32,768 到 32,767 或 -2,147,483,648 到 2,147,483,647 |
unsigned int | 2 或 4 字节 | 0 到 65,535 或 0 到 4,294,967,295 |
short | 2 字节 | -32,768 到 32,767 |
unsigned short | 2 字节 | 0 到 65,535 |
long | 4 字节 | -2,147,483,648 到 2,147,483,647 |
unsigned long | 4 字节 | 0 到 4,294,967,295 |
浮点类型
类型 | 存储大小 | 值范围 | 精度 |
---|---|---|---|
float | 4 字节 | 1.2E-38 到 3.4E+38 | 6 位有效位 |
double | 8 字节 | 2.3E-308 到 1.7E+308 | 15 位有效位 |
long double | 16 字节 | 3.4E-4932 到 1.1E+4932 | 19 位有效位 |
void 类型
在 C 语言中,void 被翻译为**“无类型”,相应的void *** 为**“无类型指针”**。
void 似乎只有"注释"和限制程序的作用,当然,这里的"注释"不是为我们人提供注释,而是为编译器提供一种所谓的注释。
- 当函数不需要返回值时,必须使用void限定,例如:void func(int a,char *b)。
- 当函数不允许接受参数时,必须使用void限定,例如:int func(void)。
序号 | 类型与描述 |
---|---|
1 | 函数返回为空 C 中有各种函数都不返回值,或者可以说它们返回空。不返回值的函数的返回类型为空。例如 void exit (int status); |
2 | 函数参数为空 C 中有各种函数不接受任何参数。不带参数的函数可以接受一个 void。例如 int rand(void); |
3 | 指针指向 void 类型为 void * 的指针代表对象的地址,而不是类型。例如,内存分配函数 void *malloc( size_t size ); 返回指向 void 的指针,可以转换为任何数据类型。 |
控制语句关键字
控制语句 | 描述 |
---|---|
if | 条件控制语句 |
else | 条件控制语句 |
switch | 条件分支语句 |
case | 条件分支语句 |
default | 用在switch分支语句中一般与case搭配使用,含义是缺省的其他条件 |
for | 循环语句 |
do | 循环语句 |
while | 循环语句 |
break | 循环约束语句;一般在switch语句中使用 |
continue | 循环约束语句;结束本次循环,继续下次循环 |
goto | 无责任跳转语句 |
return | 在函数中:结束函数;在主函数中:结束主函数的执行 |
存储类型关键字
存储类型 | 描述 |
---|---|
auto | 用于定义局部变量(函数内部变量)一般不用写 |
extern | 声明 |
register | 建议性指令 |
static | 定义静态的数据(局部、全局变量)、定义静态函数 |
const | 定义常量 |
构造类型
构造类型 | 描述 |
---|---|
[] | 数组类型 |
struct | 结构类型 |
union | 联合类型 |
enum | 枚举类型 |
其他关键字
关键字 | 描述 |
---|---|
sizeof | 是一种单目运算符:获取某个数据类型所占用空间的字节数 |
typedef | 用于定义函数指针、用于对一个已存在的数据类型起别名 |
volatile | 被设计用来修饰被不同线程访问和修改的变量,可解释为“直接存取原始内存地址”;volatile提醒编译器它后面所定义的变量随时都有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,告诉编译器对该变量不做优化,都会直接从变量内存地址中读取数据,从而可以提供对特殊地址的稳定访问。 |
常量与变量
定义常量的方式:宏定义、const(不安全的写法)关键字。
#include<stdio.h>
#define PI 3.1415926//这样是宏定义常量,注意没有分号
void main() {
//黄瓜3元每斤,购买5斤
//在整个程序运行过程中其值不会改变的量叫常量,会发生改变的是变量
const int price = 3;//使用const修饰后不能再修改他的值
int weight = 5;
int money = price * weight;
printf("买菜共花了%d元\n", money);
//求圆的面积 s = Π*r*r
//园的周长 L = 2Πr
const float pi = 3.1416;//常量Π
float r = 3;//正规写法float r = 2.f 2. 2.0都一样
float S = pi * r * r;
float L = 2 * PI * r;
printf("圆的面积为%f\n",S);//%f输出一个十进制的浮点型,默认保留6位小数
printf("圆的周长为%.2f\n",L );//保留两位小数,会四舍五入
}
标识符:由字母、数字、下划线组成;不能使用关键字,不能使用数字开头。
整形
整形变量的定义和输出
打印格式 | 含义 |
---|---|
%d | 输出一个有符号的10进制int类型 |
%o(字母o) | 输出8进制的int类型 |
%x | 输出16进制的int类型,字母以小写输出 |
%X | 输出16进制的int类型,字母以大写输出 |
%u | 输出一个10进制的无符号数 |
C语言表示相应进制数
进制 | 描述 |
---|---|
十进制 | 以正常数字1-9开头;如123 |
八进制 | 以数字0开头;如0123 |
十六进制 | 以0x开头;如0x123 |
二进制 | C语言不能直接书写二进制数 |
#include<stdio.h>
int main01(void)
{
//无符号 unsigned 有符号signed
int a = 10;//默认定义的变量都是有符号的(可以区分正负的)
signed int b = 10;
//定义无符号时不得出现负数,否则会得到一个奇怪的值
unsigned int c = -10;
//%u是一个占位符,表示输出一个无符号十进制整形数据
printf("%u\n",c);//4294967286
return 0;
}
int main()
{
int a = 123;
int b = 0123;
int c = 0xABC;
//如果输出一个十进制数用%d,八进制用%o,十六进制用%x
printf("十进制:%d\n",a);//十进制:123
printf("八进制:%o\n", b);//八进制:123
printf("十六进制:%x\n", c);//十六进制:abc
return 0;
}
整形的输入
//解决c4996报错;或者将scanf改成scanf_s
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
int a;
//使用scanf_s是为了解决c4996报错
scanf("%d",&a);//&为取地址运算符,输入时以换行为结束符
printf("你输入的值是:%d\n",a);
return 0;
}
整形的格式
数据类型 | 占用空间 |
---|---|
short(短整型) | 2子节 |
int(整型) | 4子节 |
long(长整型) | Windows为4子节,Linux为4子节(32位),8子节(64位) |
long long(长长整型) | 8子节 |
需要注意的是,整型数据在内存中占的字节数与所选择的操作系统有关。虽然C语言标准中没有明确规定整形数据地长度,但long类型整数的长度不能短于int类型,short类型整数的长度不能长于int类型。
当一个小的数据类型赋值给大的数据类型时,不会出错,因为编译器会自动转化。但当一个大的数据类型赋值给一个小的数据类型,那么就可能丢失高位。
#include<stdio.h>
int main()
{
int a = 10;
short b = 20;
long c = 30;
long long d = 40;
printf("%d\n",a);//10
printf("%hd\n", b);//20
printf("%ld\n", c);//30
printf("%lld\n", d);//40
//sizeof 计算数据类型在内存中占的字节(BYTE)大小
//1BYTE = 1bit
printf("整形:%d\n",sizeof(a));//4
printf("短整型:%d\n", sizeof(b));//2
printf("长整型:%d\n", sizeof(c));//4
printf("长长整形:%d\n", sizeof(d));//8
return 0;
}
sizeof关键字
- sizeof不是函数,所以不需要包含任何头文件,他的功能是计算一个数据类型的大小,单位为子节。
- sizeof的返回值为size_t
- size_t类型在32位操作系统下是unsigned int,是一个无符号整形。
字符型 吧
字符变量的定义和输出
字符变量用于存储一个单一字符,在C语言中用char表示,其中每个字符变量都会占用1个子节。在给字符变量赋值时,需要用一对英文引号(‘’、“”)把字符括起来
字符变量实际上并不是把该字符本身放到变量的内存单元中去,而是该字符对应的ASCII编码放到变量的存储单元中。char本质就是一个字节大小的整形。
#include<stdio.h>
int main()
{
char ch = 'a';
printf("%c\n",ch);//a
printf("%d\n", ch);//97 此时输出的是ASCII码
printf("字符的大小:%d\n", sizeof(ch));//1子节
return 0;
}
浮点型
实型变量也称为浮点型变量,浮点型变量是用来存储小数数值的。在C语言中,浮点型变量分为两种:单精度浮点数(float)、双精度浮点数(double),但是double型变量所表示的浮点数比float型变量更精确。
由于浮点型变量是由有限的存储单元组成的,因此只能提供有限的有效数字。在有效位以外的数字将被舍去,这样可能会产生一些误差。
不以f结尾的常量是double类型,以f结尾的常量(3.14f)是float类型
#include<stdio.h>
int main()
{
float a = 3.14f;
double b = 3.14;
printf("%f\n",a);//3.140000
printf("%lf\n", b);//3.140000
return 0;
}
#include<stdio.h>
int main()
{
float a = 3.14f;
double b = 3.14;
//%p是一个占位符;可输出 (无符号)16进制形式显示数据所对应的内存地址
printf("%p\n", &a);//006FFC48
printf("%p\n", &b);//006FFC38
return 0;
}
进制的转换
十进制与二进制
十进制转二进制
除2取余法
二进制转十进制
权值法
十进制与八进制
十进制转八进制
除8取余法
八进制转十进制
同样是权值法
十进制与十六进制
与上述方法相似
进制转换的规律
十进制 | 二进制 | 八进制 | 十六进制 |
---|---|---|---|
1 | 1 | 1 | 1 |
2 | 10 | 2 | 2 |
3 | 11 | 3 | 3 |
4 | 100 | 4 | 4 |
5 | 101 | 5 | 5 |
6 | 110 | 6 | 6 |
7 | 111 | 7 | 7 |
8 | 1000 | 10 | 8 |
9 | 1001 | 11 | 9 |
10 | 1010 | 12 | A |
11 | 1011 | 13 | B |
12 | 1100 | 14 | C |
13 | 1101 | 15 | D |
14 | 1110 | 16 | E |
15 | 1111 | 17 | F |
16 | 10000 | 20 | 10 |
- 十进制与二进制转换可通过8421法则快速实现
- 三位二进制数转为一位八进制数
- 四位二进制数转为以为十六进制数
小数的进制转换
十进制的小数转成二进制:小数部分和2相乘,取整数,不足1取0,每次相乘都是小数部分,顺序看取整后的数就是转化后的结果。
存储单位术语
二进制是计算技术中广泛采用的一种数制。二进制数据是用0和1两个数码来表示的数。他的基数为2,进位规则是“逢2进1”,借位规则是“借一当二”。
当前的计算机系统使用的基本上是二进制系统,数据在计算机中主要是以补码的形式存储的。
术语 | 含义 |
---|---|
bit(比特位) | 一个二进制代表一位,一个位只能表示0或1两种状态。数据传输以“位”为单位。 |
Byte(子节) | 一个字节为8个二进制,成为8位,计算机中最小的存储单元就是子节。 |
术语 | 含义 |
---|---|
WORD(双字节) | 两个字节,16位 |
DWORD | 两个WORD,四字节,32位 |
1b | 1bit,1位 |
1B | 1Byte,1子节,8位 |
1k,1K | 1024B |
1M(1兆) | 1024K |
1G | 1024M |
1T | 1024G |
1Kb | 1024bit,1024位 |
1KB | 1024Byte,1024子节 |
1Mb | 1024Kb = 1024*1024bit |
1MB | 1024KB = 1024*1024Byte |
C语言如何表示相应进制数
进制 | 表述方式 |
---|---|
十进制 | 正常数字0-9 |
八进制 | 以数字0开头,如0123 |
十六进制 | 以0x开头,如0x123 |
二进制 | C语言不能直接书写二进制 |
思考题
- 在C语言里八进制必须以(数字)0开头、十六进制必须以0x开头。
- 在C语言里标识符只能由数字、字母、下划线组成(不能以数字开头)。
- C语言里进制的表现形式有三种,分别是八进制、十进制、十六进制。
- C语言提供了sizeof运算符主要用于计算数据类型在内存中占的大小。
数据的存储方式
原码
一个数的原码(原始的二进制)有如下特点:
- 最高位做符号位,0表示正,为1表示负
- 其他数值部分就是数值本身绝对值的二进制数
- 负数的原码是在其绝对值的基础上,最高位变为1
十进制数 | 原码 |
---|---|
+15 | 0000 1111 |
-15 | 1000 1111 |
+0 | 0000 0000 |
-0 | 1000 0000 |
原码表示简单易懂,与带符号数本身转换方便,只要符号还原即可,但当两个正数相减或不同符号数相加时,必须比较两个数哪个绝对值大,才能决定谁减谁,才能确定结果是正还是负,所以原码不便于加减运算。
反码
对于正数,反码与原码相同。
对于负数,符号位不变,其他部分取反。
十进制数 | 反码 |
---|---|
+15 | 0000 1111 |
-15 | 1111 0000 |
+0 | 0000 0000 |
-0 | 1111 1111 |
反码运算也不方便,通常用来作为求补码的中间过渡。
补码
在计算机系统中,数值一律用补码来存储。
- 统一了零的编码
- 将符号位与其他位统一处理
- 将减法运算变为加法运算
- 两个用补码表示的数相加时,如果最高位(符号位)有进位,则进位会被舍弃
补码的特点:
- 对于正数:原码、反码、补码相同。
- 对于负数:其补码为它的反码加1。
- 补码符号位不动,其他位求反,最后整个数加1,得到原码。
十进制数 | 原码 | 反码 | 补码 |
---|---|---|---|
+15 | 0000 1111 | 0000 1111 | 0000 1111 |
-15 | 1000 1111 | 1111 0000 | 1111 0001 |
+0 | 0000 0000 | 0000 0000 | 0000 0000 |
-0 | 1000 0000 | 1111 1111 | 0000 0000 |
补码的运算过程
数值溢出
当超过一个数据类型能够存放的最大范围时,数值会溢出。
有符号位和最高位溢出的区别:符号位溢出会导致数的正负发生变化,但最高位的溢出会导致最高位丢失。
数据类型 | 占用空间 | 取值范围 |
---|---|---|
char | 1子节 | -128到127(-2^7 ~ 2^7-1) |
unsigned char | 1子节 | 0到255(0~2^8-1) |
类型限定符
限定符 | 含义 |
---|---|
extern | 声明一个变量,extern声明的变量没有建立存储空间。extern int a;//变量在定义的时候创建存储空间 |
const | 定义一个常量,常量的值不能修改。const int a =10; |
Volatile | 防止编译器优化代码 |
register | 定义寄存器变量,提高效率。register是建议型指令,而不是命令型的指令,如果cpu有空闲寄存器,那么register就生效,如果没有空闲寄存器,那么register无效。 |
字符串格式化输入输出
字符串常量
- 字符串是内存中一段连续的char空间,以’\0’(数字0)结尾。
- 字符串常量是由双引号括起来的字符序列,如“China”、"C"等都是合法的字符串常量。
字符串常量与字符常量不同:
‘a’字符常量 | "a"字符串常量 |
---|---|
‘a’ | ‘a’ ‘\0’ |
每个字符串的结尾,编译器会自动的添加一个结束标志位’\0’,即"a"包含两个字符’a’和’\0’。
#include<stdio.h>
int main()
{
char a = 'a';
const char * b = "hello\0world";
//%s是一个占位符,表示输出一个字符串,遇到\0停止
printf("%s\n",b);//hello
//%s 会根据*b(指针内存地址)在内存中一直找,直到找到\0才会停止
char c[4] = "123";//4
//这里数组的大小最小为4,因为默认有一个\0在结尾
printf("%s\n", c);
return 0;
}
printf函数
printf是输出一个字符串,
printf格式字符:
打印格式 | 对应数据类型 | 含义 |
---|---|---|
%d | int | 接受整数值并将它表示为有符号的十进制 |
%hd | short int | 短整数 |
%hu | unsigned short | 无符号短整数 |
%o | unsigned int | 无符号8进制整数 |
%u | unsigned int | 无符号10进制整数 |
%x,%X | unsigned int | 无符号16进制整数,x对应小写字母;X对应大写字母 |
%f | float | 单精度浮点数 |
%lf | double | 双精度浮点数 |
%e,%E | double | 科学计数法表示的数,此处“e”的大小写代表在输出时用的“e”的大小写 |
%c | char | 字符型。可以把输入的数字按照ASCII码相应转换为对应的字符 |
%s | char * | 字符串。输出字符串中的字符,直至遇到字符串中的空字符(‘\0’) |
%p | void * | 以16进制形式输出指针 |
%% | % | 输出一个百分号 |
printf附加格式
字符 | 含义 |
---|---|
l(字母l) | 附加在d,u,x,o前面,表示长整数 |
- | 左对齐 |
m(代表一个整数) | 数据最小宽度 |
0(数字0) | 将输出的前面补上0直到占满指定列宽为止,不可以搭配-使用 |
m.n(代表一个整数) | m指域宽,即对应的输出项在输出设备上所占的字符数。n指精度,用于说明输出的实型数的小数位。对数值型的来说,未指定n时,默认精度为n=6位 |
#include<stdio.h>
int main()
{
int a = 10;
printf("===%-5d===\n", a);//===10 === 左对齐,整体数据占5位
printf("===%5d===\n", a);//=== 10=== 右边对齐,整体数据占5位
printf("===%05d===\n", a);//===00010=== 在左侧用0补全;只能在左侧补全
int b = 123456;
printf("===%5d===", b);//===123456=== 输出位数大于5,所以正常输出
}
#include<stdio.h>
int main()
{
float a = 3.14;
//7为整体的位数,2为小数点位数
printf("==%7.2f==",a);//== 3.14== 3.14前有3个空格
printf("==%-7.2f==",a);//==3.14 == 加上-为左对齐
return 0;
}
putchar函数
putchar输出一个char
- putchar输出字符可以是变量、字符、数字(ASCII码内的数据)、转义字符。
scanf函数
- scanf通过%转义的方式可以得到用户通过标准输入设备输入的数据
#include<stdio.h>
int main()
{
char ch;
scanf_s("%c",&ch);
putchar(ch);
printf("\n");
int a, b;
scanf_s("%d,%d", &a,&b);//%d,%d ,是分隔符
scanf_s("%3d,%d", &a, &b);//a的长度为3
printf("%d\t%d\n",a,b);
return 0;
}
getchar函数
- getchar是从标准输入设备读取一个char
#include<stdio.h>
int main()
{
char ch;
//接收键盘获取字符
ch = getchar();
putchar(ch);//当输入多个字符时只会接受第一个字符
return 0;
}
3.运算符与表达式
常见运算符分类
运算符类型 | 作用 |
---|---|
算数运算符 | 用于四则运算 |
赋值运算符 | 用于将表达式的值赋值给变量 |
比较运算符 | 用于表达式的比较 |
逻辑运算符 | 用于根据表达式的值返回真值或假值 |
位运算 | 用于处理数据的位运算 |
sizeof运算符 | 用于字节数长度 |
算术运算符
运算符 | 术语 | 示例 | 结果 |
---|---|---|---|
+ | 正号 | +3 | + |
- | 负号 | -3 | -3 |
+ | 加 | 10+5 | 15 |
- | 减 | 10-5 | 5 |
* | 乘 | 10*5 | 50 |
/ | 除 | 10/5 | 2 |
% | 取模(取余) | 10%3 | 1 |
++ | 前自增 | a=2;b=++a; | a=3;b=3; |
++ | 后自增 | a=2;b=a++; | a=3;b=2; |
– | 前自减 | a=2;b=–a; | a=1;b=1; |
– | 后自减 | a=2;b=a–; | a=1;b=2; |
#include<stdio.h>
int main072407()
{
float a = 10.0;
int b = 7;
int c = 10;
printf("%d\n",c/b);//1
printf("%f\n", a / b);//1.428571
return 0;
}
int main072408()
{
int a = 10;
int b = 5;
int c = 3;
int d = 0;
printf("%d\n",a%b);//0
printf("%d\n", a % c);//1
//printf("%d\n",a%d);//报错;取余只能对整形操作
return 0;
}
#include<stdio.h>
int main()
{
int a = 10;
int b = 1 + a++;
printf("%d\n",b);//11
//前自增是在表达式之前进行++再进行表达式运算
//后自增先进行表达式计算再进行++操作
int c = 5;
int d = 1 + ++c;
printf("%d\n",d);//7
return 0;
}
赋值运算符
运算符 | 术语 | 示例 | 结果 |
---|---|---|---|
= | 赋值 | a=2;b=3; | a=2;b=3 |
+= | 加等于 | a=0;a+=2; | a=2; |
-= | 减等于 | a=5;a-=3; | a=2; |
*= | 乘等于 | a=2;a*=2; | a=4; |
/= | 除等于 | a=4;a/=2; | a=2; |
%= | 模等于 | a=3;a%=a; | a=1; |
#include<stdio.h>
void main()
{
int a = 5;
a %= 3;
printf("%d\n",a);//2
int b = 10;
a += b;
printf("%d\n",a);//12
int c = 6;
a -= c;
printf("%d\n",a);//6
int d = 2;
a /= d;
printf("%d\n",a);//3
}
比较运算符
C语言的比较运算中用“1”表示真;“0”表示假。
运算符 | 术语 | 示例 | 结果 |
---|---|---|---|
== | 相等于 | 4==3 | 0 |
!= | 不等于 | 4!=3 | 1 |
< | 小于 | 4<3 | 0 |
> | 大于 | 4>3 | 1 |
<= | 小于等于 | 4<=3 | 0 |
>= | 大于等于 | 4>=3 | 1 |
#include<stdio.h>
void main()
{
int a = 4;
printf("%d\n",a == 4);//1
printf("%d\n", a >= 3);//1
printf("%d\n", a <= 3);//0
printf("%d\n", a != 3);//1
}
#include<stdio.h>
void main()
{
int a = 10;
int b = 20;
int c = 10;
printf("%d\n",c = ++a <= b*2);//1
printf("a=%d\n", a);//10
printf("b=%d\n", b);//20
printf("c=%d\n", c);//1
}
逻辑运算符
运算符 | 术语 | 示例 | 结果 |
---|---|---|---|
! | 非 | !a | 如果a为假,则!a为真;如果a为真,则!a为假。 |
&& | 与 | a&&b | 如果a和b都为真,则结果为真,否则为假。 |
|| | 或 | a||b | 如果a和b有一个为真,则结果为真,二者有一个为假时,结果为假。 |
#include<stdio.h>
void main()
{
int a = 10;
printf("%d\n", ! a);//0
printf("%d\n", !!a);//1
int b = 20;
//
printf("%d\n",!a && b);//0
}
运算符的优先级
优先级1
运算符 | 名称 | 使用形式 |
---|---|---|
[] | 数组下标 | 数组名[常量表达式] |
() | 圆括号 | (表达式)/函数名(形参表) |
. | 成员选择(对象) | 对象.成员名 |
-> | 成员选择(指针) | 对象指针 -> 成员名 |
优先级2
运算符 | 名称 | 使用形式 |
---|---|---|
- | 负号运算符 | -表达式 |
~ | 按位取反运算符 | ~表达式 |
++ | 自增运算符 | ++变量名/变量名++ |
– | 自减运算符 | –变量名/变量名– |
* | 取值运算符 | *指针变量 |
& | 取地址运算符 | &变量名 |
! | 逻辑非运算符 | !表达式 |
(类型) | 强制类型转换 | (数据类型)表达式 |
sizeof | 长度运算符 | sizeof(表达式) |
优先级3
运算符 | 名称 | 使用形式 |
---|---|---|
/ | 除 | 表达式/表达式 |
* | 乘 | 表达式*表达式 |
% | 余数(取模) | 整形表达式%整形表达式 |
优先级4
运算符 | 名称 | 使用形式 |
---|---|---|
+ | 加 | 表达式+表达式 |
- | 减 | 表达式-表达式 |
优先级5
运算符 | 名称 | 使用形式 |
---|---|---|
<< | 左移 | 变量<<表达式 |
>> | 右移 | 变量>>表达式 |
优先级6
运算符 | 名称 | 使用形式 |
---|---|---|
> | 大于 | 表达式>表达式 |
>= | 大于等于 | 表达式>=表达式 |
< | 小于 | 表达式<表达式 |
<= | 小于等于 | 表达式<=表达式 |
优先级7
运算符 | 名称 | 使用形式 |
---|---|---|
== | 等于 | 表达式==表达式 |
!= | 不等于 | 表达式!=表达式 |
优先级8
运算符 | 名称 | 使用形式 |
---|---|---|
& | 按位与 | 表达式&表达式 |
优先级9
运算符 | 名称 | 使用形式 |
---|---|---|
^ | 按位异或 | 表达式^表达式 |
优先级10
运算符 | 名称 | 使用形式 |
---|---|---|
| | 按位或 | 表达式|表达式 |
优先级11
运算符 | 名称 | 使用形式 |
---|---|---|
&& | 逻辑与 | 表达式&&表达式 |
优先级12
运算符 | 名称 | 使用形式 |
---|---|---|
` | ` |
优先级13
运算符 | 名称 | 使用形式 |
---|---|---|
?: | 条件运算符 | 表达式1?表达式2:表达式3 |
优先级14
运算符 | 名称 | 使用形式 |
---|---|---|
= | 赋值运算符 | 变量=表达式 |
/= | 除后赋值 | 变量/=表达式 |
*= | 乘后赋值 | 变量*=表达式 |
%= | 取模后赋值 | 变量%=表达式 |
+= | 加后赋值 | 变量+=表达式 |
-= | 减后赋值 | 变量-=表达式 |
<<= | 左移后赋值 | 变量<<=表达式 |
>>= | 右移后赋值 | 变量>>=表达式 |
&= | 按位与后赋值 | 变量&=表达式 |
^= | 按位异或后赋值 | 变量^=表达式 |
|= | 按位或后赋值 | 变量|=表达式 |
优先级15
运算符 | 名称 | 使用形式 |
---|---|---|
, | 逗号运算符 | 表达式,表达式… |
#include<stdio.h>
void main()
{
int a = 10;
int b = 20;
int c = (a,b);
printf("%d\n",c);//20
}
类型转换
数据有不同的类型,不同的数据类型之间进行混合运算时必然涉及到类型的转换问题。
转换的方法有两种:
- 自定转换(隐式转换):遵循一定的规则,由编译系统自动完成。
- 强制类型转换:把表达式的运行结果强制转换成所需要的数据类型
类型转换的原则:占用内存字节数少(值域小)的类型,向占用内存字节数多(值域大)的类型转换,以保证精度不降低。
#include<stdio.h>
void main()
{
//隐式转换
int num = 5;
printf("%d\n",num/2);//2
printf("%lf\n", num/2.0);//2.500000
//强制转换;不会四舍五入,而是去尾
float p = 3.14;
float m = p * 2;
int n = (int)p * 2;
printf("%.2f\n",m);//6.28
printf("%d\n", n);//6
}
#include<stdio.h>
void main()
{
float p = 3.567;
int w = 2;
int sum = (int)(p * w);
int sum2 = (int)p * w;
printf("%d\n",sum);//7
printf("%d\n", sum2);//6
}
4.程序流程结构
C语言支持最基本的三种程序运行结构:顺序结构、选择结构、循环结构。
- 顺序结构:程序按顺序执行,不发生跳转。
- 选择结构:依据是否满足条件,有选择的执行相应功能。
- 循环结构:依据条件是否满足,循环多次执行某段代码。
if语句
#include<stdio.h>
int main()
{
int score;
scanf_s("%d",&score);
if (score > 700) {
printf("上清华北大了兄弟\n");
if (score > 730) {
printf("专业随便选!");
}
else if (score >720 ) {
printf("选个考古学专业没问题");
}
}
else if (score >= 600) {
printf("上个双一流吧");
}
else {
printf("再接再厉吧");
}
return 0;
}
三只小猪?
#include<stdio.h>
int main()
{
int a, b, c;
printf("请输入三只小猪的体重,用空格间隔:\n");
scanf_s("%d%d%d",&a,&b,&c);
printf("a猪体重为%d\n", a);
printf("b猪体重为%d\n", b);
printf("c猪体重为%d\n", c);
if (a>b) {
if (a>c) {
printf("a猪体重最大");
}
else {
printf("c猪体重最大");
}
}
else{
if (b > c) {
printf("b猪体重最大");
}
else {
printf("c猪体重最大");
}
}
return 0;
}
三目运算符
a?b:c;
#include<stdio.h>
int main()
{
int a = 10;
int b = 20;
if (a>b) {
printf("a大\n");
}
else if(b>a){
printf("b大\n");
}
else {
printf("ab一样大\n");
}
//三目
printf("%d大\n", a > b ? a : b);
return 0;
}
#include<stdio.h>
#define MAX(a,b) (a)>(b)?(a):(b)
int main()
{
int a = 10;
int b = 20;
printf("%d\n", MAX(a, b));//20
printf("%d\n",a>b?a:b);//20
printf("%d\n", (a) > (b) ? (a) : (b));//20
return 0;
}
switch语句
#include<stdio.h>
int main()
{
int score;
scanf_s("%d",&score);
switch (score) {
case 90:
printf("优秀的小伙子");
break;
case 80:
printf("良好啦");
break;
case 70:
printf("中等哦");
case 60:
printf("及格吧");
}
return 0;
}
while语句
#include<stdio.h>
int main()
{
int i = 0;
while(i<3){
printf("%d\n",i);
i++;
}
return 0;
}
#include<stdio.h>
int main()
{
short a = 0;
do {
printf("%d\n",a);
a++;
} while (a);
return 0;
}
dowhile语句
#include<stdio.h>
int main()
{
int i = 0;
do{
printf("%d\n",i);
i++
}while(i<10)
}
经典水仙花数
#include<stdio.h>
int main() {
int a = 100;
while (a < 999) {
int g = a % 10;
int s = a / 10 % 10;
int b = a / 100 % 10;
if (((g * g * g) + (s * s * s) + (b * b * b)) == a) {
printf("%d\n",a);
}
++a;
}
return 0;
}
数学函数
#include<math.h>
库函数 | 功能说明 | 示例 |
---|---|---|
abs(x) | 求整数 x 的绝对值 | abs(-5)=5 |
fabs(x) | 求实数 x 的绝对值 | fabs(-3.14)=3.14 |
floor(x) | 求不大于 x 的最大整数(下舍入) | floor(3.14)=3.000000 |
ceil(x) | 求不小于 x 的最小整数(上舍入) | ceil(3.14)=4.000000 |
log(x) | 求 x 的自然对数 | log(2)=0.693147 |
exp(x) | 求 x 的自然指数(ex) | exp(2)=7.389056 |
pow(x,y) | 计算 x的y次方 的值 | pow(2,5)=32.000000 |
rand( ) | 产生 0~RAND_MAX 的随机整数 | rand( )%900+100 生成三位随机整数 |
sqrt(x) | 求 x 的平方根(![]() | sqrt(36)=6.000000 |
使用数学函数求水仙花数
#include<stdio.h>
#include<math.h>
int main() {
int a = 100;
while (a < 999) {
int g = a % 10;
int s = a / 10 % 10;
int b = a / 100 % 10;
if ((pow(g, 3) + pow(s, 3) + pow(b, 3)) == a) {
printf("%d\n", a);
}
++a;
}
return 0;
}
for语句
c89库写法(当前Linux环境)
#include<stdio.h>
int main()
{
int i;
for (i = 0; i < 3;i++) {
printf("%d\n",i);
}
}
c99库
或者编译时加上…
gcc -o hello.out hello.c -std=c99
#include<stdio.h>
int main()
{
for (int i = 0; i < 3;i++) {
printf("%d\n",i);
}
}
经典猜数字游戏
产生随机数的步骤
- 导入头文件
#include<time.h>、#include<stdlib.h>
- 添加随机数种子
- 获取随机数
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
int main()
{
//time()用于获取当前时间、srand()初始化随机数种子,rand()获取随机数
srand((unsigned int)time(NULL));//随机数种子,确保每次随机数不一样
int value = rand() % 100;
int num;
int count = 0;
printf("请输入一个0-100的数字:\n");
for (;;) {
scanf_s("%d",&num);
count++;
if (num<value) {
printf("你猜的数字太小了!再猜一次吧:\n");
}
else if (num > value) {
printf("你猜的数字太大了!再猜一次吧:\n");
}
else {
printf("恭喜你仅用了%d次就猜对了!!!",count);
break;
}
}
return 0;
}
嵌套循环
双层循环时外层循环执行一次,内层循环执行一个“周期”
#include<stdio.h>
int main() {
//嵌套循环就是
for (int i = 0; i < 5;i++) {
for (int j = 0; j < 5;j++) {
printf("i=%d,j=%d\n",i,j);
}
}
return 0;
}
三层嵌套实现秒表
#include<stdio.h>
#include <windows.h>
#include<stdlib.h>
int main()
{
for (int i = 0; i < 24;i++) {
for (int j = 0; j < 60;j++) {
for (int k = 0; k < 60;k++) {
system("cls");
printf("%02d:%02d:%02d",i,j,k);
Sleep(985);
}
}
}
}
9*9乘法口诀
#include<stdio.h>
int main()
{
//外层执行一次,内层执行一"周期"
/* 1*1=0
1*2=4 2*2=4
1*3=3 2*3=6 3*3=9
*/
for (int i = 1; i <= 9;i++) {
for (int j = 1; j <= i;j++) {
printf("%d*%d=%d \t",j,i,i*j);
}
printf("\n");
}
return 0;
}
跳转语句
break语句
在switch条件语句和循环语句中都可以使用break语句
- 当它出现在switch语句中时,作用是终止某个case并跳出switch结构
- 当他出现在循环语句中,作用是跳出当前内循环语句,执行后面的代码(外层循环)
- 当他出现在嵌套循环语句中,跳出最近的内循环语句,执行后面的代码
continue语句
在循环语句中,如果希望立即终止本次循环,并执行下一次循环,此时需要使用continue语句
goto语句
无条件跳转语句,可以在各个函数、循环、程序中任意位置进行跳转。
#include<stdio.h>
int main()
{
printf("hello world01\n");
printf("hello world02\n");
goto FLAG;
printf("hello world03\n");
printf("hello world04\n");
printf("hello world05\n");
FLAG:
printf("hello world06\n");
return 0;
}
输出结果
hello world01
hello world02
hello world06
#include<stdio.h>
int main()
{
FLAG:
printf("死循环");
goto FLAG;
return 0;
}
数组
在程序设计中,为了方便处理数据,把具有相同类型的若干变量按有序形式组织起来——称为数组。
数组就是在内存中连续的相同类型的变量空间。同一个数组的所有成员都是相同的数据类型,同时所有的成员在内存中的地址是连续的。
数组属于构造数据类型
数组的定义
#include<stdio.h>
int main()
{
//变量的定义
//数据类型 变量 = 值
//
//数组的定义
//数据类型 数组[元素个数] = {值1,值2,...}
int array[6] = { 1,2,3,4,5 ,6};
printf("%d\n", array[0]);//1
return 0;
}
数组在内存中的存储方式及大小
#include<stdio.h>
int main()
{
int arr[5] = { 5,4,3,2,1 };
//下标为0的元素地址(每次显示的都是首地址)
printf("%d\n", &arr[0]);//7338948
//下标为1的元素地址
printf("%d\n", &arr[1]);//7338952
//数组名不可以被赋值,他是一个指向数组首地址的常量
//arr = 100;
printf("%d\n", arr); //7338948
printf("%d\n",sizeof(arr));//20字节 =(数据类型*元素个数)
return 0;
}
遍历数组
#include<stdio.h>
int main() {
int arr[] = { 5,4,3,2,1,6,9,8,7 };
for (int i = 0; i < (sizeof(arr) / sizeof(arr[0]));i++) {
printf("%d\n",arr[i]);
}
return 0;
}
//
十只小猪称体重
#include<stdio.h>
int main()
{
int arr[10];
printf("请输入十只小猪的体重:\n");
for (int i = 0; i < 10;i++) {
scanf_s("%d", &arr[i]);
}
int max = arr[0];
for (int j = 1; j < sizeof(arr) / sizeof(arr[0]);j++) {
if (arr[j] > max) {
max = arr[j];
}
}
printf("最重的猪体重是%d\n",max);
}
数组逆置
#include<stdio.h>
int main()
{
//数组逆置
int arr[11] = {1,2,3,4,5,6,7,8,9,10,11};
int i = 0;//数组起始下标
int j = sizeof(arr) / sizeof(arr[0]) - 1;//数组结尾下标
while (i < j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
i++;
j--;
}
for (int i = 0; i < sizeof(arr) / sizeof(arr[0]);i++) {
printf("%d\n",arr[i]);
}
return 0;
}
冒泡排序
排序:数据从无序数据到有序数据的过程
#include<stdio.h>
int main()
{
int arr[] = {5,2,4,3,6,1,0,9,8,7};
for (int i = 0; i < sizeof(arr) / sizeof(arr[0]) - 1;i++) {
for (int j = 0; j < sizeof(arr) / sizeof(arr[0]) - 1 - i;j++) {
if (arr[j] > arr[j+1]) {
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
for (int i = 0; i < (sizeof(arr) / sizeof(arr[0])); i++) {
printf("%d\n", arr[i]);
}
return 0;
}
二维数组
二维数组的定义形式
//类型说明符 数组名[常量表达式1][常量表达式2]
//其中 常量表达式1 表示第一维下标的长度,常量表达式2表示第二维下标的长度
int a[3][4];//定义了一个三行四列的数组,数组名为a其元素类型为整形,该数组的元素个数为3*4个
#include<stdio.h>
int main()
{
int arr[2][3] =
{
{1,2,3},
{4,5,6}
};
printf("%d\n",arr[0][2]);//3
return 0;
}
二维数组的遍历
#include<stdio.h>
int main()
{
int arr[3][3] =
{
{1,2,3},
{4,5,6},
{7,8,9}
};
int h = sizeof(arr) / sizeof(arr[0]);//得到行
int l = sizeof(arr[0]) / sizeof(arr[0][0]);//得到列
for (int i = 0; i < h; i++) {
for (int j = 0; j < l; j++) {
printf("%d ", arr[i][j]);
}
printf("\n");
}
return 0;
}
二维数组的地址
#include<stdio.h>
int main()
{
int arr[2][3] =
{
{1,2,3},
{4,5,6}
};
//二维数组首地址
printf("%p\n",arr);//004FFC20
//二维数组第一行首地址
printf("%p\n", arr[0]);//004FFC20
//二维数组第一行的第一个元素的地址
printf("%p\n", arr[0][0]);//00000001
//二维数组第二行首地址
printf("%p\n", arr[1]);//004FFC2C
/*
通过观察发现二维数组的首地址与第一行的首地址一样
第一行首地址与第二行之间间隔了12子节 3*4=12
由此可知数组存储的元素他们的地址都是连续的
*/
return 0;
学生成绩的练习
#include<stdio.h>
int main() {
/*
定义一个数组,存储5名学生3门成绩
求出每名学生的总成绩、平均成绩
求出每门学科的总成绩、平均成绩
*/
int arr[5][3];
//获取学生成绩
for (int i = 0; i < sizeof(arr) / sizeof(arr[0]);i++) {
for (int j = 0; j < sizeof(arr[0]) / sizeof(arr[0][0]);j++) {
scanf_s("%d",&arr[i][j]);
}
}
//打印学生成绩
for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++) {
//每次执行外层循环时会将sum置0
int sum = 0;
for (int j = 0; j < sizeof(arr[0]) / sizeof(arr[0][0]); j++) {
sum += arr[i][j];
}
printf("第%d名学生的总成绩为:%d\t平均成绩为:%d\n ",i+1,sum,sum/3);
}
return 0;
}
多维数组
多维数组的定义与二维数组类似
数组类型修饰符 数组名 [n1][n2][n3]...
int a[3][4][5];
定义了一个三维数组,数组的名字是a,数组的长度是3,每个数组的元素又是一个二维数组,这个二维数组的长度是4,并且这个二维数组中的每个元素又是一个一维数组,这个一维数组的长度是5,元素类型是int
#include<stdio.h>
int main()
{
int arr[3][4][5] = //3层5行5列
{
{
{1,2,3,4,5},
{2,3,4,5,6},
{3,4,5,6,7},
{4,5,6,7,8},
},
{
{5,6,7,8,9},
{6,7,8,9,10},
{7,8,9,10,11},
{8,9,10,11,12},
},
{
{0,1,2,3,4},
{1,2,5,8,9},
{1,4,7,8,5},
{3,6,9,8,2},
}
};
for (int i = 0; i < 3;i++) {
for(int j = 0; j < 4;j++) {
for (int k = 0; k < 5;k++) {
printf("%d\t",arr[i][j][k]);
}
printf("\n");
}
}
printf("三维数组大小:%d\n", sizeof(arr));//240 = 3*4*5*4字节
printf("三维数组一层大小:%d\n", sizeof(arr[0]));//80 = 4*5*4子节
printf("三维数组一行大小:%d\n", sizeof(arr[0][0]));//20 = 5*4子节
printf("三维数组元素大小:%d\n",sizeof(arr[0][0][0]));//4子节
printf("层小:%d\n", sizeof(arr)/sizeof(arr[0]));//3
printf("行:%d\n", sizeof(arr[0])/sizeof(arr[0][0]));//4
printf("列:%d\n", sizeof(arr[0][0])/sizeof(arr[0][0][0]));//5
return 0;
}
字符串
#include<stdio.h>
int main()
{
//定义一个字符数组
char arr[5] = {'h','e','l','l','o'};//一个char类型占1子节
//定义一个字符
char c = 'a';
printf("%d\n",sizeof(arr));//5 = 5*1子节
for (int i = 0; i < sizeof(arr) / sizeof(arr[0]);i++) {
printf("%c",arr[i]);//hello
}
return 0;
}
字符数组与字符串有什么区别?
#include<stdio.h>
int main()
{
//定义一个字符数串
const char* str = "hello";
//她两是一样的
char str2[6] = { 'h','e','l','l','o','\0'};
char str3[6] = { 'h','e','l','l','o',0 };
printf("%s\n", str2);//hello
printf("%s\n", str3);//hello
//定义一个字符数组
char arr[5] = { 'h','e','l','l','o' };
//他们有什么区别呢?
printf("%s\n",str);//hello
for (int i = 0; i < sizeof(str3) / sizeof(str3[0]); i++) {
printf("%c", str3[i]);//hello
}
//首先他们都是一组字符的集合
// 字符的打印格式为%c;字符串的打印格式为%s
//字符串结束标志为\0,也就是说字符串可以写成字符数组的格式,但是一定要有\0作为结束标志
//也就是说字符串就是字符数组的一个特例(有\0结尾)
return 0;
}
字符数组与字符串的区别
-
C语言中并没有字符串这种数据类型,可以通过char的数组来替代
-
字符串一定是一个char的数组,但char的数组未必是字符串
-
数字0(等价\0)结尾的char数组就是一个字符串,但如果char数组没有以数字0结尾,那么就不是一个字符串,只是普通的字符数组,所以字符串是一种特殊的char数组
字符串的获取
scanf_s()
//#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
//定义字符数组,获取字符串
char ch[10];
scanf_s("%s",ch,sizeof(ch));//char本身就是一个地址,所以不用&取地址符
printf("%s",ch);
return 0;
}
gets_s()
#include<stdio.h>
int main()
{
char ch[100];
//通过键盘获取一个字符串
gets_s(ch);//可以带空格,
printf("%s\n",ch);
scanf_s("%[^\n]",ch,sizeof(ch));//正则表达式,接受非\n的所有内容
printf("%s\n", ch);
return 0;
}
fgets()
fgets()
在读取一个用户通过键盘输入的字符串的时候,同时把用户输入的回车也作为字符串的一部分。通过scanf
和gets
输入一个字符串的时候,不包含结尾的“\n”,但通过fgets()
结尾多了“\n”。fgets()
函数是安全的不存在缓冲区溢出的问题。
#include<stdio.h>
int main()
{
char ch[10];
fgets(ch,sizeof(ch),stdin);//可以接受空格
printf("%s",ch);
printf("%d",sizeof(ch));//10
return 0;
}
字符串的打印
puts()
#include<stdio.h>
int main()
{
const char * s = "hello";
char ch[] = "66\06";
puts(s);//在输出完成之后会自动加一个/n
puts(ch);//66[]
return 0;
}
fputs()
#include<stdio.h>
int main()
{
char ch[] = "hello world";
fputs(ch,stdout);//不会自动加\n
printf("\n");
printf("%s\n",ch);
return 0;
}
字符串长度获取
strlen
- 计算指定字符串的长度(第一个\0之前的有效字符串),不包括字符结束符\n
- 需要头文件
#include<string>
#include<stdio.h>
#include<string>
int main()
{
char ch[100] = "hello world";
//计算字符串有效个数
printf("%d\n",strlen(ch));//11
//计算数组大小
printf("%d\n", sizeof(ch));//100
}
函数
函数的分类
C程序是由函数组成的,我们写的代码都是由主函数main()开始执行的。函数是C语言中的基本模块,是用于完成特定任务的程序代码单元。
从函数定义的角度看,函数可以分为系统函数和用户定义函数两种:
- 系统函数,即库函数。这是由编译系统提供的,用户不必自己定义这些函数,可以直接使用他们,如
printf()
函数 - 用户自定义函数:用以解决用户的各种需求
函数的作用
省去重复代码的编写,降低代码重复率
函数的调用
在调用函数时,需要关心5要素
- 头文件:包含指定的头文件
- 函数名字:函数名字必须和头文件声明的名字一样
- 功能:需要知道此函数能干嘛后才调用
- 参数:参数类型要匹配
- 返回值:根据需要接收返回值
例:
#include<stdio.h>
//函数定义
int add(int a, int b) {
return a + b;
};
int main()
{
int a = 20;
int b = 30;
//函数调用
int c = add(a,b);
printf("a+b= %d\n",c);
return 0;
}
声明和定义的区别
- 声明不需要建立存储空间,如
extern int a;
- 定义变量需要建立存储空间,如
int b;
从广义的角度来讲声明中包含着定义,即定义是声明的一个特例,所以并非所有的声明都是定义
- int b;它既是声明,同时又是定义
- 对于extern b 来说他只是声明不是定义
一般情况下,把建立存储空间的声明称之为“定义”,而把不需要建立存储空间的声明称之为“声明”。
多文件编程
function.c
int max(int a, int b) {
return a > b ? a : b;
}
head.h
#pragma once//防止头文件重复包含
//全局变量的定义
//extern int max(int a,int b);
int max(int a, int b);
main.c
#include<stdio.h>
#include "head.h"
int main()
{
int a = 90;
int b = 20;
printf("%d\n", max(a,b));
return 0;
}
指针
内存
- 存储器:计算机的组成部分之一,用来存储程序和数据,辅助cpu进行运算处理。(包括RAM、ROM)
- 内存:内部存储器,暂存数据/运行起来的程序——断电即失。SRAM、DRAM、DDR(双倍速率同步动态随机存储器)系列
- 外存:外部存储器,长时间保存程序/数据,断电不丢失。ROM、EROM、FLASH、硬盘、光盘。
内存是cpu
与硬盘沟通的桥梁
- 暂时存放cpu中的运算数据
- 暂时存放与硬盘等外部存储器交换的数据
物理存储器和存储地址空间
物理存储器:实际存在的具体存储器芯片
- 主板上插装的内存条
- 显卡上的显示RAM芯片
存储地址空间:存储器编码的空间范围(软件上说的内存地址空间)。
- 编码:对每个物理存储单元(一个字节)分配一个号码
- 寻址:可以根据分配的号码找到相应的存储单元,完成数据的读写
内存地址
- 将内存抽象成一个很大的一维字符数组
- 编码就是对内存的每一个子节分配一个32位或64位的编号(与32位或者62位处理器相关)
- 内存编号就是常说的内存地址
内存中的每一个数据都会分配相应的地址:
- char :占一个子节分配一个地址
- int : 占4个子节分配4个地址
- float、struct、函数、数组等
#include<stdio.h>
int main()
{
int a = 10;
//int* 是指针数据类型(在基本数据类型后加上*)
//定义指针类型的变量存储变量地址
int* p;
p = &a;//取a对应的地址赋值给p
//获取a的内存地址
printf("%p\n", &a);//00EFFA54
//windows系统在做数据存储时采用的是小端对齐(低位数据存在低位地址中)
printf("%p\n",p);//00EFFA54
//在32位平台下,所有指针变量指向存储地址的大小都是32位(4子节)
//在64位平台下,所有指针变量指向存储地址的大小都是64位(8子节)
printf("%d\n", sizeof(int*));
printf("%d\n", sizeof(short*));
printf("%d\n", sizeof(char*));
printf("%d\n", sizeof(long*));
printf("%d\n", sizeof(long long*));
printf("%d\n",sizeof(float*));
printf("%d\n", sizeof(double*));
return 0;
}