C++:week1:C语言基础

文章目录

    • (一) C语言概述
      • 1.预处理指令:宏定义、宏函数
      • 2.生成可执行程序的过程
      • 3.进程与虚拟内存空间
    • (二) 格式化输入输出
      • 1.变量及命名
      • 2.格式化输入输出、输入输出模型
        • (1)CPU、内存、外部设备的速度矛盾
        • (2)printf
        • (3)scanf
      • 3.代码即注释
      • 4.程序出错的原因、调试程序
      • 5.其他
        • (1)bool类型
        • (2)向0取整
        • (3)标识符
    • (三) 基本数据类型
      • 0.基本数据类型
      • 1.整数的编码
      • 2.ASCII码 (1字节,低7位)
      • 3.转义序列
      • 4.C语言中将char类型当作整数来操作
      • 5.和用户交互:读/写
        • (1)printf + %c 、scanf + %c
        • (2)putchar( c)、getchar()
      • 6.语言:惯用法
      • 7.类型转换
        • (1)隐式类型转换
        • (2)强制类型转换
      • 8.定义别名 typedef
      • 9.sizeof()
      • 10.void
    • (四) 运算符和表达式
      • 1.运算符
        • (1)属性
          • 运算符优先级
          • ②运算符的结合性
        • (2)分类
          • ①自增运算符
          • 位运算符
            • 1)移位运算符
            • 2)按位运算符
          • 按位运算的经典面试题
      • 2.表达式
        • (1)表达式的概述
        • (2)逗号表达式
    • (五) 语句
      • 1.表达式语句
      • 2.选择语句
        • (1)if、else
        • (2)switch、case
      • 3.循环语句
        • (1)while
        • (2)do while
        • (3)for
      • 4.跳转语句
      • 5.空语句
      • 6.复合语句
    • (六) 数组
      • 1.一维数组
        • (9)数组长度的宏
      • 2.多维数组
      • 3.常量数组
        • ①伪随机数
        • ②随机发牌 问题
    • (七) 函数
      • 1.函数的定义和调用
        • (1)函数的使用准则
          • ①判断素数
          • 掷骰子游戏
          • 德克萨斯扑克
        • (2)函数声明、函数定义、函数调用、函数指针
      • 2.函数的参数传递、实际参数 与 形式参数
        • (3)数组作为参数进行传递
      • 3.局部变量 和 外部变量
        • (1)局部变量
        • (2)全局变量
      • 4.return语句
      • 5.程序终止 exit()
      • 6.递归
        • (1)递归的概念
        • (2)循环不变式
        • (3)经典例题
          • ①斐波那契数列
          • 汉诺塔问题
          • 约瑟夫环问题
            • 1)每隔一个人,出局一个人
            • 2)每隔m个人,出局一个人
    • (八) 指针
      • 1.指针基础
        • (1)指针的声明
        • (2)指针的两个基本操作
        • (3)野指针
        • (4)指针的应用
          • ①指针作为参数进行传递
          • ②指针作为返回值
      • 2.指针与数组的关系
      • 3.指针的高级应用

(一) C语言概述

C语言:函数、结构体、指针

VS的使用:
①X86:
在这里插入图片描述


1.预处理指令:宏定义、宏函数

#:预处理指令
1.#include :头文件复制过来
2.宏定义: 是 文本替换
3.宏函数:也是文本替换用 实参 替换 形参
(1)注意事项
①左括号紧贴宏函数名,否则会当成宏定义
②参数要括起来,整个表达式也要括起来
(2)为什么要用宏函数?/ 使用宏函数的好处:
①宏函数快,仅仅是替换,避免了函数调用开销
应用场景:简短的、频繁调用的函数,写成宏函数,可以降低函数调用开销
②提供了一定的宏编程能力

在这里插入图片描述


2.生成可执行程序的过程

①预处理:执行预处理指令
②编译:将C语言翻译成汇编语言
③汇编:将汇编语言翻译成机器语言,生成目标文件
④链接:将多个目标文件和库文件链接在一起,生成可执行程序

.h头文件在预处理阶段用
库文件是.o文件,在链接阶段用


3.进程与虚拟内存空间

运行中的程序称为进程(Process),每个进程都有自己的虚拟内存空间
①内核:和内核交互
②栈:管理函数调用
③堆:存放动态数据
④数据段:存储数据,包括静态局部变量等
⑤代码段:存储指令、字符串字面值 (如 0、1、A、B、C)

在这里插入图片描述

程序 = 数据 + 指令
冯诺依曼型计算机,又叫存储程序型计算机


(二) 格式化输入输出

1.变量及命名

1.变量的三要素:变量名、类型、值 int a = 10;
(1)变量名:引用变量绑定的值
(2)类型:①限定了变量的取值范围:编码、长度 (所占内存大小) ②限定了值能进行的操作(运算方法)
(3)值:

2.变量的命名
①下划线命名法:current_page、name_and_address
②驼峰命名法:currentPage、nameAndAddress

3.用宏定义避免魔法数字,代码是给人看的,程序才是给机器运行的


2.格式化输入输出、输入输出模型

(1)CPU、内存、外部设备的速度矛盾

(1)CPU、内存:Cache、TLB
(2)CPU、设备:DMA
(3)内存、设备:①缓存(集合,替换机制) ②缓冲区(队列,内存中)

输入输出模型:键盘→stdin→应用程序→stdout→显示器


(2)printf

(1)格式化输出:print format (输出格式)

(2)作用/工作原理:打印格式串中的内容,并用后面表达式的值替换转换说明

(3)格式串:
①普通字符:原样打印
转换说明:占位符,用后面表达式的值填上占位符的值

(4)占位符的格式:%-m.pX
-:左对齐
m:最小字段长度
p:精度。%d是前面补0,%f是小数点后几位
X:字符数据要转换的类型

%-5.2f:-是左对齐(默认右对齐),5是占位5个位置,.2是小数点后2位,f是float类型
%.2d:.2是前面补0,用于打印月份、日期

输出:
%d:整数 int
%f:单精度浮点数 float
%lf:双精度浮点数 double
%c:字符型
%u:无符号整数
%x:十六进制数(小写字母)
%X:十六进制数(大写字母)
%p:指针、打印地址


(3)scanf

(1)格式化输入:scan format

(2)工作原理:拿stdin里面的数据和格式串进行匹配,从左到右依次匹配格式串中的每一项。如果有一项匹配不成功,scanf会立刻返回。scanf的返回值匹配成功的转换说明的个数

(3)格式串:
普通字符精确匹配
②空白字符:任意个空白字符 (包括0个)
③转换说明:
%d:忽略前置的空白字符(空格、\t、\v、\n),匹配一个十进制的有符号整数
%f:忽略前置的空白字符,匹配一个浮点数
%c:匹配一个字符,不会忽略前置的空白字符

Q:匹配第一个非空白字符,转换说明怎么写?
A:空格%c


3.代码即注释

1.代码即
①给变量起好听的名字
②留白,一个功能段写完要空行,不要写成一坨。


4.程序出错的原因、调试程序

详情请见:https://blog.csdn.net/Edward1027/article/details/135511540

1.程序出错的原因:
(1)编译错误:语法错误
(2)链接错误:①没有包含对应的头文件 ②函数名写错了
(3)运行时错误:逻辑错误,即BUG

2.调试程序:
①打断点、取消断点
②逐过程:遇到函数调用,执行整个函数
③逐语句:进入到自定义的函数中
④继续:运行到逻辑上的下一个断点
⑤跳出:执行完该被调函数,返回主调函数

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


5.其他

(1)bool类型

#include <stdbool.h>
可以在C语言中使用bool类型

C语言中,%运算符和数学上的mod不一样
C语言中,余数和符号和被除数的符号一致。


(2)向0取整

向0取整。正数向下取整,负数向上取整。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>int main(void){//向0取整。正数向下取整,负数向上取整int i;float f1 = -0.5;i = f1;printf("i = %d\n", i); //0float f2 = -1.5;i = f2;printf("i = %d\n", i); //-1return 0;
}

(3)标识符

字符、数字、下划线


(三) 基本数据类型

0.基本数据类型

①整型 int
②浮点型 float、double
③字符型 char


1.整数的编码

1.无符号整数(二进制编码)
(1)无符号整数求值
在这里插入图片描述


2.有符号整数(补码)
(1)有符号整数求值
在这里插入图片描述

(2)性质:
①补码 11111…11111 的值为 -1
②补码最高位为1,则值一定为负数
③ X + (-X) = 1000…0000

从右向左找到第一个1,此1左边按位取反



例题1:
有符号整数 1101 0100(2),求它的相反数的二进制表示

思路:凑两数相加后为1000 0000。从右向左找到第一个1,此1左边按位取反
答案:0010 1110(2)



(3)为什么计算机采用补码存储有符号整数?
原因:用加法器来实现减法运算,减少硬件成本,代替了减法器。 a - b = a + (-b)


2.ASCII码 (1字节,低7位)

①0-31、127 是 控制字符
②空字符:0
空格:32
字符 `0`:48 【48-57是数字】
A:65 【65-90是大写字母】
a:97 【97-122是小写字母】(小写字母比大写字母大32)

在这里插入图片描述


3.转义序列

(1)字符转移序列
\n 换行
\r 回车
\t 水平制表符
\\ 反斜杠
\' 单引号
\" 双引号

在这里插入图片描述


(2)数字转移序列
①八进制转义序列:反斜杠开头,接1-3个八进制数值
②十六进制转义序列:以 \x开头,后接十六进制数字

int main(void){printf("%c\n",'A');printf("%c\n",'\101'; //八进制转义序列printf("%c\n",'\x41'); //十六进制转义序列return 0;
}

4.C语言中将char类型当作整数来操作

大小写转换,大写转小写:+32,小写转大写 -32

在这里插入图片描述


6.字符处理函数
扩展了字符类型支持的操作
在这里插入图片描述
在这里插入图片描述


5.和用户交互:读/写

(1)printf + %c 、scanf + %c

%c:匹配一个字符 (不忽略空白字符)

scanf("%c %c",&c1,&c2); //注意%c之间要有空格,匹配空白字符。跳过空白字符,读取下一个非空白字符

(2)putchar( c)、getchar()

putchar( c)、getchar() 更高效,读写一个字符 (字符数据)

char c = 'A';
putchar(c);
char c = getchar();

6.语言:惯用法

1.基本语法

2.惯用法 (代码的惯用法,类似汉语的成语,很不错)
①getchar的惯用法:跳过该行剩余的字符

while(getchar() != '\n');

在这里插入图片描述

3.设计模式


7.类型转换

(1)隐式类型转换

①整数提升 (低于int的转换为int,如char、short)

②值的表示范围,表示范围小的会向表示范围大的类型转换,这样没有数据丢失
在这里插入图片描述

③同一转换等级,有符号转换为无符号
在这里插入图片描述

避免有符号数和无符号数一起运算,比如有符号数-1可能变成无符号数的最大值
在这里插入图片描述


(2)强制类型转换

作用:
①求浮点数的小鼠部分
②显式地强调这里有类型转换
③精准控制类型的转换
④避免(int类型)溢出

在这里插入图片描述


8.定义别名 typedef

1.格式:

typedef 类型 别名;

2.作用:起别名的好处:
①可读性强
②可移植性强


在这里插入图片描述

在这里插入图片描述


9.sizeof()

作用:计算某一类型的值在内存中占用的大小

sizeof(数组),可以得到数组的大小

在这里插入图片描述


10.void

空类型 void

特点:没有值,不能作为变量的类型



(四) 运算符和表达式

1.运算符

(1)属性
运算符优先级

单目运算符(自增运算符、位运算符)、加减乘除、比较运算符、位运算符、赋值运算符、逗号运算符

== 优先级高于 &

在这里插入图片描述

同一个符号在不同上下文语境中的含义不同:
① a*b:双目运算符,乘号
② *p:单目运算符,解引用
③ int *p:声明语句中的单目运算符,指针


②运算符的结合性
int i;
float f;
f = i = 3.14f; //从右向左结合

(2)分类
①自增运算符

++i:表达式的值为i+1,副作用是i自增

i++:表达式的值为i,副作用是i自增

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>int main(void) {//++i:表达式的值i+1,副作用是i自增int i = 1;printf("i is %d\n", ++i);printf("i is %d\n", i);//i++:表达式的值i,副作用是i自增int i = 1;printf("i is %d\n", i++);printf("i is %d\n", i);return 0;
}

位运算符

位运算符:
  移位运算符: <<,>>
  按位运算符:~,&,|,^


1)移位运算符

移位运算不会改变变量的值
若想改变,加个等号,移位赋值运算符,<<=

①左移运算符
左移n位,相当于乘以2n

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>int main(void){unsigned u = 0xAB;printf("%u\n", u);      //171printf("%x\n", u << 2); //0x2acprintf("%u\n", u << 2); //171*4 = 684printf("u = %u\n", u);  //移位运算不会改变变量的值u <<= 2;printf("u = %u\n", u);  // <<= 移位赋值运算符才能改变变量的值return 0;
}

②右移
右移n位,相当于乘除以2n,向下取整

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>int main(void) {unsigned u = 0xAB;printf("%u\n", u);      //171printf("%X\n", u >> 2); //0x2Aprintf("%u\n", u >> 2); //171/4 = 42printf("u = %u\n", u);  //移位运算不会改变变量的值return 0;
}

在这里插入图片描述


2)按位运算符

按位运算符:按位与&、按位或|、按位异或^、按位取反~

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>int main(void) {unsigned short i, j, k;i = 21;            // 0000 0000 0001 0101j = 56;            // 0000 0000 0011 1000k = ~i;    //按位取反 1111 1111 1110 1010 ,即 65535-16-4-1 = 65514k = i & j; //按位与   0000 0000 0001 0000 ,即 16k = i | j; //按位或   0000 0000 0011 1101 , 即 61k = i ^ j; //按位异或 0000 0000 0010 1101 ,即 45return 0;
}

1.按位异或 ^

结合律:按位异或的结果 取决于1的个数是奇数个还是偶数个。奇数个为1,偶数个为0。~
在这里插入图片描述


按位运算的经典面试题

在这里插入图片描述

法1:数学逻辑
①有问题写法,对负奇数失效。因为 负奇数%2等于-1。%2可能会得到-1、0、1三种结果。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdbool.h>bool isOdd(int n) {if (n % 2 == 1)	return true; //负奇数%2 == -1else            return false;
}int main(void) {int i;scanf("%d", &i);int ret = isOdd(i);if (ret)   printf("Odd\n");else       printf("Even\n");return 0;
}

②正确写法:n % 2 != 0

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdbool.h>bool isOdd(int n) {return n % 2 != 0;
}int main(void) {int i;scanf("%d", &i);printf("该数字是%s", isOdd(i) ? "奇数" : "偶数");return 0;
}

法2:位运算:n & 0x1

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdbool.h>//奇数与1按位与得1,偶数与1按位与得0
bool isOdd(int n) {return n & 0x1; //与二进制1按位与,把个位截取下来
} int main(void) {int i;scanf("%d", &i);printf("该数字是%s", isOdd(i) ? "奇数" : "偶数");return 0;
}

在这里插入图片描述

①传统数学运算

bool isPowerof2(int n){// n > 0while(n % 2 == 0){n /= 2; }return n == 1;
}

②用位运算优化

bool isPowerof2(int n){// n > 0while((n & 0x1) == 0){n >>= 1;}return n == 1;
}

③直接一步到位: n & n-1

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdbool.h>bool isPowerof2(int n) {return (n & n - 1) == 0;
}int main(void){int i;scanf("%d", &i);printf("%d%s\n", i, isPowerof2 ? "是2的幂次" : "不是2的幂次");//bool ret = isPowerof2(i);//if (ret)  printf("%d是2的幂次\n", i);//else      printf("%d不是2的幂次\n", i);return 0;
}

在这里插入图片描述
①原始做法:从最低位开始截取,为0就左移,直到找到第一个不为0的位,就是最低有效位(last set bit)

int lastSetBit(int n){int x = 0x1;while((n & x) == 0){x <<= 1;} // (n & x) != 0return x;
}

②进阶做法:n与其相反数-n按位与,因为-n的二进制位是 n从最低有效位开始左边按位取反,故 n & -n就是最低有效位

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>int lastSetBit(int n){//int x = 0x1;//while((n & x) == 0){//	x <<= 1;//} // (n & x) != 0//return x;return n & -n;
}int main(void){int n;scanf("%d", &n);printf("%d\n", lastSetBit(n));return 0;
}

在这里插入图片描述


在这里插入图片描述
一对逆运算:加减、异或 异或

①加减法

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>int main(void){int a = 3, b = 4;a = a + b;b = a - b;a = a - b;printf("a = %d, b = %d\n", a, b); //a = 4, b= 3return 0;
}

②异或 与 异或 也是逆运算
在这里插入图片描述


在这里插入图片描述

异或

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>int findSingleNum(int nums[], int n) {int singleNum = 0;for (int i = 0; i < n; ++i) {singleNum ^= nums[i]; //连连看,异或消一对}return singleNum;
}int main(void) {int nums[5] = { 1,4,2,1,2 };printf("singleNum = %d\n", findSingleNum(nums, 5)); //单独的数为4return 0;
}

在这里插入图片描述

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>int main(void) {int nums[6] = { 1,2,1,3,2,5 };int xor = 0;for (int i = 0; i < 6; ++i) {xor ^= nums[i];}// xor = a ^ b (xor != 0),结果为单独的那两个数int lsb = xor & (-xor); //找到了a和b不同的最低有效位//根据这一位将所有元素分类 (根据该lsb将a和b分类)int a = 0, b = 0;for (int i = 0; i < 6; ++i) {if (nums[i] & lsb){//为1a ^= nums[i]; //连连看,异或消除一对}else{//为0b ^= nums[i]; //连连看,异或消除一对}}printf("%d %d\n", a, b);return 0;
}

2.表达式

(1)表达式的概述

1.C语言是一个非常看重表达式的语言
2.表达式的定义:计算某个值的公式
3.最简单的表达式:变量和常量,如:a、i、10、20
4.运算符的作用:连接表达式,创建更复杂的表达式。 如,a + b + c/d


(2)逗号表达式

(表达式1,表达式2,表达式3,… ,表达式n)
计算前n-1个表达式的值后丢弃,最后一个表达式的值作为整体的值



(五) 语句

在这里插入图片描述

1.表达式语句

expr


2.选择语句

(1)if、else
if( ){}else if( ){}else{}

(2)switch、case

1.级联式if else的弊端:
①可读性差
②效率低

2.switch case的优点:
可读性好
②当分支较多时,效率高

3.switch case的限制条件:
①switch后的表达式必须是整型 (int、char、枚举类型)
②switch后的表达式和case的标签,只能用 == 做比较,不能用大于小于

switch(表达式){case 4:情景4;break;case 3:情景3;break;case 2:情景2;break;case 1:情景1;break;case 0:情景0;break;	default:情景-1;break;	
}

4.注意事项:
①多个case可共用一组语句

switch(grade){
case 4: case 3: case 2: case 1:printf("Passing\n");break;
case 0:printf("Failing\n");break;	
default:printf("Illegal grade\n");;break;	
}

②警惕case穿透现象:没加break,会往下继续执行。因为case标签只会比较一次。
case穿透现象不是一种错误,而是一种机制。
如果下面的标签,需要上一条标签的代码,则上一条标签的break可以省略。但是要加注释,以免别人误以为你漏写了break。

case 4:情景4;/* break through */
case 3:情景3;break;

3.循环语句

(1)while
(2)do while
(3)for

1.for(exp1;exp2;exp3)
在这里插入图片描述

2.用for表示无限循环

for(  ;   ;  ){}

4.跳转语句

continue、break、goto、return

5.空语句

;

6.复合语句



(六) 数组

在这里插入图片描述

1.一维数组

1.数组的内存模型?
连续的一片内存空间,并且划分成大小相等的小空间。

2.为什么每个元素的大小要设置相同?或者为什么只能存储同一种类型的数据?
答案:为了可以 随机访问元素

i_addr = base_addr + i * sizeof(elem_type)

3.为什么数组的下标 (索引 index) 要从0开始?
答案:若下标从1开始,则计算地址的公式就变成了 i_addr = base_addr + (i-1) * sizeof(elem_type) ,每次都要做一次 i-1 的减法操作,耗能高,需要优化。

现代某些语言的数组下标会从1开始,做法是在开头多申请一块内存空间(下标0),空着不用。

4.刻板印象:数组效率 > 链表效率 的原因?
①空间利用率高 (链表需要存储指针,信息密度不如数组)
②空间局部性好 (数组是连续的,内存读数据的时候会把目标数据前后附近的数据也放在缓存中)

5.声明一个数组

int a[10] = {1,2,3};

在这里插入图片描述

6.数组的类型
int arr[4] 的类型为 int [4]

7.数组的初始化
{ } 是数组的 初始化式
初始化式的长度不可以为0,至少有一个元素。
若初始化式中只有一个元素0,则意思为将整个数组初始化为0

8.数组的操作:
①取下标 [ ]

arr[5];

②数组和for一对好伙伴,经常用for循环来处理数组


(9)数组长度的宏
#define SIZE(a) (sizeof(a)/sizeof(a[0]))

在这里插入图片描述


2.多维数组

1.结论:C语言只有一维数组,多维数组本质上也是一维数组。逻辑上是矩阵。
二维数组实际上就是 元素为一维数组 的数组。
n维数组实际上就是 元素维n-1维数组 的数组。

在这里插入图片描述

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#define SIZE(a) (sizeof(a)/sizeof(a[0]))int main(void) {int matrix[3][7];printf("SIZE(matrix) = %d\n", SIZE(matrix));        //3printf("SIZE(matrix[0]) = %d\n", SIZE(matrix[0]));  //7return 0;
}

2.二维数组的声明

int matrix[3][7];

标识符matrix,向右看,知道matrix类型是长度为3的数组。再向右看,其元素是长度为7的数组,向左看,是int类型的

3.二维数组的初始化

int matrix[3][7] = { 0 }; //将整个二维数组全部初始化为0
int matrix[3][7] = { {1,2,3,4},{2,2,3,4},{3,2,3,4}  }; //不要省略大括号

4.二维数组的操作:
①二维数组取下标

matrix[1];
matrix[1][5];

②二维数组和嵌套的for循环是一对好伙伴


3.常量数组

1.常量数组:前面加了const
特性:常量数组的元素值不能改变

const int arr[] = {1,2,3,4};
//arr[0] = 100;  //报错

2.用处:
①安全,存储静态数据 (在程序的运行过程中不会发生修改的数据)
②处理速度快,效率高 (编译器可以对常量数组做一些极致的优化)

3.结论:能用常量数组的地方尽量用常量数组

4.使用场景:静态数据 (程序运行过程中不会发生修改的数据),存储到常量数组中。如:扑克牌的花色、大小

const char suit[4] = {'S', 'H' ,'C','D'};
const char ranks[13] = {'2','3','4','5','6','7','8','9','T','J','Q','K','A'};

Spade:黑桃♠,Heart:红心♥,Club:梅花♣,Diamond:方块♦

①伪随机数
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>int main(void) {//伪随机数 rand()//seed -> n1 -> n2 -> n3 -> ...srand(time(NULL)); //设置随机种子,考虑以时间作为随机种子,则每次种子都不同printf("rand() = %d\n", rand());printf("rand() = %d\n", rand());printf("rand() = %d\n", rand());return 0;
}

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

自1970.1.1 00:00:00 GMT时间 到现在的秒数,时间戳


②随机发牌 问题

在这里插入图片描述

①花色、大小

const char suit[4] = {'S', 'H' ,'C','D'};
const char ranks[13] = {'2','3','4','5','6','7','8','9','T','J','Q','K','A'};

②随机发牌?
伪随机数

srand(time(NULL)); //设置随机种子,考虑以时间作为随机种子,则每次种子都不同printf("rand() = %d\n", rand());
printf("rand() = %d\n", rand());
printf("rand() = %d\n", rand());

③如何避免重复发牌?

bool in_hand [4][13] = { false };
//bool in_deck [4][13] = { true }; //这样是错误的!C语法规定只能一键全部初始化为0

④完整代码

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdbool.h>
#include <time.h>
#include <stdlib.h>int main(void) {int cards;printf("Enter number of cards in hand: ");scanf("%d", &cards);//随机发牌const char suits[] = { 'S','H','C','D'};const char rank[] = { '2','3','4','5','6','7','8','9','T','J','Q','K','A' };bool in_hand[4][13] = { false };srand(time(NULL));printf("Your hand: ");while (cards) {int i = rand() % 4;int j = rand() % 13;if (!in_hand[i][j]) {in_hand[i][j] = true;cards--;printf("%c%c ", suits[i], rank[j]);}} // cards == 0return 0;
}



(七) 函数

1.函数的定义和调用

function,一个函数应该可以实现一个功能。

(1)函数的使用准则

函数的功能越单一越好 (高内聚,低耦合),则复用的概率更高。函数的实现越高效越好。
C语言是面向过程(函数)的语言函数是C语言的基本构件组件 (以函数为单位思考问题)。C语言程序的本质就是函数之间的调用

C语言程序的本质就是函数之间的调用,举例子,掷骰子游戏。高内聚,低耦合。游戏换了,main函数不需要改,只改play_game()函数。

在这里插入图片描述


①判断素数

在这里插入图片描述
①判断素数

bool is_prime(int n) {int root = sqrt(n); //square rootfor (int i = 2; i <= root; i++) {if (n % i == 0) {return false;}}return true;
}

②完整代码

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdbool.h>
#include <math.h>bool is_prime(int n) {int root = sqrt(n); //square rootfor (int i = 2; i <= root; i++) {if (n % i == 0) {return false;}}return true;
}int main(void) {int n;printf("Enter a number: ");scanf("%d", &n);if (is_prime(n)) {printf("Prime\n");}else {printf("Not Prime\n");}return 0;
}

掷骰子游戏

在这里插入图片描述
在这里插入图片描述

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <time.h>int roll_dices(void) {int a = rand() % 6 + 1;int b = rand() % 6 + 1;printf("You rolled: %d\n", a + b);return a + b;
}bool play_game(void) {int nums = roll_dices();if (nums == 7 || nums == 11) {printf("You win!\n");return true;}else if (nums == 2 || nums == 3 || nums == 12) {printf("You lose!\n");return false;}else {int point = nums;printf("Your point is %d\n", point);while (1) {int current_roll = roll_dices();if (point == current_roll) {printf("You win!\n");return true;}else if (7 == current_roll) {printf("You lose!\n");return false;}else continue;}}
}int main(void) {int wins = 0, losses = 0;char again;srand(time(NULL)); //设置随机种子do {play_game() ? wins++ : losses++;printf("\nPlay again? (Y/y continue) ");//scanf(" %c", &again); //方法一:空格 + %c//scanf("%c", &again);  //方法二:getchar()//getchar(); //吃掉换行符scanf("%c", &again);while (getchar() != '\n') //getchar()惯用法:跳过该行剩余的字符;} while (again == 'y' || again == 'Y');printf("\nWins:%d, Loses:%d\n", wins, losses);return 0;
}

德克萨斯扑克

在这里插入图片描述

在这里插入图片描述

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>int num_in_suit[4];   //每个花色有几张
int num_in_rank[13];  //每个点数有几张bool straight, flush, four, three;
int pairs; //0,1,2void read_cards(void) {//初始化:清零for (int i = 0; i < 4; ++i) {num_in_suit[i] = 0;}for (int j = 0; j < 13; ++j) {num_in_rank[j] = 0;}bool inHand[4][13] = { false };int readCards = 0;while (readCards < 5) {bool badCard = false;//读点数printf("Enter a card: ");char c = getchar();int rank;switch (c) {case '0': exit(0);case '2': rank = 0;			   break;case '3': rank = 1;			   break;case '4': rank = 2;			   break;case '5': rank = 3;			   break;case '6': rank = 4;			   break;case '7': rank = 5;			   break;case '8': rank = 6;			   break;case '9': rank = 7;			   break;case 'T': case 't': rank = 8;  break;case 'J': case 'j': rank = 9;  break;case 'Q': case 'q': rank = 10; break;case 'K': case 'k': rank = 11; break;case 'A': case 'a': rank = 12; break;default:  badCard = true;	   break;}//读花色c = getchar();int suit;switch (c) {case 'D': case 'd': suit = 0; break;case 'C': case 'c': suit = 1; break;case 'H': case 'h': suit = 2; break;case 'S': case 's': suit = 3; break;default:  badCard = true;     break;}//处理多余的字符while ((c = getchar()) != '\n') {if (c != ' ' && c != '\t') {badCard = true;}}if (badCard) {printf("Bad card; ignored.\n");}else if (inHand[suit][rank]) {printf("Duplicate card; ignored.\n");}else {readCards++;inHand[suit][rank] = true;num_in_suit[suit]++;num_in_rank[rank]++;}}
}void analyze_hand(void) {//初始化straight = false, flush = false, four = false, three = false;pairs = 0; //0,1,2//判断是否为同花 flushfor (int i = 0; i < 5; ++i) {if (num_in_suit[i] == 5) {flush = true;}}//判断是否为顺子 straight//找到第一个下标为1的点数,往后判断5个,看是不是顺子for (int i = 0; i < 13; ++i) {bool strg = true; //先假定是真if (num_in_rank[i] == 1) {for (int j = i; j < i + 4; ++j) {if (num_in_rank[j] != 1) {strg = false;}}straight = strg;break;   //仅有5张牌,判断完毕直接退出循环}}//判断是否为三张、四张、两对、一对for (int i = 0; i < 13; ++i) {if (num_in_rank[i] == 4) {four = true;}else if (num_in_rank[i] == 3) {three = true;}else if (num_in_rank[i] == 2) {pairs++;}}
}void print_result(void) {if (straight && flush) {printf("Straight Flush\n");  //1.同花顺}else if (four) {printf("Four of a kind\n");			 //2.四条}else if (three && pairs == 1) {printf("Full House\n");      //3.葫芦}else if (flush) {printf("Flush\n");			 //4.同花}else if (straight) {			 printf("Straight\n");		 //5.顺子}else if (three) {printf("Three of a kind\n");			 //6.三条}else if (pairs == 2) {printf("Two pairs\n");		 //7.两对}else if (pairs == 1) {printf("One pair\n");		 //8.一对}else {printf("High card\n");		 //9.高牌}printf("\n");
}int main(void) {while (1) {read_cards();    //读取用户输入(一副手牌),内含exit(0)analyze_hand();  //分析手牌 (计算)		print_result();  //输出结果}return 0;
}

(2)函数声明、函数定义、函数调用、函数指针

函数的声明 void foo(int a);
函数的定义 void foo(int a){ ... }
函数调用 foo(3);
函数指针 foo&foo (编译器将这两个当作同一种东西)

函数指针是指向函数的指针变量。函数指针是函数的入口地址。


2.函数的参数传递、实际参数 与 形式参数

1.实参与形参
实参(argument):函数调用中的参数
形参(parameter):函数定义中的参数

2.C语言中的实参是值传递的。
实际参数的值,按位置,复制给形式参数,这个过程叫做 参数传递。

局限性:不能通过修改形参来改变实参,即在被调函数中无法修改主调函数中参数的值
解决方法:指针

在这里插入图片描述

(3)数组作为参数进行传递

1.数组名作为参数进行传递时,退化为指向数组第一个元素的指针,丢失了数组长度的信息 (需要额外传递一个数组长度参数)

2.为什么这样设计?
答案:有3个好处
①避免大量复制
②解决了“在被调函数中不能操作主调函数中的变量的值”的问题
③让函数调用更灵活

当然也有缺点:新手C程序员会分不清形参中的指针和数组

在这里插入图片描述
在这里插入图片描述


3.局部变量 和 外部变量

1.定义
①局部变量:定义在函数里面的变量
②外部变量(全局变量):定义在函数外面的变量

2.作用域:作用于编译时,变量可以被引用的本文范围
①局部变量的作用域:块作用域:从变量定义开始,到块的结束。即大括号内。
②外部变量(全局变量)的作用域:文件作用域:从定义开始,到文件的末尾。

3.存储期限:作用于运行时,变量绑定的值可以被引用的时间长度 (存储期限就是变量在程序运行过程中存在的时间长度)
按放到虚拟内存不同位置进行分类:
①存在数据段、代码段,静态存储期限:进程启动→进程销毁 (与天地同寿)
②存在栈上,自动存储期限:栈帧入栈→栈帧出栈
③存在堆上,动态存储期限:由程序员手动管理,malloc→free


(1)局部变量

1.定义:在函数体内声明的变量称为该函数的局部变量

2.局部变量的性质:自动存储期限、块作用域

3.static关键字:
static int i = 1; 只初始化一次,在程序装载阶段,存放在数据段。

在这里插入图片描述

4.静态局部变量:存储期限改为静态存储期限,但是作用域仍然不变,仍是块作用域。

5.静态局部变量的作用 / 使用场景:可以存储上一次函数调用的状态 (并只初始化一次)


(2)全局变量

1.定义:外部变量(全局变量)就是声明在任何函数体外的变量

2.全局变量(外部变量)的性质:静态存储期限文件作用域

3.外部变量的缺点:不利于定位bug

在这里插入图片描述


4.return语句


5.程序终止 exit()

exit(0);

头文件:<stdlib.h>


6.递归

(1)递归的概念

1.递归的概念
:将大问题分解成若干个子问题,子问题和大问题的求解方式一样,只是问题规模不同。

:将子问题的解,合并成大问题的解

递归的数学公理:数学归纳法

在这里插入图片描述

2.学会用递归的思维思考问题:找到递归结构,把大问题分解为小问题

3.递归三问
(1)找到问题的递归结构

(2)要不要使用递归求解?
①存在重复计算,不要用递归
②问题规模缩减的幅度很小,不推荐用递归,容易栈溢出(StackOverflow)

(3)如果以上两个问题都不存在,则可以大胆地使用递归。
考虑两点:
边界条件 (递归出口)
递归公式 (你只需要考虑这一层和下一层之间的关系)

栈空间是受限的:主线程8MB,其他线程2MB


(2)循环不变式

初始化、保持、终止


(3)经典例题

递归的例子:电影院伸手不见五指,问在第几排,把问题往前传递,到第一排后,再归回来。


①斐波那契数列

①低效的递归实现 (大量重复计算),时间复杂度为指数级O(2n)

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>//斐波那契数列: 0,1,1,2,3,5,8,13,21,34,55
long long int Fibonacci(int n) {if (n == 0 || n == 1)	return n;else   return Fibonacci(n - 1) + Fibonacci(n - 2);
}int main(void) {int n;scanf("%d", &n);printf("fibonacci(%d) = %lld", n,Fibonacci(n));return 0;
}

教训:不是所有具有递归结构的问题,都要用递归的方式求解
因为这种自顶向下的思维,可能存在大量重复计算,导致效率低下。


②自底向上思考:动态规划,顺序求解子问题,并将子问题的解保存起来,从而避免重复计算,
最终求解到大问题。动态规划可以将指数级复杂度的问题,转变为多项式级别的算法。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>long long int Fibonacci_2(int n) {long long int a = 0;long long int b = 1;//循环不变式:每次进入循环体之前都成立的调条件for (int i = 2; i <= n; ++i) {long long int t = a + b;a = b;b = t;}return b;
}int main(void) {int n;printf("请输入数字n: ");scanf("%d", &n);printf("Fibonacci(%d) = %lld\n", n, Fibonacci_2(n));return 0;
}

汉诺塔问题

在这里插入图片描述

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>void Hanoi(int n,char start,char middle,char target) {//边界条件if (n == 1) {printf("%c -> %c\n", start,target);return;}//递归公式//将上面n-1个盘子从start,经过target,移动到middle上Hanoi(n - 1, start, target, middle);//将最大的盘子从start直接移动到target上printf("%c -> %c\n", start, target);//将上面n-1个盘子从middle,经过start,移动到target上Hanoi(n - 1, middle, start, target);
}int main(void) {int n;scanf("%d", &n);//计算最少需要移动的次数//S(n) = S(n-1) + 1 + S(n-1)//S(n) + 1 = S(n-1) + 1 + S(n-1) + 1 = 2 * [S(n-1) + 1]//故S(n) + 1 为公比为2的等比数列,且首项为 S(1) + 1 = 1 + 1 = 2//故由等比数列公式 an = a1*2^(n-1),即 S(n) + 1 = 2*2^(n-1) = 2^n, 故 S(n) = 2^n -1printf("Total steps: %lld\n", (1LL << n) - 1);//打印移动步骤Hanoi(n, 'A', 'B', 'C');return 0;
}

在这里插入图片描述


约瑟夫环问题
1)每隔一个人,出局一个人

在这里插入图片描述

在这里插入图片描述


找到:边界条件、递归公式
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>int joseph(int n) {//边界条件if(n == 1 || n == 2){return 1;}//递归公式if(n & 0x1){ //n为奇数return 2 * joseph(n >> 1) + 1; //n >> 1,即 n/2}else{       //n为偶数return 2 * joseph(n >> 1) - 1;}
}int main(void) {printf("每隔一个人,出局一个人,请输入初始玩家人数: ");int n;scanf("%d", &n);printf("joseph(%d) = %d\n", n, joseph(n));return 0;
}

2)每隔m个人,出局一个人

在这里插入图片描述

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>int joseph(int n,int m) {//边界条件if (n == 1){return 0; //从0开始编号}//递归公式return (joseph(n-1,m) + m) % n;
}int main(void) {int n,m;printf("每隔一个人,出局一个人,请输入初始玩家人数: ");scanf("%d", &n);printf("请输入每隔多少人出局一个人: ");scanf("%d", &m);printf("joseph(%d,%d) = %d\n", n, m, joseph(n,m) + 1); //从0开始编号return 0;
}



(八) 指针

计算机最小寻址单位:字节
变量的地址:变量首字节的地址
指针:就是地址
指针变量:存储地址的变量


1.指针基础

(1)指针的声明

int *p
*说明了p是指针
int是指向对象的类型:①说明对象所占内存大小 ②如何解释那片内存空间 (说明了对象的类型)

变量名:p,类型:int *


(2)指针的两个基本操作

0.示例
i:直接访问,逻辑上访问内存一次
p:间接访问,逻辑上访问内存两次

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>int main(void) {int i = 1;int* p = &i;printf("*p = %d\n", *p);*p = 2; //*p 是 i 的别名,有读写权限printf("i = %d\n", i);return 0;
}

1.取地址运算符 &
在这里插入图片描述

2.解引用运算符 *
在这里插入图片描述


(3)野指针

1.野指针
野指针:不知道指向哪块数据

int* p;
int* q =0xABCD;

正确地给指针变量赋值

int *p = &i;
int *q = p;
p = NULL;

2.空指针
空指针(null pointer):不指向任何对象的指针,不指向任何有效的内存地址

3.指针变量的赋值 vs 指针变量指向对象的赋值

在这里插入图片描述

(4)指针的应用
①指针作为参数进行传递

在被调函数中修改主调函数中变量的值,解引用

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>void swap(int* p, int* q) {int temp = *p;*p = *q;*q = temp;
}int main(void) {int a = 3, b = 4;printf("a = %d, b = %d\n", a, b);swap(&a, &b);printf("a = %d, b = %d\n", a, b);return 0;
}

在这里插入图片描述


②指针作为返回值

教训:不要返回指向当前栈帧区域的指针 (因为返回以后,该栈帧就出栈了,变量被销毁)

在这里插入图片描述


2.指针与数组的关系


3.指针的高级应用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/829743.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

提示词优化的自动化探索:Automated Prompt Engineering

编者按&#xff1a; 作者在尝试教授母亲使用 LLM 完成工作任务时&#xff0c;意识到提示词的优化并不像想象中简单。提示词的自动优化对于经验并不丰富的提示词撰写者很有价值&#xff0c;他们没有足够的经验去调整和改进提供给模型的提示词&#xff0c;这引发了对自动化提示词…

C++学习之指针和引用

指针 指针是一个变量&#xff0c;其值为另一个变量的地址&#xff0c;即&#xff0c;内存位置的直接地址。就像其他变量或常量一样&#xff0c;您必须在使用指针存储其他变量地址之前&#xff0c;对其进行声明。指针变量声明的一般形式为&#xff1a; type *var-name; 在这里…

kotlin 编写一个简单的天气预报app (七)使用material design

一、优化思路 对之前的天气预报的app进行了优化&#xff0c;原先的天气预报程序逻辑是这样的。 使用text和button组合了一个输入城市&#xff0c;并请求openweathermap对应数据&#xff0c;并显示的功能。 但是搜索城市的时候&#xff0c;可能会有错误&#xff0c;比如大小写…

steam打不开没反应 steam客户端启动不了一直无响应的解决方法

steam打不开没反应 steam客户端启动不了一直无响应的解决方法 steam这个平台想必各位游戏爱好者们肯定不会陌生&#xff0c;作为全球最大的游戏服务平台&#xff0c;steam不仅为玩家们提供了全面的游戏服务&#xff0c;还经常给玩家们提供各种游戏优惠&#xff0c;并且每年四…

【综述】DSP处理器芯片

文章目录 TI DSP C2000系列 TMS320F28003X 典型应用 开发工具链 参考资料 TI DSP TI C2000系列 控制领域 TI C5000系列 通信领域 TI C6000系列 图像领域 C2000系列 第三代集成了C28浮点DSP内核&#xff0c;采用了65nm工艺&#xff08;上一代180nm&#xff09; 第四代正在…

无人零售与传统便利店的竞争优势

无人零售与传统便利店的竞争优势 成本控制 • 无人零售 显著降低了人力成本&#xff0c;无需支付店员薪资和相关福利&#xff0c;且通过智能化管理减少能源消耗与维护费用&#xff0c;尤其在高租金和高人流区域效益突出。 • 传统便利店 则承担较高的人员开支&#xff0c;…

chrome 查看版本安装路径、cmd命令行启动浏览器

chrome 查看版本安装路径 浏览器输入 chrome://version/cmd命令行启动浏览器 "C:\Program Files\Google\Chrome\Application\chrome.exe" www.baidu.com

恒峰智慧科技—高扬程水泵:解决远距离输水难题的新选择!

在森林消防领域&#xff0c;水泵是一个至关重要的设备。它的主要功能是将水源输送到火灾现场&#xff0c;为消防人员提供足够的水源进行灭火。然而&#xff0c;传统的水泵往往面临着距离限制的问题&#xff0c;这对于远距离输水来说是一个巨大的挑战。幸运的是&#xff0c;高扬…

Jenkins构建触发器-Git hook自动触发构建

系列文章目录 文章目录 系列文章目录前言 前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站&#xff0c;这篇文章男女通用&#xff0c;看懂了就去分享给你的码吧。 Jenkins是一个开源…

《苍穹外卖》Day10部分知识点记录

一、Spring Task 介绍 Spring Task是Spring框架提供的任务调度工具&#xff0c;可以按照约定的时间自动执行某个代码逻辑。 定位&#xff1a;定时任务框架 作用&#xff1a;定时自动执行某段Java代码 应用场景&#xff1a;只要是需要定时处理的场景都可以使用Spring Task …

2024腾讯游戏安全技术竞赛-机器学习赛道

决赛赛题链接https://gss.tencent.com/competition/2024/doc/2024%E8%85%BE%E8%AE%AF%E6%B8%B8%E6%88%8F%E5%AE%89%E5%85%A8%E6%8A%80%E6%9C%AF%E7%AB%9E%E8%B5%9B-%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0-%E5%86%B3%E8%B5%9B.zip 今年的题目是游戏跨语言恶意内容识别 ,题目比较…

技术速递|利用 Redis 使 AI 驱动的 .NET 应用程序更加一致和智能

作者&#xff1a;Catherine Wang 排版&#xff1a;Alan Wang Redis 是一种流行的内存数据存储&#xff0c;可用于解决构建和扩展智能应用程序的关键挑战。在本文中&#xff0c;你将了解如何使用 Redis 的 Azure 缓存来提高使用 Azure OpenAI 的应用程序的效率。 Redis 的 Azur…

西电超算使用方法-简易版

一、引言 西电超算不错&#xff0c;我很喜欢。本文仅供自己学习使用。 二、环境搭建 搭建环境需要有一些依赖库&#xff0c;但是其实西电超算说明手册并没有写的非常清楚。因此&#xff0c;这次实战演示一下&#xff0c;写一个运行sh文件脚本并提交作业。 1、选择GPU还是CP…

AI赋能分层模式,解构未来,智领风潮

​&#x1f308; 个人主页&#xff1a;danci_ &#x1f525; 系列专栏&#xff1a;《设计模式》 &#x1f4aa;&#x1f3fb; 制定明确可量化的目标&#xff0c;坚持默默的做事。 &#x1f680; 转载自热榜文章&#x1f525;&#xff1a;探索设计模式的魅力&#xff1a;AI赋能分…

人脸识别概念解析

目录 1. 概述 2. 人脸检测 3. 人脸跟踪 4. 质量评价 5. 活体检测 6. 特征提取 7. 人脸验证 8. 人脸辨识 1. 概述 人脸识别在我们的生活中随处可见&#xff0c;例如在大楼门禁系统中&#xff0c;它取代了传统的门禁卡或密码&#xff0c;提高了进出的便捷性和安全性。在商…

【Linux】基础指令

文章目录 基础指令1. pwd 指令2. cd 指令3. ls 指令4. touch 指令5. mkdir 指令6. rmdir 和 rm 指令7. man 指令8. cp 指令9. mv 指令10. cat 指令11. more 和 less 指令12. head 和 tail 指令13. date 指令14. cal 指令15. find 指令16. grep 指令18. zip 和 unzip 指令19. ta…

Jenkins - macOS 上安装

文章目录 关于 JenkinsmacOS 上安装 Jenkins方式一&#xff1a;brew方式二&#xff1a;tomcat Jenkins war 关于 Jenkins 官网上下载Jenkins并将其安装到持续集成服务器 https://jenkins.io/download/ macOS 上安装 Jenkins 现在本 macOS 上测试 https://www.jenkins.io/do…

[蓝桥杯2024]-PWN:fd解析(命令符转义,标准输出重定向)

查看保护 查看ida 这里有一次栈溢出&#xff0c;并且题目给了我们system函数。 这里的知识点没有那么复杂 完整exp&#xff1a; from pwn import* pprocess(./pwn) pop_rdi0x400933 info0x601090 system0x400778payloadb"ca\\t flag 1>&2" print(len(paylo…

消息服务应用1——java项目使用websocket

在当前微服务项目中&#xff0c;由于业务模块众多&#xff0c;消息服务的使用场景变得异常活跃。而WebSocket由于其自身的可靠性强&#xff0c;实时性好&#xff0c;带宽占用更小的优势&#xff0c;在实时通讯应用场景中独占鳌头&#xff0c;加上HTML5标准的普及流行&#xff0…

分类神经网络3:DenseNet模型复现

目录 DenseNet网络架构 DenseNet部分实现代码 DenseNet网络架构 论文原址&#xff1a;https://arxiv.org/pdf/1608.06993.pdf 稠密连接神经网络&#xff08;DenseNet&#xff09;实质上是ResNet的进阶模型&#xff08;了解ResNet模型请点击&#xff09;&#xff0c;二者均是…