目录
Linux基础
学习使用VMware workstation(虚拟一个新的操作系统)
Linux是一个统称,其中包括Ubuntu、Redhat、Debian、centos、FreeBSD
在此我学习使用的是Ubuntu,
一、常见的操作
1、挂起
2、关闭
3、重启:先“power off”再关闭,再启动
4、全屏:CTRL+alt+enter
5、打开终端:CTRL+alt+t
二、Linux用户
用户分两种:1、普通用户(权限会受限制):可有多个,也可没有
2、超级用户(权限不会受限制):有且只有一个,root
三、Linux基本命令
1、ls:
1. ls:查看当前目录下的所有文件(蓝为目录文件;白为普通文件)
2. ls -a:查看所有文件(包括隐藏文件,其中“.”为当前目录“..”为当前的上一级目录)
3. ls -l:查看当前目录下文件的详细信息;体现为:文件类型+三组rwx+用户+组+文件大 小+文件改变类型+文件名(r用户拥有读权限,w用户拥有写权限,x用户拥有执行权限)
2、mkdir 在当前目录创建新的目录文件(文件名由数字字母下划线构成)
例如:mkdir 文件名 (注:文件名不能加空格、为保证可移植性虽然Linux不以后缀区分文件类型、文件名区分大小写)
3、rmdir 删除指定目录文件(rm -rf 文件名 可删除非空文件夹)
4、touch 创建普通文件
5、rm 删除普通文件
6、cd 进入指定目录(有" ..""/""~")
7、pwd 查看当前所在目录的绝对路径
8、cp ->copy 拷贝文件或目录
普通文件:cp 源文件路径 目的文件路径
目录文件:cp 源文件路径 目的文件路径 -rf
9、mv 移动文件或目录/修改文件名
mv 源文件路径 目的文件路径
mv 文件旧名 文件新命
四、基于Linux的C语言开发流程简介
1、vim编辑器
1.使用vim打开
vim 文件名.c
2.点击"i"键开始编辑(“o”“a”键一般用不上)
3.编写程序
#include<stdio.h>
int main(void)
{
printf("Holle World!");
return 0;
}
4.点“esc”退回底行模式
5.输入“:wq”保存并退出
“:q”只退出不保存
“:q!”不保存强制退出
2、gcc编译器
1.编译gcc
gcc 文件名.c(默认生成a.out)
gcc 文件名.c -o 文件名(指定生成文件名)
2.执行"./文件名"
3.可能出现error;warnning;
4.逻辑错误,需要花大量时间,所以编辑时需注意
2024.1.15完成以下内容
20240115作业
起止时间: 2024/01/15 17:08 ~ 2024/02/15 23:59
作业: 1.在~/Desktop目录下创建三个文件夹test1 test2 test3 2.在test1下创建filename1.txt 在test2下创建filename2.txt 在test3下创建filename3.txt 3.将test1目录下的filename1.txt拷贝到test2目录下 4.将test2目录下的filename2.txt移动到test3目录下 5.将test3拷贝到test1目录下 6.将test1目录修改文件名为filename 7.最后将filename文件夹删除 2.编写一个C语言程序利用printf打印如下图形
第二步C语言基础(基础MAX)
day1(2024.1.15)
C语言基础
一、基础
写在前面{1.进制转换:二进制、八进制、十进制、十六进制;o开头为八进制,ox开头为十六进制
二进制、八进制、十六进制转换为十进制(对应位*对应位的权值在求和)
十进制转换为二进制、八进制、十六进制(十进制数除以进制数得到的余数逆序排列)
HEX:十六进制 DEC:十进制 OCT:八进制 BIN:二进制
2.程序在C语言中运行
CPU--内存--外存 程序(外存中)
执行(加载到内存中)
程序运行结束(回收内存空间)
3.数字在内存中的存储
正数在计算机中存放对应的二进制数
负数在计算机中存放其对应的补码(1.原码2.反码3.补码4.内存数据)
4.gcc编译的流程步骤
a.预处理
#define 宏定义
#include 头文件展开
#if #elif #else #endif #ifndef #ifdef条件编译
gcc -E 文件名.c -o 文件名.i
b.编译
将c语言编译为汇编
gcc -s 文件名.c -o 文件名.s
c.汇编
将汇编代码编译为二进制
gcc -c 文件名.c -o 文件名.o
d.链接
将多个二进制代码链接成一个可执行程序
直接gcc 文件名.c}
1、基本数据类型、运算符、表达式
数据类型分类:1.基本数据类型(整形short;int;long;浮点型float;double;字符型char;逻辑型bool;缺省型void;)只有整数型有符号概念
2.构造数据类型(结构体 共用体 枚举类型)
1、基本数据类型
1.整数类型:
(移植性差,故规定short不大于int;long不小于int)
a. short短整型;包含有符号和无符号(signed & unsigned)
2个字节空间(-32768~32767)&(0~65535)
b. int整型;包含有符号和无符号(signed & unsigned)
4个字节空间(-2^31~2^31)&(0~2^32)
c. long长整型;包含有符号和无符号(signed & unsigned)
8个字节空间(-2^63~2^63)&(0~2^64)
注:C语言中规定-32768==1000 0000 0000 0000
测试不同数据类型的字节长度:
#include<stdio.h>
int main(void)
{
printf("%ld\n",sizeof(short));/*"%ld"为占位符,sizeof的括号内为数据类型*/
return 0;
}
2.浮点型:
a. float单精度浮点型;只有有符号(最多表示6~8位有效数字)
4个字节空间(-3.4*10^38~-1.18*10^-38
1.18*10^-38~3.4*10^38)
存储方式为:1符号位+8指数位+23尾数位
b. double双精度浮点型;(最多表示12~14位有效数字)
8个字节空间(-1.8*10^308~-2.23*10^-308
2.23*10^-308~1.80*10^308)
存储方式为:1符号位+11指数位+52尾数位
3.字符型:
a. char;
1个字节空间(-128~127)
b. unsigned char;
1个字节空间(0~255)
储存方式为ASCII码对应的二进制形式
4.逻辑类型:
bool;
1个字节空间(ture,false)
存储方式为1,0
注:C语言中所有的非0值均为逻辑真值,0为逻辑假值。
5.缺省:
void;搭配函数和指针使用
2、变量和常量
1.常量(在程序运行过程中值不允许发生改变的量)
2.变量(在程序运行过程中值允许发生改变的量)
3.表达式(有一个或多个运算符连接的变量和常量组成的式子)
整型常量:默认为int型
整型变量:1.先定义再使用
a.变量定义的形式:数据类型 变量名
b.变量名由下划线、字母、数字构成
c.不能与关键字重合
d.不要与库函数重合
e.变量名最好见名知意
2.变量拥有存储空间,也拥有值域范围,注意不要超过
浮点型常量:默认为double类型
浮点型变量:double d;float f;(%d ->short;%ld ->long;%u ->unsigned short; %lu ->unsigned long;%f ->float;%lf ->double)
字符型常量:用单引号引起来,且内容只能有一个
特殊:'\n'换行符;'\r'回车符;'\t'横向制表符;'\v'纵向制表符;'\b'退格符;'\0'ASCII码值为0,用来标识字符串的结尾
字符型变量:char 变量名
今日完成任务
day2(2024.1.16)
4.字符串常量:
用双引号"ab";
末位都有一个用来表示字符串结尾的\0字符;
字符串没有变量;
5.标识常量:
#define :宏只是代码的替换,中间不进行计算
宏的名字一般都是大写,用来和变量区分
6.变量:
a.初始化:定义变量时对变量赋初值
b.赋值:定义变量后对变量赋值
注:1、未经初始化的变量为随机值;2、定义变量时最好对变量初始化
3、类型转换
1.显式类型转换
(强制类型转换),只在当时转换,只转换一次
2.隐式类型转换
低精度转高精度
表达式一定有值、一定有类型
同种类型:所占空间越大精度越高
无符号精度高于有符号精度
float类型进行数据运算和数据处理时会转换为double类型
short、char类型进行数据运算和数据处理时会转换为int类型
4、运算符
1.算术运算符
+ - * / %
/:不能除以零
%:不能对浮点数操作
++ 自增运算符
-- 自减运算符
i++ 先用后加;++i 先加后用;i-- 先用后减;--i 先减后用;(尽量不要写在式子里)
2.赋值运算符
“=”
左值 = 右值
左值:只有变量;
右值:都可以有;、
左值为浮点数类型,右值为整形,会将值扩展精度
左值为整数类型,右值为浮点数类型,会舍弃精度
左右两边均为整形:将右值直接拷贝到左值空间中
小数据放入大空间:数据扩展,有符号数高位补符号位,无符号高位补0
大数据放入小空间:直接截取数据低位放入小空间
3.三目运算符(特殊运算符)
结合方向:自右向左;形式为:(表达式?表达式1:表达式2)
4.逗号运算符
从左至右依次执行,并将最后一个逗号表达式的结果作为整体的结果
5.sizeof运算符
sizeof获得数据类型或者变量在内存中所占的字节数
2、常用的输入输出函数
1.getchar
从终端接收一个字符;具有阻塞功能,直到输入数据才会继续(只能接收字符且只接收一个字符)
2.putchar
在终端打印一个字符;只能打印一个,想打印多个字符需要多次使用putchar
今日完成任务
day3(2024.1.17)
3.printf
1.打印非输出控制符;
\n 换行符 \t 横向制表符
\v 纵向制表符 \b 退格符
\r 回车符 \\ \字符
\" "字符
2.打印单个输出控制符;
%d 按照十进制打印
%o 按照八进制打印
%x 按照十六进制打印
%c 按照字符打印
%f 按照浮点数类型打印
%s 按照字符串打印
%p 按照指针类型打印
%u 按照无符号类型打印
%e 按照科学计数法打印浮点数类型
%% 打印%号
3.打印多个输出控制符;
注:输出控制符的个数必须与后面的表达式个数一一匹配
4.格式化打印;
%nd n:域宽,数据不足域宽,高位用空格补齐
%0nd 0:数据不足域宽,高位用0补齐
%-nd -:左对齐
%.mf m:保留小数点后m位有效数字
4.scanf
1.接收非输入控制符(非输入控制符需要原封不动的从终端输入)
2.接收输入控制符
%d 按照有符号十进制接收数据
%c 按照字符接收数据
_%c 接收一个' '和'\n'的字符
%f 按照float接收数据
%lf 按照double接收数据
%s 按照字符串接收数据
3.接收多个输入控制符
注:1.scanf如果输入数据与想要接收的内容不匹配,从不匹配位置开始不再向后读取;
2.除了%s以外都要加&;
3.scanf除%c外均无法接收空格和\n;
4.scanf中不允许加\n;
5.puts
在终端打印一行字符串
注:puts会在字符串末尾多打印一个\n字符
6.gets
从终端接收一行字符串
注:1.gets会去掉从终端接收的\n
2.gets可以接收带空格的字符串
3、流程控制
1.顺序结构
从main开始执行到main结束;
2.分支结构
1.if
if(逻辑表达式)
{
语句块1;
}
else
{
语句块2;
}
2.逻辑表达式
a.关系运算符
> 大于 < 小于 >= 大于等于 <= 小于等于
== 等于(通常左边写常量) != 不等于
b.逻辑运算符
&& 与 一假为假,真真为真
|| 或 一真为真,全假为假
! 非
截断特性(1、与:左假右不算;2、或:左真右不算)
注:1.if后面必须加判断条件 else不能加判断条件
2.if可以脱离else单独使用,但是else不能脱离if单独使用
3.if和else不加括号,默认控制一句代码,为了保证程序不出错,加{}
今日完成
day4(2024.1.18)
if的阶梯分支
使用场景:1、判断逻辑使用分支语句 2、统一数据完成不同操作
if (表达式1)
{
语句块1;
}
else if (表达式2)
{
语句块2;
}
else if (表达式3)
{
语句块3;
}
else
{
语句块4;
}
if嵌套分支(尽量少用)
2.switch分支
注:1、switch后的表达式的值必须为整形 2、case后必须为常量
1.基本形式switch (整形表达式)
switch (整形表达式)
{
case 常量1:语句块1;break;
case 常量2:语句块2;break;
case 常量3:语句块3;break;
default:
语句块4;
}
2.switch的嵌套类似if的嵌套
(里层switch结束后如果不加break,外层switch顺序向下执行)
{括号>单目>双目(算数>位移>关系>位运算符>逻辑运算符)>三目>赋值>逗号}
3.循环结构
1.while循环
1.基本形式
while (表达式)
{
语句块1;
}
2.死循环
while (1)
{
}
2.do..while循环
1.基本形式
do {
语句块;
} while (表达式);
注:1.do .. while后面要加;
2.do while无论表达式是否成立都会先执行一次
3.do while和while不能等价转换
3.for循环
1.基本形式
for (A; B; C)
{
D;
}
A:在循环前执行一次
B:循环的条件
C:每次循环结束后都执行C
D:循环体内部代码
今日完成
day5(2024.1.19)
2.嵌套
外层循环一次,里层循环一遍
4.goto
定义一个标号,跳转到标号
(辅助控制语句
1.break 跳出循环或switch
2.continue 结束本次循环,一个循环中屏蔽某几次循环
3.return 结束当前函数)
二、重点知识
1、数组
1.基本概念
一组数据类型相同且有限个元素的集合
2.分类
1.一维数组
一维整型数组
定义:数据组 数组名[个数]
注:元素个数必须为常量或常量表达式,不能是变量或变量表达式
元素访问
数组名[元素下标];元素下标: 0 - 元素个数-1
元素下标:可以用变量、常量或者表达式访问
注:不能越界访问
数组的初始化
定义数组时对数组元素赋初值
1.全部初始化:所有的元素都可以得到初始化;
2.局部初始化:给定一部分元素的值,没给定的元素默认初始化为0;
3.默认初始化:不给定数组元素个数通过初值元素个数决定数组元素个数
内存空间
数组占空间 = 数据类型所占空间大小*元素个数
1.连续性:数组所占空间连续
2.有序性:数组空间存放顺序有序
获得元素个数: sizeof(a)/ sizeof(a[0])
获得数组中最大值和最小值
今日完成
day6(2024.1.20)
2.二维数组
1.二维整形数组:
1.定义:
数组类型 数组名[行数][列数];
行数和列数:整形常量或整形常量表达式,不能为变量或变量表达式
int a[2][3];
1 2 3
4 5 6
2.元素访问:
数组名[行下标][列下表]
a[0][0]
a[0][1]
a[0][2]
a[1][0]
a[1][1]
a[1][2]
下标:可以是常量或者变量或者表达式
3.元素初始化:
初始化 != 赋值
1.全部初始化:
int a[2][3] = {1, 2, 3, 4, 5, 6};
int a[2][3] = {{1, 2, 3}, {4, 5, 6}};
2.局部初始化:
int a[2][3] = {1, 2, 3, 4};
int a[2][3] = {{1, 2}, {3}};
int a[2][3] = {0};
int a[2][3] = {{0}};
3.默认初始化:
行能省略,列不能省略
int a[][3] = {1, 2, 3, 4, 5, 6};
int a[][3] = {{1, 2, 3}, {4, 5, 6}};
int a[][3] = {1, 2, 3, 4};
int a[][3] = {1, 2, 3};
int a[][3] = {{1, 2}, {3}};
int a[][3] = {0};
int a[2][3] = {0};
4.二维数组的存储:
int a[2][3];
数组所占空间大小 = 数据类型所占空间大小 * 行数 * 列数
1.连续性
数组存放空间连续
2.有序性
逐行从左向右存储
sizeof(a) / sizeof(a[0][0]);
结论:二维数组可以看成是由一维数组构成的一维数组
2.多维数组:
N维数组可以理解成是由N-1维数组构成的一维数组
练习:从终端接收6个数存放到二维数组int a[2][3]中并打印平均数
2.字符型数组:
"hello world"
1.使用场景:
C语言没有字符串类型,也无法定义字符串变量,只能通过字符型数组,每个元素存放一个字符,最终存放字符串
2.字符型数组的定义:
数据类型 数组名[元素个数];
"hello world"
char str[12];
注:
1.元素个数必须为常量或常量表达式,不能为变量或变量表达式
2.元素个数必须能够容纳下所有字符(包括\0)
字符型数组和字符串区别:
1.字符型数组可以存放字符,不一定包含\0
2.字符串最末尾一定要有\0
3.字符型数组的初始化:
初始化 != 赋值
1.全部初始化:
char str[6] = {'h', 'e', 'l', 'l', 'o', '\0'};
char str[6] = {"hello"};
char str[6] = "hello";
2.局部初始化:
char str[6] = {'h', 'e', 'l', '\0'}; //没有给定初值的元素,值为0值('\0')
char str[6] = {"hel"};
char str[6] = "hel";
char str[6] = {0};
3.默认初始化:
char str[] = {'h', 'e', 'l', 'l', 'o', '\0'};
char str[] = {"hello"};
char str[] = "hello";
char str[] = {'h', 'e', 'l', 'l', 'o'};
char str[32] = {0};
4.数组的存储:
字符型数组所占字节 == 元素个数
1.连续性
2.有序性
5.字符串的输入和输出:
printf("str = %s\n", str);
puts(str);
scanf("%s", str);
gets(str);
6.字符串常见的操作函数:
1.strlen:获得字符串的长度(字符串开头到离开头最近的\0之 间的字符个数,不包含\0字符)
"hello world": 长度 11
注:
1.strlen获得字符串的长度
2.sizeof获得字符串或数组在内存中所占字节数
day7(2024.1.22)
2、 strcpy
字符串的拷贝
注:目的空间一定要足够大存放的下源字符串
3、strcat
字符串的拼接,在第一个字符串末尾拼接上第二个字符串
注:第一个字符串要空间足够大
4、strcmp
字符串的比较
如果两个字符串相同,结果为0
如果第一个字符串 > 第二个字符串,结果为 > 0 的值
如果第一个字符串 < 第二个字符串,结果为 < 0 的值
两个字符串比较,从左到右每个字符进行比较,不相同的字符,谁的ASCII码值大,字符串就大
二维字符型数组
1.定义
数据类型 数组名[行数][列数];
2.存储
1.连续性
2.有序性
3.初始化
局部初始化:
2、函数
特性:
1.避免重复性代码
2.程序代码模块化
1.函数的定义:
函数类型 函数名(数据类型1 形式参数1, 数据类型2 形式参数2, ...)
{
函数体;
return 返回值;
}
函数类型:函数运行结果的类型
int float double char void
1.函数类型如果不写,默认为int类型
2.函数返回值缺省,应该写void类型
3.函数类型应该与返回值的类型一致,如果不一致以函数类型为准
函数名:
1.由字母、数字和下划线构成,不能以数字开头
2.不要与C语言关键字重名
3.最好不要与C语言库函数重名
4.最好见名知意(动宾形式)
2.函数的调用:
函数名(实际参数1, 实际参数2, ...);
注:
1.实参个数必须与形参个数一一对应
2.实参可以是常量、变量、或者表达式
3.函数调用时会发生实参向形参空间的拷贝,实参将值拷贝给形参(一一对应),实参类型与形参类型不一致时,将实参类型默认转换为形参类型
3.函数的声明
定义: 定义需要为变量开辟空间
声明: 该变量是存在的,不需要开辟空间
如果被调函数的定义在主调函数的下方,需要对被调函数声明一下
今日完成
day8(2024.1.23)
1.函数定义:
函数类型 函数名(数据类型1 形参1, 数据类型2 形参2, ...)
{
函数体;
return 返回值;
}
2.函数的调用
函数名(实参1, 实参2, ...);
3.函数的声明
4.变量作用域和生存周期:
1.作用域:
变量能够使用的范围
变量作用域默认为离定义该变量最近的大括号内
局部变量:变量作用域在某个大括号范围内
全局变量:变量作用域在整个文件中
全局变量都在所有函数的外面定义
2.生存周期:
变量从空间被开辟到被回收的整个过程称为生存周期
存储类型 数据类型 变量名;
1.auto(默认)
自动型变量、局部变量
将变量存放在栈区:
1.未经初始化值为随机值
2.执行到变量定义时开辟空间
3.执行到超过变量作用域范围回收变量空间
2.register
寄存器变量
寄存器存满了,会将变量存放到栈区(等价于auto类型)
3.extern
外部变量
extern int a;
声明一个变量为外部存储的变量
4.static
静态变量
将变量存放在数据区中:
1.未经初始化值为0值
2.编译时开辟空间,程序运行时加载该空间
3.程序结束时回收空间
static作用:
1.将变量定义为静态变量
2.可以将变量的作用域限定到本文件中
3.可以防止多文件全局变量名冲突
5.递归函数:
函数定义时调用函数本身
1.递归一定要有结束条件
2.避免深层次的递归
6.函数的传参:
1.赋值传递(复制传递)
实参将值拷贝一份给形参,形参是实参的副本,形参的值发生改变不会影响实参
函数体内部想使用函数体外部变量值的时候使用复制传递
2.全局变量传递
7.数组传递:
int a[5];
1.形式一:
int Fun(int array[5]);
2.形式二:
int Fun(int array[], int len);
注意:
数组传递时,函数体内部形参操作的数组和实参操作的数组为同一数组
day9(2024.1.24)
1.宏:
1.宏是代码的替换,中间不进行任何数据计算的操作
2.能加括号就加括号,不好吝啬小括号
不带参宏
带参宏(宏函数)
带参宏与函数的区别:
1.函数有参数类型、返回值、调用的概念
2.带参宏只是代码的替换
3.带参宏会让程序代码体积增大
4.函数不会让程序代码体积增大
5.函数传参会占用资源开销,增大程序运行时间
6.带参宏没有传参调用的过程,程序运行速度更快
带参宏:
用于封装比较简单的模块代码
3、指针
1.指针:
1.代码更加简洁高效
2.指针可以直接访问内存
3.指针可以操作硬件
2.概念:
1.地址:用来区分内存中不同字节的编号
2.指针:指针就是地址,地址就是指针,指针多了指向的概念
3.指针变量:是一个变量, 变量里面存放了一个指针(地址), 称为指针变量,
使用指针变量等价于使用其内部存放的指针,所以我们有时也把指针变量简称为指针
3.运算符:
&:只能操作左值(变量)
*:只能操作指针类型
int Num;
&Num:
值:获得Num变量在内存空间中的首地址
类型:int * (&有类型升级效果)
*&Num:
值:获得&Num指针对应空间中的值
类型:int (*有类型降级效果)
注意:
作为左值,将右值放入左边指针指向的空间
作为右值,直接取指针指向空间中的值
*&Num = 100;
*&Num;
4.指针变量的定义:
int *p;
int *q;
int *p, *q;
野指针:未经初始化的指针,指向已经被释放过空间的指针称为野指针
指针一定记得初始化
空指针:NULL 0x0, 指向内存地址为0x0的指针
int *p = NULL;
所有的指针变量占8个字节
直接访问:通过变量的变量名访问内存空间
间接访问:通过变量在内存中的地址访问变量空间
5.指针常见操作:
int *p = NULL;
int *q = NULL;
int a = 100;
int b = 200;
p = &a;
q = &b;
1.p = &b
2.*p = b
3.p = q
4.*p = *q
6.指针的算数运算:
int *p = NULL;
int *q = NULL;
+
-
++
--
p++:向内存高地址偏移指向数据类型大小个字节空间
p--:向内存低地址偏移指向数据类型大小个字节空间
p - q:
int *p = NULL;
int a = 100;
p = &a;
*p++; 等价于: *p;
p++;
(*p)++; 等价于: *p
(*p) ++
7.指针作为函数参数:
1.复制传递(赋值传递)
实参将值传递给形参,形参是实参的副本,形参改变不会影响实参
函数体内想使用函数体外部变量值的时候使用复制传递
2.地址传递
实参地址传递给形参,形参就是实参的指针,可以通过对形参取*改变实参的值
函数体内想修改函数体外部变量值的时候使用地址传递
day10(2024.1.25)
1.数组和指针的关系:
1.一维数组和指针的关系:
int a[5] = {1, 2, 3, 4, 5};
int *p = NULL;
p = &a[0];
p = a;
数组的数组名a是指向数组第一个元素的一个指针常量
a == &a[0]
a 的类型可以理解为 int *
有两种情况除外:
1.sizeof运算时
2.&运算符
&int * == int **
&int [5] == int (*)[5]
访问下标为n的数组元素的方式:
a[n] == *(a+n) == *(p+n) == p[n]
2.一维数组传参:
int a[5] = {0};
int Fun(int parray[5]);
int Fun(int parray[], int len);
int Fun(int *parray, int len);
3.一维字符型数组传参:
char str[32] = {0};
int Fun(char *pstr, int len);
4.字符串传参:
char str[32] = {"hello world"};
int Fun(char *pstr);
注意:
字符串遍历:
char str[32] = {"hello world"};
char *pstr = NULL;
pstr = str;
while (*pstr != '\0')
{
pstr++;
}
2.数组指针和指针数组:
数组指针是指针,指针指向一个数组
指针数组时数组,数组每个元素都是指针类型
int *a[5] = {NULL};
定义一个指针数组,数组占40个字节空间,每个元素为int *型,共5个元素
int (*a)[5] = NULL;
定义一个数组指针变量,占8个字节空间,指针指向了一个int类型5个元素的数组
1.数组指针:
int a[5];
&a:获得数组的首地址,其类型为int (*)[5]
注意:
对一维数组数组名&,值不变,类型升级为数组指针类型
对数组指针*,值不变,类型降级成为指向数组第一个元素的指针
day11(2024.1.26)
1.指针数组:
int *a[5];
char *str[5];
指针数组主要用来操作字符串数组,通过将指针数组的每个元素存放字符串的首地址实现对多个字符串的操作
二维数组主要用来存储字符串数组,通过每行存储一个字符串,多行存储多个字符串所组成的数组
2.指针和二维数组的关系:
int a[2][3] = {0};
int *p = NULL;
int (*q)[3] = NULL;
p = &a[0][0];
p = a[0];
p = *a;
q = a;
二维数组的数组名是指向数组第一行元素的数组指针
访问二维数组第m行第n的方式:
a[m][n]
*(a[m]+n)
*(*(a+m)+n)
*(p+m*N+n)
*(*(q+m)+n)
*(q[m]+n)
q[m][n]
二维数组传参:
int a[2][3] = {0};
int Fun(int (*parray)[3], int len);
char str[5][32] = {0};
int Fun(char (*pstr)[32], int len);
3.二级指针:
1.指针数组传参时
int *a[5] = {NULL};
a: int **
char *pstr[5] = {"hello", "world", "how", "are", "you"};
int Fun(char **ppstr, int len);
day12(2024.1.27)
1.const指针
const 关键字 常量(只读) readonly
1.const int *p;2.int const *p;3.int *const p;
4.const int *const p;5.int const *const p;
1和2是等价的
const修饰 *p,指针变量p的值可以改变,但不能利用指针修改指向空间中的值
3
const修饰 p,指针变量p的值不能改变,但可以利用指针变量p修改指向空间中的值
一定要对指针初始化
4和5是等价的
const修饰p和*p,指针变量p的值不能改变,也不能利用*p改变直系那个空间中的值
一定要初始化
2.void
void a;
高精度
int *p;
double *p;
char *p;
低精度
void *p;
p保存内存地址
int *、double *、char * 转换为 void * 需要强制类型转换
void *转换为int *、double *、char *不需要强制类型转换
strcpy 拷贝字符串
memcpy 拷贝一段内存空间
3.函数指针和指针函数:
指针函数:是函数,函数的返回值类型是指针类型
函数指针:是指针,指针指向了函数
1.指针函数:
局部变量的地址不能返回,因为局部变量会随函数作用域结束被回收,虽然能够获得返回的地址,但地址对应的空间已经被回收过了
将函数的返回值作为下一个函数的参数
2.函数指针:
int (*Fun)(int a, int b) = NULL;
定义函数指针变量Fun,占8个字节空间,指向int返回值有2个int参数的函数
4.C语言中二级指针使用场景:
1.指针数组传参时:
char *str[5];
int Fun(char **ppstr, int len);
2.函数体内想修改函数体外指针变量值的时候,要传递指针变量的地址即二级指针
char *p = NULL;
int Fun(char **pp);
三、难点知识
1、构造数据类型
数据类型:
1.基本数据类型:
int
double
char
2.构造数据类型:
1.struct 结构体
2.union 共用体
3.enum 枚举
1.结构体:
1.结构体类型的定义
struct 结构体名
{
数据类型1 成员变量1;
数据类型2 成员变量2;
数据类型3 成员变量3;
..
};
2.结构体变量的定义
数据类型 变量名;
数据类型 *指针变量名;
3.结构体变量的初始化:
1.全部初始化:
struct student s = {"zhangsan", 'm', 19, 100};
2.局部初始化:
struct student stu = {
.name = "lisi",
.score = 90,
};
4.结构体成员变量的访问:
. :结构体变量类型访问成员变量使用 .
-> :结构体指针类型访问成员变量使用 ->
整体类型由成员变量类型决定
day13(2024.1.29)
1.结构体:
1.结构体类型定义
2.结构体变量的定义
3.结构体元素的访问
4.结构体的存储:
内存对齐:
char 按照1字节对齐
short 按照2字节对齐
int 按照4字节对齐
double 按照4字节对齐
结构体整体的大小必须为最大基本类型长度的整数倍
5.结构体作为函数参数
练习:定义一个学生的类型,封装一个函数GetStuInfo获得学生信息放入结构体中,
再封装一个函数PutStuInfo打印学生信息
struct student
{
char name[32];
char sex;
int age;
int score;
};
6.结构体数组:
struct student s[5];
2.共用体:
union 共用体名
{
数据类型1 成员变量1;
数据类型2 成员变量2;
数据类型3 成员变量3;
...
};
共用体所有成员变量共享同一片空间
内存大小端:
1.内存小端:
内存低地址存放低数据位
内存高地址存放高数据位
2.内存大端:
内存低地址存放高数据位
内存高地址存放低数据位
3.枚举:
enum 枚举类型名
{
枚举常量1,
枚举常量2,
枚举常量3,
..
};
1.枚举类型一般说明将来变量的值在给定的常量中选择其一作为它的值
2.枚举常量的值总是前一个枚举常量的值+1,第一个默认为0值
3.枚举常量默认为int类型,可以直接在程序中使用
4.位运算运算符:
& 按位与
| 按位或
^ 按位异或 相同为0 相异为1
~ 按位取反
>> 右移 右移n位 等价于 让该数 / 2^n
<< 左移 左移n位 等价于 让该数 * 2^n
与0得0
或1置1
按位异或实现数据交换:
a = a ^ b;
b = a ^ b;
a = a ^ b;
将变量的第n位置0:
num = num & ~(1 << n)
将变量的第n位置1:
num = num | (1 << n)
day14(2024.1.30)
2、链表
======================================================================================================================================================
链表:
作业:
1.封装函数在链表中实现尾插法
int InsertTailLinkList(LinkNode *pHead, DataType TmpData);
2.从终端接收一个字符串,将字符串倒置后输出
"how are you"
"you are how"
3、内存管理
1.malloc
void *malloc(size_t size);
功能:
申请堆区空间
参数:
size:申请堆区空间的大小
返回值:
返回获得的空间的首地址
失败返回NULL
2.free
void free(void *ptr);
功能:
释放堆区空间
注意:
1.free只能释放堆区空间
2.一个空间只能被free一次,多次free程序会崩溃
3.malloc需要和free搭配使用,如果只有malloc没有free会内存泄露
练习:要求申请堆区空间,将"hello world"存放到堆区空间,完成打印
2.内存溢出:
内存溢出也称为内存越界
3.内存泄露:
程序中malloc的空间没有被释放
4.内存碎片:
由于频繁申请和释放,导致连续的空间分散成一些小的碎片空间,当malloc超过碎片空间时,则无法获得该空间(空间不连续),将这样的空间称为内存碎片
day15(2024.1.31)
第三步穿插单片机内容
第四步软件编程
1.IO编程
IO(文件)
文件操作:
文本文件,mp3,jpeg,png ,mp4,avi
文件:一组相关数据的有序集合
文件名:这组相关数据的一个名称
b c d - l p s
b -- block -- 块设备文件 --- 硬盘(存储设备)
c -- character -- 字符设备文件 --- 鼠标
d -- directory -- 目录文件
- -- regular -- 普通文件
l -- link -- 软连接文件 --- 类似windows的快捷方式
p -- pipe -- 管道文件 --- 实现操作系统中 进程间的一些 信息交换(通信)
s -- socket -- 套接字文件 --- 网络的时候 (进程间的通信)
文件操作三步骤:
1.打开
2.读写
3.关闭
Linux提供的两种文件操作方式:
文件编程:
1.标准IO --- 库函数 -------标准c库函数,
2.文件IO --- 系统调用-------Linux内核为用户提供的函数接口
系统调用:Linux内核为用户提供的函数接口
库函数:标准c库函数, 对Linux内核中系统调用的封装
标准IO库:
1.标准io的概念
1975 Dennis Ritchie编写, IO库,
从C语言的标准,ANSI c
IO input output
I: 键盘是标准输入设备 ====》默认输入就是指键盘 /dev/input
O: 显示器是标准输出设备 ==》默认输出就是指显示器
Linux操作系统当中IO都是对文件的操作
C一部分,任何支持标准C的系统都可使用标准IO实现文件存储
标准IO在UNIX上是对文件IO的封装
一般都是对普通文件操作是一种有缓存的IO 在文件IO和用户程序之间,
加入缓冲区,可以有效减少系统调用的次数,节省系统IO调度资源
2.功能:
用来操作普通文件
普通文件:
1.ASCII码文件(代码、文本文件)
2.二进制文件(音视频、图片)
ASCII码文件是一种特殊的二进制文件
3.操作方法
1.打开 -- fopen //FILE open
2.读写 --
fgetc / fputc 读出、写入单个字符
fgets / fputs 读出、写入一行字符
fread / fwrite 读出、写入指定长度数据
3.关闭 -- fclose
man手册
标准man手册分为8个章节:
man 1 用户命令
man 2 系统调用
man 3 c函数库调用
man 4 设备文件和特殊文件
man 5 配置文件和格式
man 6 游戏相关
man 7 杂项,总述
man 8 管理类命令
4.API函数接口
1.fopen
#include <stdio.h>
FILE* fopen(const char *path, const char *mode);
功能:
流打开函数 (打开文件,并关联到一个流)
参数:
@path --要打开的文件的文件名(字符串形式)
@mode --打开文件的操作模式
r ---打开文件做读操作
注意:
文件必须存在
r+ 打开文件做读写操作
注意:
文件必须存在
w 打开文件做写操作
注意:
如果文件已经存在,则会将文件清空为0
如果文件不存在,则会创建一个新文件。
w+ 打开文件做读写操作
注意:
如果文件已经存在,则会将文件清空为0
如果文件不存在,则会创建一个新文件。
a 打开文件做写操作
注意:
如果文件已经存在,则在文件末尾进行写入
如果文件不存在,则会创建一个新文件。
a+ 打开文件做读写操作
注意:
如果文件已经存在,则在文件末尾进行写入
如果文件不存在,则会创建一个新文件。
返回值:
成功 FILE * (文件流指针)
失败 NULL 并且 设置 errno 表明错误原因
流:
数据从文件当中流入和流出所体现出来的字节流叫做流
FILE 结构定义的对象(FILE * 指向的对象)称之为流对象,FILE *叫文件流指针。
标准IO将对文件的操作转换成了对"流"的操作。
所以,标准IO的文件相关的函数都是围绕着 FILE进行的。
FILE * fp
FILE * fp;//流指针------关联一个文件
FILE * 实际上是指向了一块内存空间(缓存,fileno)
FILE *fp <->【FILE结构体[缓存,fileno]】//堆区(malloc)
2.fclose
int fclose(FILE *fp);
功能:
关闭文件流指针
参数:
fp:文件流指针
返回值:
成功返回0
失败返回EOF(-1)
注意:fopen操作完毕后使用fclose关闭,否则会产生内存泄漏
不要重复调用fclose
3.fputc
int fputc(int c, FILE *stream);
功能:
向流中写入一个字符
参数:
c:要写入的字符
stream:文件流指针
返回值:
成功返回 写入的字符ASCII码值
失败返回 EOF
练习:利用fputc实现在file.txt文件中写入hello world
//读
4.fgetc
int fgetc(FILE *stream);
功能:
从流中读取一个字符
参数:
stream:文件流指针
返回值:
成功返回读到字符的ASCII码值
读到文件末尾 返回 EOF
失败返回EOF EOF
操作系统在运行一个程序时,会默认打开三个流:
stdin FILE* 标准输入流 ---->键盘
stdout FILE* 标准输出流 ---->屏幕
stderr FILE* 标准出错流 ---->屏幕
gets scanf getchar -> stdin
puts printf putchar -> stdout
perror -> stderr
如果输入q 表示输入结束!
stdin --获取数据 --- fgetc
fp --保存数据 --- fputc(ch,fp); // fp --- fopen
等价:
ch = getchar();
ch = fgetc(stdin);
等价:
putchar(ch);
fputc(ch, stdout)
//面试题:
fgetc()/fputc()面试题:
如何用最短的代码实现用户任意输入并打印输出。
要求用fgetc/fputc函数实现。
while(1)
fputc(fgetc(stdin),stdout);
while(fputc(fgetc(stdin),stdout))
;
//注意点:
fgetc()函数的结束判断。
1、EOF 宏来判断 ===》系统预制的文件结束标记
end of file -1
c = fgetc(fp);
if(EOF == c) ///file end;
if(!feof(fp))
while(1)
{
c=fgetc(fp);
if(feof(fp))
break;
}
2、feof() 函数判断到文件末尾 // ferror()
int feof(FILE *stream);
功能:
判断当前参数stream的文件流指针是否到达文件结尾。
如果到达文件结尾则返回真,否则返回假
注意:
该操作一定要在一次IO操作之后判断(在一次读写操作后判断)。
参数:
stream 要判断结尾的文件流对象
返回值:成功到达结尾是 真
否则 是假
3.ferror()
int ferror(FILE *stream);
功能:
判断当前参数stream的文件流指针是否出错。
参数:
stream 要判断的文件流对象指针
返回值:出错 真
否则 假
缺点:
效率低下
5.fputs
int fputs(const char *s, FILE *stream);
功能:
向流中写入一行字符串
参数: s
要写的信息,一般是固定的字符串或者有数据的数组。
stream
要写入的目标文件流对象
返回值:成功 nonnegative number on success
失败 -1;
注:
不写入 '\0'
puts和fputs的区别:
1.fputs不会自动增减\n
2.puts 会多打印\n字符
6.fgets
//按行读写 按字符串读写
fgets -- char ---> string
fputs -- char ---> string
char *fgets(char *s, int size, FILE *stream);
功能:
从stream流对象关联的文件中获取size大小字节的文本数据
并存储到s对应的本地内存(栈区数组,堆区内存)
参数: s 要存储数据的本地内存(栈,堆)
size 要获取的数据长度,单位字节。
stream 要获取的目标文件流对象,
可以是stdin ,程序会阻塞等待
如果是普通文件fp 则指向文件第一行数据
返回值:成功 返回指向有效数据的首地址,一般等于s的地址
失败 或者 文件末尾 NULL;
注意:
1.fgets能读取一行就读取一行
2.fgets会读到n个数据,如果n个数据中存在\n字符则立即停止当
前的读取操作,如果没有\n,则读取n-1个字符,最后一个存储\0
3.fgets每行读取的数据后面都有一个\n,在\n后面存放一个\0
12345
buf[] = '1''2''3''4''5''\n''0'
4.gets是危险的,因为没有规范读到数据的上限
5.gets会去掉从终端读入的\n字符 //也读走了\n 但是将 \n 去掉了 \n --> '\0'