中央处理器(CPU):
包括运算器、控制器、寄存器
Enum:
Enum: 枚举类型
System函数:
执行系统命令。如pause、cmd、calc、mspaint、notepad.....
System(“pause”);//暂停
System(“calc”);//打开计算机
System(“cls”);//清屏命令
System(“mspaint”);//打开画板
程序详细编译过程
- 预处理:
-E 生成 xxx.i 预处理文件
gcc -E xxx.c -o xxx.i
- .头文件展开----不检查语法错误,可以展开任意文件
- 宏定义替换-----将宏名替换为宏值
- 替换注释 ------变成空
- 展开条件编译---根据条件展开指令;
- 编译:
-S 生成 xxx.s 汇编文件
gcc -s xxx.i -o xxx.s
- 逐行检查语法错误。【重点】。---整个编译4步骤中最耗时的过程
- 将C程序编译成 汇编指令,得到.s 汇编文件
- 汇编:
-c 生成 xxx.o 目标文件
gcc -c xxx.s -o xxx.o
- 将汇编指令翻译成对应的二进制编码。
- 链接:
无 生成 xxx.exe 可执行文件
gcc xxx.o -o xxx.exe
- 数据段合并
- 数据地址回填
- 库引入
调试程序:
- 设置断点,F5启动该调试;
- 停止的位置,是尚未执行的指令
- 逐语句执行下一条(F11) :进函数内部,逐条执行跟踪。
- 逐过程执行下一条(F10) :不进函数内部,逐条执行程序。
- 添加监视:
调试-->窗口--》;输入监视变量名。自动监视变量值的变化
常量与变量
常量:不会变化的数据
- “hello”、’A’、-10、3.1415926(浮点常量)
- #define PI 3.1415926 定义宏 语法:#define 宏名 宏值。
- Int a=10; const int a = 10; const 关键字: 该关键字修饰常量,表示只读
变量:会变化的数据
定义语法:类型名 变量名 = 变量值;(一般方法)
变量三要素: 类型名、变量名、变量值 //int r = 3;
变量的定义:int a = 40;
变量的声明:in a; 没有变量值的变量定义 叫做声明。
extern int a; 添加关键字 extern
- 变量定义会开辟空间,变量的声明不会开辟空间;
- 当编译器编译程序时,在变量使用之前,如果没有看到变量定义,编译器会自动找寻一个变量声明提升成为定义,如果该变量的声明前有extern 关键字,则无法提升。
定义变量时,尽量不要重名。
标识符
标识符: 变量和常量的统称;
命名规则: 1.通常常量用大写,变量使用小写,大小写严格分明
2.只能使用字母、数字、下划线(-)命名标识符,且数字不能开头 a-z/A-Z/0-9/_
数据类型
整型:
Int 类型: 4字节 打印格式:%d
Int 名 = 值;
float 类型 4字节
float 名 = 值;
short 类型: 2字节 打印格式:%hd
short 名 = 值;
long 类型: 4字节
long 名 = 值; 打印格式:%ld
long long 类型: 8字节
long long 名 = 值;打印格式:%lld
sizeof 关键字
sizeof 关键字 不是函数, 作用:求变量/类型的大小 返回一个无符号整数,使用%u接收返回值
用法:(1).sizeof(类型名) //sizeof(int);
- .sizeof(变量名) //sizeof(a);
无符号整型:
unsigned 无符号 只有数据量,没有方向(没有正负)
unsigned Int 类型: 4字节
unsigned Int 名 = 值; 打印格式:%u
unsigned short 类型: 2字节
unsigned short 名 = 值;
unsigned long 类型: 4字节
unsigned long 名 = 值; 打印格式:%lu
unsigned long long 类型: 8字节
unsigned long long 名 = 值; 打印格式:%llu
char
字符类型:一字节 本质是ASCII码
存储一个字符 ‘A’、’a’、’#’、’65’
char 类型:一字节 8个bit位,数值位有7个。
有符号:-2^7--- 2^7-1== -2^(8-1) -- 2^(8-1)-1
--> -128 -- 127
无符号:0~2^8-1
-->0~65535
不要超出该数据类型的存储范围;
Short 类型:
2字节16 bit
有符号:-2^15 --- 2^15-1 == -2^(16-1) -- 2(16-1)-1
-->-32768~32767
Int 类型:
4字节 -2^(32-1) -- 2^(32-1)-1
有符号:
-->-2147483648~2147483647
无符号: 0~2^32-1
-->0~4294967295
long 类型
4字节
有符号:
--> -2147483648~2147483647
无符号: 0~2^32-1
-->0~4294967295
longlong类型:
8字节
有符号:
-->2^(63)~ 2^(63)-1
无符号:
-->0 - 2^63-1
转移字符:
printf(“n=%08.3f\n”,n);//输出含义为:显示8位数(包含小数点),不足8位用0填充。并且保留3位小数。对第4位做四舍五入。
进制和转换:
10进制转2进制: ---除2取余法
10进制转8进制 ---除8反向取余
10进制转16进制 ---除16反向取余
Int a = 56; ----111000
2进制转10进制
2^10=1024
2^9 =512
2^8 =256
2^7 =128
2^6 =64
2^5 =32
2^4 =16
2^3 =8
2^2 =4
2^1 =2
8进制转10进制
056, 0开头,每位数0~7之间
0 5 6
2 1 0
0*8^2+5*8^1+6*8^0=46 --》46 再转二进制
八进制转二进制
按4 2 1 码将每个八进制位展开
4 2 1
5 1 0 1
056 -->101110
6 1 1 0
二进制转八进制:
4 2 1逆运用 从后往前分为三位一组 高位不足三位补0
101 100 110 001--> 5461
5 4 6 1
16进制转10进制:
语法:以0x开头,每位 取0-9/A-F/a-f
- -10
- -11
- -12
- -13
- -14
- -15
Eg:0x1A--> 1-->1*16^1
- ->10*16^0 1A-->26
0x13F 1*16^2
3*16^1
15*16^0 13F-->
16进制--转2进制
8 4 2 1 运用
Eg: 0x1A--> 1: 0001 A: 1010 -->00011010
0X13F 1: 0001 3:0011 F: 1111 -->000100111111
2进制转8进制
方法: 4 2 1 逆运用 从后往前分成四个一组,高位不足4位补0。
不能给变量直接赋值二进制;
Printf输出格式:
%d:有符号整型
%u:无符号整型
%x:输出十六进制
%c:输出字符
%s:输出字符串
存储知识:
1bit位 就是一个 二进制位
一个字节 1B = 8bit位
1KB = 1024B
1MB = 1024KB
1GB = 1024MB
1TB = 1024GB
源码 反码 补码:
源码:
43 -->00101011
-43 -->10101011
反码:
43 -->00101011
-43 -->10101011
11010100
补码(现今计算机采用的存储方式)
43 -->00101011 :正数不变
-43 -->11010101 :负数,最高位表符号位,其余取反+1;
人为规定:0000000 ——》 0
10000000 ——》 -128
特殊修饰符:
Extrn:
表示声明,没有内存空间,不能提升。
Const:
限定一个变量为只读变量
Volatile:
防止编译器优化代码
Volatile int fig = 0;
Register
定义一个寄存器变量
字符串:
c语言中,用双引号引着的一串字符,称之为字符串,一定有一个结束标记”\0”
Char ch = ‘A’;一个字符
“abc” --->’a’ ‘b’ ‘c’ ‘\0’
printf();函数 -->%s 挨着从字符串的第一个字符开始打印,打印到’\0’结束
‘a’ 不等价 ”a” (‘a’ ’\0’)
--》%m.n : 打印实型,一共有m 位(整数、小数、小数点),n位小数;
--》%0m.n :打印实型,一共有m位,不够的前面添0,保留n位小数;
%% : 输出一个 % 转义字符 ’\’ 对 ‘%’ 无效;
%Ns : 显示N个字符的字符串。不足N 用空格向左填充,
%0Ns : 显示N个字符的字符串。不足N 用0向左填充,
%-Ns : 显示N个字符的字符串。不足N 用空格向右填充;
putchar函数:
输出一个字符到屏幕;
直接使用ASCII码
不能输出字符串
‘abc’ 既不是一个有效字符。也不是一个有效字符串。
Scanf(); 函数
从键盘接收用户输入
- 接收 整数 %d
Int a,b,c; //创建变量空间等待接受用户输入
Scanf(“%d %d %c\n”,&a,&b,&c);
- 接收字符 %c
Char a, b, c;
Scanf(“%c %c %c”,&a, &b,&c);
- 接收字符串 %s
char str [10]; //定义一个数组,用来接收用户输入的 字符串。
scanf(“%s”, str); //变量名要取地址符传递给scanf,数组名本身表示地址,不用&符。接收字符串:
- .具有安全隐患。如果储存空间不足,数据能存储到内存中,但不能被保护【空间不足不能使用】
- . scanf函数接收字符串时,碰到 空格和换行会自动终止,不能使用scanf接收带有空格的字符串
getchar函数
函数从标准输入里读取下一个字符,返回值为int类型
直接使用ASCII码
出错直接返回EOF;
算数运算符
不允许对小数、0 取余,对负数取余,结果为余数的绝对值 。
前缀自增:++a // a=a+1;先自增,后取值;
后缀自增:a++ // 先取值,后自增;
逻辑运算符:
与:&&
或:||
非: !
三目运算符:
?: 表达式1 ? 表达式2 :表达式3
a>b? max=a : max=b;
if与switch
If 分支语句,匹配一个范围,属于模糊匹配
Switch分支: 精确匹配
循环结构:
Break;//跳出一重循环 for , while , do while;
// switch 语句,防止case穿透;
Continue:作用:结束当前(本次)循环,continue之后的循环体在本次不执行;
go to [了解]
用法:1)。先定义一个标签
2)。go to 标签 跳转到标签处 (只在函数内部生效)
数组
数组:相同数据类型的有序结合。
Int ar[10]={1,2,3,4,5,6,7,8,9,10};
各个元素的内存地址 连续。
数组名为地址,是数组首元素的地址 arr=&arr[0];
数组的大小:printf(“数组大小:%u\n”,sizeof(arr));
Printf(“数组元素的大小:%u\n”,sizeof(arr[0]));
Printf(“数组元素个数:%u\n”,sizeof(arr)/sizeof(arr[0]));
Int arr[10]={1,2,3,4,5}; 剩余未初始化的元素,默认 0 值。
Int arr[10]={0}; 初始化一个全为0 的数组
Int arr[ ]= {1,2,3,4}; 编译器自动求取元素个数
Int arr[ ]={0}; 只有一个元素0;
Int arr[10];
arr[0]=5;
arr[1]=6;
arr[2]=7;//其余 元素未被初始化, 默认值 随机数
二维数组:
Int arr[3][2] ={{1,2},{3,4},{5,6}};
等价于
Int arr[3][2] ={
{1,2},
{3,4},
{5,6}
};
二维数组大小:sizeof(arr);
二维数组第一行大小:sizeof(arr[0]);
二维数组一个元素大小:sizeof(arr[0][0]);
二维数组行数:sizeof(arr)/sizeof(arr[0]);
二维数组列数:sizeof(arr[0])/sizeof(arr[0][0]);
地址合一:数组的首地址==数组的首元素地址== 数组的首行元素地址;
Printf(“%p\n”,arr); == printf(“%p\n”,&arr[0][0]); == printf(“%p\n”,arr[0]);
字符串 char str[6]=”hello”;//需要给\0 留空间;
printf(“%s”);//输出字符串,必须碰到\0结束;
字符串获取scanf:
注意:
- 用于存储字符串的空间必须足够大,防止溢出;
- 获取字符串,%s, 遇到空格 和 \n 终止;
借助”正则表达式”可以获取带有空格的表达式 scanf(“%[^\n]”,str);
字符串操作函数:
gets:
获取一个字符串,返回字符串的首地址,可以获取带有 空格的字符串。
char *gets(char *s);
参数:用来存储字符串的空间。
返回值:返回实际获取到的字符串的首地址。
fgets:
获取一个字符串 预留 \0 的存储空间
char *fgets(char *s, int size, FILE *stream);
参1:用来存储字符串的空间大小;
参2:描述空间大小;
参3:读取字符串的位置,键盘--》标准输入:stdin
返回值:返回实际获取到的字符串的首地址。
puts:
输出字符串后会自动添加 \n;
Int puts(const char *s);
参1:待写出到屏幕的字符串。
返回值:成功:非负数;失败:-1;
fputs:
将一个字符串写到屏幕stdout,输出字符串后不添加 \n;
Int fputs(const char *str, FILE * stream );
参1:待写出到屏幕的字符串。 屏幕--》标准输出:stdout
参数:写出位置 stdout
返回值:成功:非负数;失败:-1;
strlen:
返回字符串的有效长度,不包括\0; 碰到\0结束;
size_t strlen(const char * s);
参1:待求长度的字符串
返回:有效的字符个数。
函数
函数的作用:
- 提高代码的复用性
- 提高程序模块化组织性。
函数分类:
系统库函数:标准C 库libc
- 引入头文件---声明函数
- 根据函数原型调用。
用户自定义
除了需要提供函数原型,还要提供函数实现
随机数:
- 播种随机数种子:srand(time(NULL));
- 引入头文件#include<stdio.h> <time.h>
- 生成随机数: rand() % 100;
函数定义:
包括 函数原型(返回值类型、函数名、形参列表)和 函数体(大括号一对,具体代码实现)一定包含类型名 形参名;
Int add(int a,intb,int c)
{return a+b+c;}
函数调用:
包括 函数名(实参列表);
Int ret = add(10,20,30);
实参(实际参数):在调用时,传参必须严格按照形参填充。(参数的个数、类型、顺序) 没有类型描述符;
函数声明:
要求 在函数调用之前,编译必须见过函数定义,否则 需要函数声明;
隐式声明:默认编译器做隐式声明函数时,返回都为 int ,根据调用语句不全函数名和形参列表。
exit 函数:
return:
返回当前函数调用,将返回值返回给调用者。
exit() 函数:
退出当前程序;
多文件编程:
多个含有不同函数功能 .c 文件模块,编译到一起,生成一个 .exe 文件
防止头文件重复包含:头文件守卫
- #pragma once --->windows中
- #ifndef __HEAD_H__ head.h
#define __HEAD_H__
指针
指针和内存单元:
指针: 地址;
内存单元:计算机中内存最小的存储单位。——内存单元。大小为一个字节,每个内存单元都有一个唯一的编号。
指针定义和使用:
int a = 10;
int *p = &a;
*p = 250; //指针的解引用。间接引用
//*p :将p变量的内容取出,当成地址看待,找到该地址对应的内存空间。
//如果做左值,存数据到空间中,
// 如果做右值,取出空间中的内容。
指针的大小与 类型 无关。只与当前使用的平台架构有关。32位:4字节; 64位:8字节;
野指针:
- .没有一个有效地址空间的指针。
int *p;
*p = 200;
- .p变量有一个值,但是该值不是访问的内存区域;
int *p = 10;
*p = 1000;
【杜绝野指针】
空指针:
int *p = NULL;
*p 时 p 所对应的存储空间一定是一个 无效的访问区域
万能指针/泛型指针(void *)
可以接收任意一种变量地址。但是在使用必须借助“强转” 具体化数据类型
char ch = ‘R’;
void *p;
p = &ch;
printf(“%c\n”,*(char *)p);
const 关键字:
修饰变量:
const int a = 20;
int *p = &a;
*p = 650;
printf(“%d\n”,a);
修饰指针:
const int *p;
可以修改p;
不可以修改*p;
int const *p;
可以修改p;
不可以修改*p;
int * const p;
可以修改*p;
不可以修改 p;
const int *const p;
不可以修改p;
不可以修改*p;
总结:const向右修饰,被修饰部分即为只读。
常用:在函数形参内,用来限制指针所对应的内存空间为只读;
指针和数组:
数组名:【数组名是地址常量】不可以被修改(赋值)。
指针是变量,可以用数组名给指针赋值。
取数组元素:
int arr[] = {1,3,5,7,8};
int *p = arr;
arr[0]==*(arr+0) == p[0] == *(p+0);
指针和数组的区别:
- 指针是变量,数组名为常量。
- sizeof(指针) ==》4/8
sizeof(数组)==》数组的实际字节数。
指针++操作数组:
int arr[] = {1,2,3,4,5,6,7,8,9,0};
int *p = arr;
for(size_t i=0;i<n;i++)
{
printf(“%d”,*p);
p++;//p=p+1;一次加过一个int大小,一个元素。
}
p的值会随着循环不断变化。打印结束后,p指向一块无效的地址空间(野指针)
指针加减运算:
数据类型对指针的作用:
- .间接引用:
决定了从指针存储的地址开始,向后读取的字节数(与指针本身存储空间无关。)
- .加减运算:
决定了指针进行+1操作向后加过的 字节数。
指针不能进行:* / % 运算
指针+-整数
- .普通指针变量+-整数:
char *p: 打印p、p+1 偏过 1 字节
short *p: 打印p、p+1 偏过 1 字节
int *p: 打印p、p+1 偏过 1 字节
- 在数组中+-整数
short arr[] = {1,3,5,8};
int *p = arr;
p+3; //向右(后)偏过 3 个元素
P-2; //向左(前)偏过 2 个元素
- &数组名+1;
加过一个 数组的大小 (数组元素个数*sizeof(数组元素类型))
指针 + 指针 :error!!!
指针 — 指针:
- .普通变量来说,语法运算。无实际意义。
- 数组来说,偏移过的元素个数
指针实现strlen函数:
char str[] = “hello”;
char *p = str;
while(*p !=’\0’)
{
p++;
}
p-str:即为 数组有效元素个数
指针比较运算:
- .普通变量来说,语法语法允许,无实际意义。
- .数组来说: 地址之间可以进行比较大小。
可以得到 元素存储的先后顺序
- . int *p;
p = NULL; //这两行代码等价于:int *p = NULL;
指针数组:
一个存储地址的数组。数组内部所有元素都是地址 。
本质:是一个二级指针
二维数组,也是一个二级指针。
多级指针:
int a = 0;
int *p = &a; 一级指针是变量的地址。
int **pp = &p; 二级指针是一级指针的地址。
int ***ppp = &pp; 三级指针是二级指针的地址。
int ****pppp = &ppp 四级指针是三级指针的地址。
多级指针不能跳跃定义;
对应关系:
ppp === &pp; 三级指针
*ppp = pp =&p; 二级指针
**ppp = *pp = p = &a; 一级指针
***ppp = **pp = *p = a 普通整型变量
指针和函数:
栈帧:当函数调用时,系统会在stack 空间上申请一块内存区域,用来功函数调用,主要存放 形参 和 局部变量(定义在函数内部)。
当函数调用结束,这块内存区域自动被释放(消失)。
传值和传址;
传值:函数调用期间,实参将自己的值,拷贝一份给形参
传址:函数调用期间,实参将地址值,拷贝一份给形参。
(地址值--》 在swap函数栈帧内部,修改了main函数栈帧内部的局部变量值)
指针做函数参数:
int swap2(int *a,int *b);
调用时,传有效的地址值。
数组做函数参数:
void BubbleSort(int arr[10]) == void BubbleSort(int arr[]) == void BubbleSort(int *arr)
传递的不再是整个数组,而是数组的首地址。(一个指针的大小)
所以,当整型数组做函数参数时,我们通常在函数定义中,封装2个参数,一个表数组首地址,一个表元素个数
指针做函数返回值:
int *test_func(int a,int b)
指针做函数返回值,不能返回【局部变量的地址值】。
数组做函数返回值: C语言,不允许!!!只能写成指针形式。
指针和字符串:
1). char str1[] = {‘h’,’i’,’\0’};
char str2[] = “hi”;
char *str3[] = “hi”;
char *str4[] = {‘h’,’i’,’\0’};//错误!!!
2). 当字符串(字符数组),做函数参数时,不需要提供2个参数,因为每个字符串都有’\0‘.
字符串去空格:
//字符串去空格实现 -数组
void str_no_space(char *src, char *dst)
{
int i = 0;//遍历字符串src
int j = 0;//记录dst存储位置
while (src[i] != 0)
{
if (src[i] != ' ')
{
dst[j] = src[i];
j++;
}
i++;
}
dst[j] = '\0';
}
//字符串去空格实现 -指针
void str_no_space2(char *src, char *dst)
{
while (*src)
{
if (*src != ' ')
{
*dst = *src;
dst++;
}
src++;
}
*dst = '\0';
}
带参数的main函数:
无参main函数: int main(void) == int main()
带参数的main函数:int main(int arge,char *argv[]) == int main(int argc,char **argv)
参1:表示给main 函数传递的参数总个数;
参2:是一个数组:数组的每一个元素都是字符串 char *;
测试1:命令行终端中,使用gcc编译器生成 可执行文件,如:test.exe
test.exe a b c d
argc---5
tect.c -->argv[0];
a-->argv[1];
b-->argv[2];
c-->argv[3];
d-->argv[4];
测试2:在vs中,项目名称上——》右键——》属性——》调试——》命令行参数——》将 test.exe a b c d 写入
argc---5
tect.c -->argv[0];
a-->argv[1];
b-->argv[2];
c-->argv[3];
d-->argv[4];
str中 substr出现次数:
strstr函数:在str中,找substr出现的位置。
char *strstr(char *str,char *substr) ---#include <string.h>
参1:原串
参2:子串
返回值:返回子串在原串中的位置。(地址值)
如果没有:返回 NULL;
字符串处理函数#include<string.h>
字符串拷贝:
strcpy:
将str的内容,拷贝给dest,返回dest,注意要保证dest空间足够大,防止内存溢出;char *strcpy(char *dest,const char *src);
函数调用结束,返回值和dest 结果一致;
strncpy:
将 src的内容拷贝给 dest,只拷贝n个字节,通常n与dest 对应的空间一致。
默认 不添加’\0’
char *srencpy(char *dest,const char *src, size_t n);
特性:n>src:只拷贝src 的大小;
n<src: 只拷贝 n字节的大小。不添加‘\0’;
字符串拼接:
strcat: 将src的内容,拼接到dest后,返回拼接的字符串。需确保dest空间足够大
char *strcat(char *dest,const char *src,)
strncat: 将src的前n个字符,拼接到dest后形成一个新的字符串。需确保dest空间足够大
char *strcat(char *dest,const char *src)
字符串比较:
strcmp:比较s1和s2两个字符串,如果相等,返回0,如果不相等,进一步s1和s2对应位ASCII码值 int strcmp(const char *s1,const char *s2,size_t n)
s1>s2 返回 1
s1<s2 返回 -1
strcmp:比较s1和s2两个字符串的前n个字符
如果相等,返回0,如果不相等,进一步s1和s2对应位ASCII值(不比较字符串ASCII码的和)
函数原型: int strcmp(const char *s1,const char *s2,size_t n)
s1>s2 返回 1
s1<s2 返回 -1
字符串格式输入、输出:
sprintf():
函数原型: int sprintf(char *str, const char *format,...)
对应printf,将原来写到屏幕上的“格式化字符串”,写到 参数1 str中
sscanf():
函数原型:int sscanf(const char *str,const char *format,...)
对应scanf:将原来从屏幕获取的“格式化字符串”,从参数1 str中获取
scanf("%d+%d=%d",&a,&b,&c);
——》
char str[]="10+24=34":
sscanf(str,“%d+%d=%d”,&a,&b,&c);a->10;b->24; c->34;
字符串查找字符、子串
strchr():
在字符串str中 找一个字符出现的位置,返回字符在字符串中的地址
函数原型:char *strchr(const char *s,int c);
printf(“%s\n” strchr(“hehehheahahahohoh”,’a’));
-->返回的结果为:“ahahahohoh”
strrchr():
自右向左,在字符串str中 找一个字符出现的位置,返回字符在字符串中的地址
函数原型:char *strrchar(const char *s,int c);
printf(“%s\n” strrchr(“hehehheahahahohoh”,’a’));
-->返回的结果为:“ahohoh”
strstr():
在字符串str中,找子串substr第一次出现的位置,返回地址
函数原型:char *strstr(const char *str,const char *substr);
在字符串中找子串的位置。
printf(“%s\n” strrchr(“hehehheahahahohoh”,’ho’));--》“hohoh”
printf(“%s\n” strrchr(“hehehheahahahohoh”,’xixi’));--》NULL;
字符串分割:
strtok();按照既定的分隔符,来拆分字符串”www.baidu.com”
char *strtok(char *str const char *delin);
参1:带分割字符串; 参2:分割符组成的”分割串”
返回 字符串拆分后的首地址。”拆分“ :将 分割字符用‘\0’
strtok(“www.baidu.com”,”.”);
第一次拆分,参一传待拆分的原串,第+ 次拆分时,参1传NULL
练习“www.itcast.co$This is a strtok$test”
char str[] = "www.itcast.co$This is a strtok$test";
char *p = strtok(str, " .$");
while (p != NULL)
{
p = strtok(NULL, " .$");
printf("p = %s\n", p);
}
atoi/atof/atol:使用这类函数进行转换,要求,原串必须是可转换的字符串。
atoi:字符串转整数
atof字符串转浮点数
atol字符串转:长整型数
内存管理:
局部变量:
定义在函数内部的变量。
作用域:从定义位置开始,到包裹该变量的第一个大括号结束
全局变量:
概念:定义到函数 外 部的变量;
作用域:从定义位置开始,默认到本文件内部,其他文件如果想要使用,可以通过声明方式将作用域导出
静态全局变量:
static 全局变量:
定义语法:在变量定义之前添加static 关键字
作用域:被限制在本文件内部,不允许通过声明导出到其他文件。
静态局部变量:
static 局部变量
定义语法:在局部变量定义之前添加static 关键字
特性:静态局部变量只定义一次,在全局位置,通常用来做计数器。
作用域:从定义位置开始,到包裹该变量的第一个大括号结束。
全局函数:
函数
定义语法:函数原型+函数体;
static函数:
定义语法:static+函数原型+函数体
static 函数 只能在本文件内部使用其他文件即使声明也无效。
生命周期:
局部变量: 从变量定义开始,函数调用完成结束;---函数内部。
全局变量: 程序启动开始,程序终止结束---程序执行期间。
static局部变量: 程序启动开始,程序终止结束---程序执行期间。
static全局变量: 程序启动开始,程序终止结束---程序执行期间。
全局函数: 程序启动开始,程序终止结束---程序执行期间。
static函数: 程序启动开始,程序终止结束---程序执行期间。
Windows:内存四区模型:
代码段: .test段(代码段);(二进制形式存储)
数据段: .data段:存放初始化为非0的全局变量和静态变量
.bss段:初始化为0,未初始化的全局变量和静态变量。程序加载执行前,宏将该段整体赋值为0。
.rodata:只读数据段。存放常量
(栈)stack:比较小,在其之上开辟栈帧Windows:1M-- 最大10M;Linux :8M--最大16M
系统自动管理,自动分配,自动释放
存储特性是;FILO
(堆)heap:空间足够大,给用户自定义数据提供空间,约1.3G+;
开辟释放heap空间:
void malloc(size_t size)申请size大小的空间
返回实际申请到的内存空间首地址【我们通常拿来当数组】
void free(void *ptr)释放申请的空间
参数:malloc返回的地址值。
使用heap空间:空间连续时,当成数组使用
free后的空间,不会立即失效,通常将free后的地址置为NULL;
free 地址必须是malloc申请地址,否则出错
如果malloc之后的地址一定会变化,那么使用临时变量temp保存
二维指针对应的heap空间:
申请外层指针:
char **p = malloc(sizeof(char)*5);
申请内存指针:
for(i = 0;i<5;i++)
{
p[i] = (char *)malloc(sizeof(char)*10);
}
使用:不能修改p的值
for(i = 0;i<5;i++)
{
strcpy(p[i],”helloheap”);
}
//释放内层:
for(i = 0;i<5;i++)
{
free(p[i]);
}
//释放外层:
free(p);
结构体:
结构体成员赋值:
struct Student s1;
struct Student s2 = {1,”张三”,99.9};
//方法1:逐个成员赋值
s1.id = s2.id;
strcpy(s1.name , s2.name);
s1.score = s2.score;
//方法2:整体赋值(相同数据类型结构体变量之间)推荐
//s1 = s2;
//方式3:memcpy
memcpy(&s1,&s2,sizeof(struct Student));
//结构体变量交换:
struct Student s1 = {1,”tom”,60};
struct Student s2 = {2,”jerry”,90};
//三杯水法:
int idTemp = s1.id;
s1.id = s2.id;
s2.id = idTemp;
char nameTemp[64];
strcpy(nameTemp,s1.name);
strcpy(s1.name,s2.name);
strcpy(s2,name,nameTemp);
int scoreTemp=s1.score;
s1,score = s2.score;
s2.score = scoreTemp;
//直接交换
struct Student temp;
temp = s1;
s1 = s2;
s2 = temp;
//结构体数组:
struct Student arr[5]={
{1,”张三”,100},
{1,”李四”,59},
{1,”王五”,97},
{1,”赵六”,98},
{1,”陈冠希”,99}
//最后一个成员的最后 可写可不写’,’,
}
遍历数组:
int len = sizeof(arr)/sizeof(struct Student);
for(int i=0;i<len;i++)
{
printf(“学号:%d 姓名:%s 得分:%lf\n”,arr[i].id,arr[i].name,arr[i].score);
}
结构体嵌套结构体:
struct Stu
{
int id;
int age;
};
struct Tea
{
int id;
struct Stu st;
};
struct Tea t1 = { 100,{1,18}};
struct Tea t2 = {101,2.19};//两种调用方式均可用
//通过指针获得键盘输入
void test02()
{
struct Student s;
struct Student *p = &s;
scanf("%d %s %lf", &p->id, p->name, &p->score);
printf("id = %d name = %s,score = %.1lf\n", p->id, p->name, p->score);
}
//结构体创建在堆区
void test03()
{
struct Student * p = malloc(sizeof(struct Student));
if (p == NULL)
{
return;
}
scanf("%d %s %lf", &p->id, p->name, &p->score);
printf("id = %d name = %s,score = %.1lf\n", p->id, p->name, p->score);
//堆区释放空间
if (p != NULL)
{
free(p);
p = NULL;
}
}
//结构体数组创建在堆区
void test04()
{
int n;
printf("请输入数组元素个数:");
scanf("%d\n",&n);
struct Student *arr = malloc(sizeof(struct Student)*n);
//清空空间
memset(arr,0, sizeof(struct Student)*n);
for (int i = 0; i < n; i++)
{
printf("请给第%d个学生赋值:\n",i+1 );
scanf("%d %s %lf", &arr->id, arr->name, &arr->score);
}
for (int i = 0; i < n; i++)
{
printf("id=%d name=%s score=%.1lf\n", arr[i].id, arr[i].name, arr[i].score);
}
//释放数组
if (arr != NULL)
{
free(arr);
arr = NULL;
}
}
//结构体做函数参数
void printStudent(struct Student s)//值传递
{
printf("id=%d name=%s,score=%.1lf\n", s.id, s.name, s.score);
}
void printStudent2(struct Student *s)
{
printf("id=%d name=%s score=%.1lf\n", s->id, s->name, s->score);
}
void test05()
{
struct Student s1 = { 1,"tom",80 };
printStudent(s1);
printStudent2(&s1);
}
//封装一个函数,给数组赋值
void setStudentArray(struct Student *arr, int len)
{
for (int i = 0; i < len; i++)
{
printf("请给第%d个同学赋值\n", i + 1);
scanf("%d %s %lf", &arr[i].id, arr[i].name, &arr[i].score);
}
}
void test06()
{
struct Student arr[2];
memset(arr, 0, sizeof(arr));
int len = sizeof(arr) / sizeof(arr[0]);
setStudentArray(arr, len);
for (size_t i = 0; i < len; i++)
{
printf("id=%d name=%s score=%lf", arr[i].id, arr[i].name, arr[i].score);
}
}
const 修饰结构体:
const struct Student *p1 = &s1;//常量指针 指针指向的值不可以改,指向可以改
//p1->id = 200;//error指针指向的值不可以改,
p1 = &s2;//success
struct Student * const p2 = &s2;//指针常量,指针指向不可以改,指向的值可以改
p2->id = 200;//success
//p2=&s1;指针的指向不可以改
const struct Student *const p3 = &s1;//指针的指向和指针指向的值都不能修改
结构体嵌套一级指针:需要进行多次翻译堆区空间
//创建
struct Teacher ** teaArray = malloc(sizeof(struct Teacher*) * 3);
for (size_t i = 0; i < 3; i++)
{
teaArray[i] = malloc(sizeof(struct Teacher));
//老师的姓名 在堆区创建
teaArray[i]->name = malloc(sizeof(char) * 64);
sprintf(teaArray[i]->name, "Teacher_%d", i + 1);
teaArray[i]->age - 30 + i;
}
for (size_t i = 0; i < 3; i++)
{
printf("姓名:%s 年龄:%d\n", teaArray[i]->name, teaArray[i]->age);
}
//释放
for (size_t i = 0; i < 3; i++)
{
if (teaArray[i]->name != NULL)
{
printf("%s被释放了\n", teaArray[i]->name);
free(teaArray[i]->name);
teaArray[i]->name = NULL;
}
if (teaArray[i] != NULL)
{
free(teaArray[i]);
teaArray[i] = NULL;
}
}
//释放数组
if (teaArray != NULL)
{
free(teaArray);
teaArray = NULL;
}
}
共用体(联合体):
联合体中的成员,公用同一块内存空间 按照最大的数据类型分配内存空间
修改其中一个成员变量的值,其他成员变量会随之修改
枚举:
neum color {枚举常量};//能穷举得尽的
枚举常量的值:是整型常量,不能是浮点数,可以是负值,默认初始值从0开始,后续常量较前一个常量+1,可以给任意一个常量赋任意初值,后续常量较前一个常量+1;
读写文件与printf 、scanf关联
printf--屏幕--标准输出
scanf--键盘--标准输入
perror--屏幕--标准错误
系统文件:
标准输入--stdin--0
标准输出--stdout--1
标准错误--stderr--2
应用程序启动时,自动被打开,程序执行结束时,自动被关闭;
文件指针和普通指针区别:
FILE *p = NULL;
借助文件操作函数来改变fp为空、野指针的状况。fopen();-->相当于fp=malloc();
操作文件,使用文件读写函数来完成。fputc、fgetc、fputs、fgets、fread、fwrite;
文件分类:
设备文件:
屏幕、键盘、磁盘、网卡、声卡、显卡、扬声器、
磁盘文件:
文本文件
二进制文件
文件操作的一般步骤:
- 打开文件 fopen();
- 读写文件 fputc、fgetc、fputs、fgets、fread、fwrite
- 关闭文件 fclose()
打开 关闭文件函数:
FILE *fopen(const char * filename,const char * mode);
参数1:待打开文件的文件名(访问路径)
参数2:文件打开权限:
“r” 只读方式打开文件,文件不存在,报错。存在,以只读方式打开。
“w”只写方式打开文件 文件不存在,创建一个空文件文件如果存在,清空并打开。
“w+”:读、写方式打开文件,文件不存在,创建一个空文件,文件如果存在,清空并打开。
“r+”读、写方式打开文件,文件不存在,报错。存在,以读 写方式打开。
“a”以追加的方式打开文件。
“b”:操作的文件是一个二进制文件(Windows)
返回值:成功:返回打开文件的文件指针
失败:NULL
int fclose(FILE * stream);
参数1:打开文件的fp(fopen的返回值)
返回值:成功:0 失败:-1;
文件访问路径:
绝对路径:
从系统磁盘的根盘符开始,找到带访问的文件路径
C:\Users\24720\Desktop\新建 DOC 文档.doc
C:/Users/24720/Desktop/新建 DOC 文档.doc --- 也使用于linux
相对路径:
- .如果在VS 环境下,编译执行文件相对路径是指相对于工程文件所在目录位置
- .如果双击xx.exe文件执行,文件的相对路径是相对于xxx.exe所在目录位置
按字符写文件:fputc:
int fputc(int ch,FILE *stream);
参1:待写入的字符
参2:打开文件fp(fopen 的返回值)
返回值:成功:写入文件中的字符对应的ASCII码
失败:-1
按字符读文件 fgetc
int fgetc(FILE * stream)
参数1:带读取的文件fp(fopen的返回值)
返回值:成功:写入文件中的字符对应的ASCII码
失败:-1
文本文件的结束标记:EOF--》-1
feof()函数:
int feof(FILE * stream);
参数1:fopen 的返回值
返回值:到达文件结尾——》非0【真】
没有到达文件结尾--》0【假】
作用::用来判断到达文件结尾,既可以判断文本文件,也可以判断二进制文件
特性:要想使用feof()检测文件结束标记,必须在该函数调用之前,使用读文件函数。
feof()调用之前,必须有读文件函数调用;
fgets()函数:
获取一个字符串。以\n作为结束标记,自动添加\0,空间足够大 读\n,空间不足舍弃\n,必须有\0;
char *fgets(cahr *str,int size,FILE * stream);
char buf[10]; // hello --> hello\n\0
返回值:成功:读到的字符串; 失败:NULL;
fputs()函数:
写出一个字符串,如果字符串中没有\n不会写出\n;
int fputs(const char *str,FILE *stream)
返回值: 成功 0
失败 -1
fgets() 和fputs() 练习:
FILE *fp = fopen("test07.txt" ,"w");
if (fp == NULL)
{
perror("fopen error");
return -1;
}
char buf[4096] = { 0 };
while (1)
{
fgets(buf, 4096, stdin);
if (strcmp(buf, ":wq\n") == 0)
{
break;
}
fputs(buf, fp);
}
fclose(fp);
练习 文件版四则运算:
- 封装write_file函数,将四则运算表达式写入;
FILE *fp = fopen(“w”);
futs(“10/2=\n”,fp);
futs(“10+2=\n”,fp);
.....
- 封装read_file函数,将四则运算表达式读出,拆分、运算、写回。
- .读出:
FILE *fp=fopen(“r”);
int buf[4096];
while(1)
{
fgets(buf,sizeof(buf),fp); //buf 中存储的 四则运算表达式
}
- 拆分:
sscanf(buf,”%d%c%d=\n”,&a,&ch,&b);//得到运算数,运算符
- 根据运算符,得到运算结果
switch(ch)
{
case ‘/’:
a+b;
....
}
- 拼接 结果到 运算式 上
char result[4096];
sprintf(result,”%d%c%d=%d\n”a,ch,b,a+b);//result 中包含带有结果的运算式。
- 将 多个带有结果的运算 拼接成为一个字符串
char sum_rea[4096]; //存总的字符串
strcat(sum_rea,result),//在while中循环拼接
- 重新打开文件,清空原有的四则运算表达式
fclose(fp);
fp = fopen(“w”);
- 将 拼接成一个字符串,写入到空文件中
fputs(sum_res);
printf --- sprintf --- fprintf : 均为变参函数:参数形参中有“...”,最后一个固参通常是格式描述串(包含格式匹配符),函数的参数个数、类型、顺序由这个固参决定。
printf(“hello”);
printf(“%s”,“hello”);
printf(“ret = %d\n”,ret);
char buf[4096] = {0};
sprintf(buf,“%d = %d%c%d\n”,10+5,10,’+’,5);--》buf中
FILE *p = fopen()
fprintf(fp,”%d = %d%c%d\n”,10+5,10,’+’,5);-->fp对应的文件中
scanf --- sscanf ---fscanf
scanf(“%d”,&m);
char[] = “98”;
sscanf(str,”%d”,&m);//str-->m
FILE *fp = fopen(“r”);
fscanf(fp,”%d”,&m);//fp指向文件中——>m
fprintf()函数:
int fprintf(FILE *stream,const char *format,...)
fscanf()函数:
int fscanf(FILE *stream,const char *format,...)’
- .边界溢出:储存读取的数据空间,在使用之前清空。
- .fscamf()函数,每次在调用时都会判断下一次调用调用是否匹配参数2,如果不匹配提前结束文件读操作(feof(fp)为真)。
练习:文件版排序:生成随机数,写入文件,将文件内乱序随机数读出,排好序再写回文件
void write_file()//在文件中添加10个随机数
{
FILE *fp = fopen("test11.txt", "w");
if (fp == NULL)
{
perror("fopen error");
return -1;
}
srand(time(NULL));
for (size_t i = 0; i < 10; i++)
{
fprintf(fp, "%d\n", rand() % 100);//在文件中添加10个随机数
}
fclose(fp);
}
//冒泡排序
void BubbleSort(int * arr, int len)
{
for (size_t i = 0; i < len-1; i++)
{
for (size_t j = 0; j < len-1-i; j++)
{
if (arr[j] > arr[j + 1])
{
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
//将文件中的随机数取出,排序
void read_file()
{
int arr[10],i=0;
FILE *fp = fopen("test11.txt", "r");
if (fp == NULL)
{
perror("fopen error");
return -1;
}
while (1)
{
fscanf(fp,"%d\n", &arr[i]);
i++;
if (feof(fp))//到最回一个跳出
{
break;
}
}
for (size_t i = 0; i < 10; i++)
{
printf("%d\n", arr[i]);
}
BubbleSort(arr,sizeof(arr)/sizeof(arr[0]));
//fclose(fp);
//写回文件中,覆盖之前数据
fp = fopen("test11.txt", "w");//清空未排序文件
if (!fp)
{
perror("fopen error");
return -1;
}
//重新写入
for (size_t i = 0; i < 10; i++)
{
fprintf(fp, "%d\n", arr[i]);//排好序的数组到文件
}
fclose(fp);
}
fgetc--fputc
fgets--fputs
fprintf--fscanf 默认处理文本文件
fwrite()函数:
---用来处理二进制文件(按二进制形式存储)
size_t fwrite(const void *ptr size_t nmemb, FILE *stream)
参数1:待写出数据的地址
参数2:待写出数据的大小
参数3:写出的个数 --参数2*参数3 = 写出数据的总大小
参数4:文件
返回值:成功 永远是 参数3 的值
失败:0
fread():
size_t fread(const void *ptr size_t nmemb, FILE *stream)
参数1:待读出数据的地址
参数2:待读出数据的大小
参数3:读出的个数 --参数2*参数3 = 读出数据的总大小
参数4:文件
返回值:成功 参数3 ---通常把参数2传1,将参3传欲读出的字节数
失败:0
0--读失败--feof(fp)--到文件结尾;
练习:大文件拷贝:
已知一个任意类型的文件对该文件复制,产生一个相同的新的文件:
- 打开两个文件,一个“r”.另一个“w”
- 从r中fread,fwrite 到w文件中
- 判断到达文件结尾 终止
- 关闭
注意:在windows下,打开二进制文件(MP3、MP4、avi、jpg...)时需要使用“b”如“rb”“wb”;
随机位置读:
fseek():
文件读写指针,在一个文件内只有一个。
int fseek(FILE *stream,long offset,int whence);
参1:待读写的文件。
参2:偏移量(矢量:+ 向后; - 向前)
参3: SEEK_SET:WE:文件开头位置
SEEK_CUR:当前位置
SEEK_END:文件结尾位置
返回值: 成功:0,失败:-1;
ftell():
获取文件读写指针位置
long ftell(FILE *stream);
返回ftell + fseek(SEEK_END);来获取文件大小
rewind:
回卷文件读写指针,将读写指针移动到起始位置。
void rewind(FILE *stream)