一、为什么要学习这门技术
当前已迈入物联网时代,我们即将迎接移动5G+北斗导航大数据社会,而承载它们的技术基石便是我们的嵌入式技术,百花齐放百家争鸣,而STM32便是这嵌入式技术上耀耀发光的明珠,STM32家族以其优良的基因ARM内核和完善丰富的技术生态系统,使得我们开发变得简单容易,大大减少产品项目周期,更快投向市场。
这门60天STM32单片机开发实战线上特训班,是总结导师多年项目产品开发经验,已最贴产品开发实战的方式,一步一步带领大家,从易到难,模块化方式,按照零基础定位,分为基础,进阶,高级。课程以一个工程师角度讲解我们学习STM32技术所要掌握的各方面知识,包括编程框架,模块化封装,代码版本管理,源码变动比较等等课程特色。
二、什么是《STM32单片机开发实战特训班》
STM32单片机实战特训班是一群对单片机感兴趣的小伙伴,一起学习进步的学习圈,由凡亿教育名师团队带队,以独特的教学方式,在60天帮助大家梳理STM32单片机的学习方法、学习要点,并通过多个实战案例,有效帮助提升自己或转行者掌握STM32单片机,掌握关键技术,提升工程师的竞争力!
三、STM32单片机开发实战特训班授课形式介绍:
1、边做边学,现场分析,先从切身感受再深入原理
2、提出小项目,设计原理图,零散知识点融入具体实战项目
3、自己动手在万能板搭建模块电路,可以是最简单的流水灯,也可以是复杂的物联网系统
4、从原理图到元件选型,从硬件制作到软件代码编写
5、微信问题答疑,论坛分享作品,直播平台现场实战
【全新录播课程+老师手把手教学】(重要知识点反复观看)
【特训专属群在线答疑】(遇到难题不卡壳,老师在线答疑)
【班主任导师知识点总结与解析】(化零为整,不再是自学时零散知识点)
【作业批改】(知道自己错在哪?知道别人错在哪?问题总让人进步!)
四、特训班适合学员有哪些?
1、学习PCB画板学员想往软件编程方向多学一门技能
2、从事硬件Layout工程师想转型软件编程工作
3、刚毕业大学生想学精嵌入式技术便于找到合适工作
4、爱好DIY电子制作想系统深入学习编程技巧
五、60天STM32单片机开发实战特训班知识点有哪些?
1、自己动手设计制作项目,切身感受更深刻,获得感自豪感是驱动力源泉
2、硬件制作及软件编程上都采用模块化方式,化难为易,划繁为简
3、导师为从事多年产品开发工程师,集合多年经验总结,粹取其精华
六、学完能达到什么水平?
1、学员多学了一门技能,以后找工作有更多的选择
2、硬件工程师能在与软件工程师合作上更默契,转型也能胜任软件开发的工作
3、刚毕业的大学生学完,简历上也有更多的特长展示,满足企业对人才技能的需求
4、爱好DIY电子制作可以设计更高端的作品
七、课程学不会,我怎么办?
1)录播课知识点非常完整,可以反复回看,不担心遗忘。
2)老师总结知识点及答疑,知识点手把手过,不担心卡壳,有疑问都是当场解决。
3)延班保障,学员万一觉得自己学得不够好,可以直接免费参加下一期的课程,继续学习深造,直到学会为止。(为了学习效率,仅限延班一次)
《60天STM32单片机开发实战线上特训班》
第一期 正式招生报名
扫描二维码即可报名 ▼
60天从基础篇→进阶篇→高阶篇全面掌握STM32开发板开发
12大实战案例,从简单到复杂的系统开发
1、从数字电路到单片机
2、二进制十进制十六进制
5、算术运算和逻辑运算
6、一维数组和二维数组
1、if 和for等基本结构语句
2、变量的定义和初始化
3、全局变量和局部变量
5、带参函数和函数返回值
6、主程序入口main函数
3、新建第一个C语言工程
6、编译输出可执行文件
1、IAR仿真模式打印输出英文字符
2、IAR仿真模式打印输出中文字符
3、IAR仿真模式打印输出变量值
4、IAR仿真模式键盘输入值给到程序变量
1、STM32最小系统核心板
2、程序下载和调试用的仿真器
3、集成开发环境编写和编译代码
4、STM32参考手册和电路原理图
2、CubeMX图形配置时钟树
3、CubeMX图形配置调试接口
4、使用内部RC和外部晶振
5、配置I/O输入输出模式
6、生成IAR模版程序框架
7、生成Keil模版程序框架
8、不用写一行代码的点灯程序
八 信号时序分析利器逻辑分析仪
6、波形数据保存和输出
九 电子元件测量和制作
1、测量轻触按键KEY波形
2、测量发光二极管LED极性
4、万用表测量电阻阻值
5、测量有源蜂鸣器发声
6、测量PNP型和NPN型三极管
7、电烙铁和焊锡丝的使用
8、动手制作模块化开发板
2、单灯间隔闪烁之单片机的等待延时方式
3、单灯间隔闪烁之单片机的轮询计数方式
4、多灯不同频率闪烁之单片机串行和并发
5、按键单击检测之传统延时方式消抖
6、按键单击检测之状态机方式消抖
4、加载固件文件烧录编程
4、加载固件文件烧录编程
十三 综合应用案例
3、按键切换LED闪烁频率
5、数码管静态显示0-9
4、快速定位源文件函数和变量
8、快速查找和替换目标
1、利用仿真器打印输出英文字符
2、利用仿真器打印输出中文字符
3、利用仿真器打印输出变量值
4、调试技巧之设置断点
5、调试技巧之查看变量waitch
6、调试技巧之查看存储器memory
7、调试技巧之查看堆栈
1、CubeMX图形配置定时器初始化
3、TIMER中断回调函数
6、共极数码管动态扫描显示0-9
7、LED点阵屏动态扫描显示ASCII,汉字,图案
2、24位内核递减计数
5、操作系统滴答节拍器
1、CubeMX图形配置外部中断初始化
3、EXIT中断回调函数
3、ADC中断回调函数
4、旋钮电位器滤波检测
3、UART中断回调函数
4、发送和接收上位机串口数据
3、I2C阻塞方式发送和接受数据
4、I2C中断方式发送和接受数据
5、掉电记忆数据EEPROM驱动
3、SPI阻塞方式发送和接受数据
4、SPI中断方式发送和接受数据
5、大容量存储Flash驱动
1、手册查看内置Flash结构分布
3、移植HAL库例程擦除和页编程
4、临时数据保存内置Flash指定地址
2、释放CPU负担直接完成数据交换
3、模数转换ADC的DMA方式
5、同步串行总线接口I2C的DMA方式
6、串行外设总线接口SPI的DMA方式
十三 综合应用案例
1、无线遥控左右移动广告字幕
2、OLED液晶屏时钟显示
3、TFT LCD彩色液晶屏幕显示图片
一 C语言精华结构体和指针
1、数据封装的集合体struct
2、结构体占用字节大小
二 模块化抽象封装
6、C语言简易实现面向对象编程
三 单片机的纯模拟算法
1、渐明渐暗呼吸灯之模拟PWM
2、按键长按一次有效和长按多发
3、按键单击,双击,长按等一键复用
4、带按键旋转编码器正转和反转驱动
6、大容量存储Flash擦除编程之模拟SPI
四 分布式版本管理大神Git
3、给版本库添加文件git add
4、给本次快照输入变更信息git commit
8、程序员的天堂GitHub网站
五 独家自创状态机多任务框架
1、设备皆文件,功能皆算法
2、层次分明,底层驱动,上层应用
3、驱动任意裁剪,应用任意替换
4、前台处理紧急事件,后台处理轮询服务
5、设备划分驱动和应用,两者独立分离
6、应用之间通过全局数据共享消息传递
7、开辟裸机多任务并发处理的新思路
8、消息事件机制处理,熟悉数据结构先进先出FIFO
9、软件定时器函数回调,熟悉数据结构后进先出LIFO
2、程序镜像文件bin
3、中断向量表地址偏移
5、Flash解锁,擦除,编程
2、文件系统底层存储器读写驱动diskio
3、文件系统的常用API函数ff
4、新建txt文本文件并写入字符串
5、新建bin文件并写入16进制数据
3、读取U盘目录下的txt文件
4、读取U盘目录下的bin文件
5、U盘目录新建txt文件并写入文本数据
6、U盘目录新建bin文件并写入十六进制数据
九 上位机程序开发
2、新建命令行CMD程序工程
3、新建图形界面GUI程序工程
4、窗口界面的拖放绘制
5、窗口控件的属性和方法
7、上位机和单片机通讯联调
8、上位机程序打包发布
2、修改鼠标和键盘的设备描述符
4、STM32键盘向电脑文本框输入字符
5、STM32鼠标向电脑移动光标
3、用RAM作为存储介质的U盘
4、用ROM作为存储介质的U盘
5、用外置SPI Flash作为存储介质的U盘
6、电脑枚举弹出U盘设备并查看容存储量
7、STM32虚拟U盘存取文件
5、创建不同优先级任务
7、时间管理和内存管理
十四 综合实战项目
1、数码管数字时钟项目,可以菜单编辑时间日期和闹钟
2、带固件更新的点阵万年历项目,可以移动显示图形,汉字,菜单切换日期时间以及温度
3、ESP8466网络wifi和串口透传,熟悉物联网智能控制
空行:函数与函数之间空两行,类内部的函数之间空一行
一、函数名小写,可采用下划线加字母;类名单词第一个字母大写,采用驼峰式命名;
二、命名必须有意义,可识别性,不能重复;
长度:每行长度不能超过79,能够采用下划线隔开并另起一行
空格:逗号以后和操做符先后采用空格隔开;
import:不要一次导入多个不是一个类型的库;
手动转换参考:python
什么是慢日志? MySQL的慢查询日志是MySQL提供的一种日志记录,它用来记录在MySQL中响应时间超过阀值的语句,具体指运行时间超过long_query_time值的SQL,
# 注:查看当前配置信息:一、建立数据表时把固定长度的放在前面;
二、将固定数据放入内存: 例如:choice字段 (django中有用到,数字一、二、3…… 对应相应内容)
四、联合索引遵循最左前缀(从最左侧开始检索);
六、读写分离:两台服务器同步数据,利用数据库的主从分离(主 用于删除、修改、更新;从 用于查);
七、分库:当数据库中的表太多,将某些表分到不一样的数据库,例如:1W张表时,缺点是连表查询。
- 水平分表:将记录选择性地存放到对应的表中。避免一张表出现几百万条数据,缩短了一条sql的执行时间,例如聊天信息,会讲将每一个人的聊天记录分别存放到各自的记录表中,这样须要查询的时候,只须要从本身的记录表中查询便可;
- 垂直分表:拆分表,将经常使用的字段和不经常使用且数据量大的字段分别存放在两种表中。将一个表中不常常用到的,或者像text这种类型比较大的字段挪到一个表中,其余经常使用的(访问频繁的)字段单独一个表,两张表再经过外键关联起来。
九、加缓存:利用redis、memcache (经常使用数据放到缓存里,提升取数据的速度)
缘由:limit
的意思扫描知足条件的1000020行,扔掉前面的100w行,返回最后的20行。
一、子查询优化法:先找出第一条数据,而后大于等于这条数据的id就是要获取的数据:
从结果中能够得知,当偏移1000以上使用子查询法能够有效的提升性能。
二、倒排表优化法: 倒排表法相似创建索引,用一张表来维护页数,而后经过高效的链接获得数据。缺点是只适合数据数固定的状况,数据不能删除,维护页表困难。
一、redis不只支持简单的key_value类型,还支持字典(hash)、字符串、列表、集合、有序集合类型。
二、内存使用效率对比:使用简单的key-value存储的话,Memcached的内存利用率更高而若是Redis采用hash结构来作key-value存储,因为其组合式的压缩,其内存利用率会高于Memcached;
三、性能对比:因为Redis只使用单核,而Memcached可使用多核,因此平均每个核上Redis在存储小数据时比Memcached性能更高;而在100k以上的数据中,Memcached性能要高于Redis;
四、Redis虽然是基于内存的存储系统,可是它自己是支持内存数据的持久化的,并且提供两种主要的持久化策略:RDB快照和AOF日志;而memcached是不支持数据持久化操做的。
五、集群管理不一样,Memcached自己并不支持分布式,所以只能在客户端经过像一致性哈希这样的分布式算法来实现Memcached的分布式存储。
Redis默认支持16个数据库,能够经过配置databases来修改这一数字。客户端与Redis创建链接后会自动选择0号数据库,不过能够随时使用SELECT命令更换数据库。
Redis支持多个数据库,而且每一个数据库的数据是隔离的不能共享,而且基于单机才有,若是是集群就没有数据库的概念。
RDB是在某个时间点将数据写入一个临时文件,持久化结束后,用这个临时文件替换上次持久化的文件,达到数据恢复。
AOF:把全部命令保存起来,若是想到从新生成到redis,那么就要把命令从新执行一次。
相关知识:redis 内存数据集大小上升到必定大小的时候,就会施行数据淘汰策略(回收策略)。redis 提供 6种数据淘汰策略:
六、no-enviction(驱逐):禁止驱逐数据。
浏览器本质:socket客户端遵循Http协议
HTTP协议本质:经过\r\n
分割的规范+请求响应以后断开连接(无状态、短链接)
Http协议是创建在tcp之上的,是一种规范,它规范定了发送的数据的格式,然而这个数据格式是经过\r\n 进行分割的,请求头与请求体也是经过2个\r\n分割的,响应的时候,响应头与响应体也是经过\r\n分割,而且还规定已请求已响应就会断开连接,即短链接、无状态。
http1.1版本有keepalived的请求头,一次TCP链接中能够持续发送多份数据而不会断开链接,经过使用keep-alive机制,能够减小tcp链接创建次数。可是,keep-alive并非免费的午饭,长时间的tcp链接容易致使系统资源无效占用。配置不当的keep-alive,有时比重复利用链接带来的损失还更大。因此,正确地设置keep-alive
一、wsgi:就是socket服务端,用于接受用户请求,并进行初次封装,而后将请求交给web框架(flask、django);
二、中间件:帮助咱们对请求进行校验或在请求中添加其余相关数据,例如 csrf、request.session;
三、路由匹配:url和视图函数或类的映射表;
四、视图函数:在视图函数中进行业务逻辑的处理,可能涉及到ORM(数据库交互组件)、templates 渲染;
六、中间件:对响应的数据进行处理;
七、wsgi:将响应的内容发送给浏览器。
也是基于tcp的三次握手和四次挥手。
一、是真正的全双工方式,创建链接后客户端与服务器端是彻底平等的,能够互相主动请求。而HTTP长链接基于HTTP,是传统的客户端对服务器发起请求的模式,服务端永远是被动的;
二、HTTP长链接中,每次数据交换除了真正的数据部分外,服务器和客户端还要大量交换HTTP header,信息交换效率很低。Websocket协议经过第一个request创建了TCP链接以后,以后交换的数据都不须要发送 HTTP header就能交换数据,这显然和原有的HTTP协议有区别因此它须要对服务器和客户端都进行升级才能实现(主流浏览器都已支持HTML5)。此外还有
multiplexing(多路复用)、不一样的URL能够复用同一个WebSocket链接等功能。这些都是HTTP长链接不能作到的。
web通信的改进方式:
以频繁请求方式来保持客户端和服务端的同步,可是若服务端的数据无变化,会形成通讯低效。
当服务端没有数据更新的时候,链接会保持一段时间周期直到数据或者状态改变或者过时,依次减小无效的客户端和服务端的交互。且当服务端数据变动频繁的话,这种机制和定时轮询毫无区别。
轮询:经过定时器让程序每隔n秒执行一次操做。
跨域广义上指的是指一个域下的文档或脚本试图去请求另外一个域下的资源。
狭义上指的是浏览器的同源策略,即协议、域名、端口必须一致才算同源,才能互相访问,不然就算跨域且不能访问。
一般为了减轻web服务器的负载,咱们把js、css,img等静态资源分离到另外一台独立域名的服务器上,在html页面中再经过相应的标签从不一样域名下加载静态资源,而被浏览器容许,基于此原理,咱们能够经过动态建立script,再请求一个带参网址实现跨域通讯。
# 容许你的域名来获取个人数据
浏览器将CORS请求分红两类:简单请求和赋复杂请求。
简单请求(同时知足如下两大条件)
一、请求方法是如下三种方法之一:HEAD、GET、POST
二、HTTP的头信息不超出如下几种字段:
凡是不一样时知足上面两个条件,就属于非简单请求。
列举Http请求中常见的请求方式:
HEAD(相似于get请求,只不过返回的响应中没有具体的内容,用于获取报头)列举Http请求中的状态码:
1** 信息,服务器收到请求,须要请求者继续执行操做 2** 成功,操做被成功接收并处理 3** 重定向,须要进一步的操做以完成请求 4** 客户端错误,请求包含语法错误或没法完成请求 5** 服务器错误,服务器在处理请求的过程当中发生了错误
Http请求中常见的请求头:
flask是微型框架,内部组件就比较少了,可是有不少第三方组件来扩展它,好比说有那个wtform(与django的modelform相似,表单验证)、flask-sqlalchemy(操做数据库的)、flask-session、flask-migrate、flask-script、blinker可扩展强,第三方组件丰富。因此对他自己来讲有那种短小精悍的感受。
django和flask的共同点:两个框架都没有写socket,因此他们都是利用第三方模块wsgi。
二、请求管理不太同样:django是经过将请求封装成request对象,再经过参数传递,而flask是经过上下文管理机制。
Tornado是一个轻量级的Web框架,异步非阻塞 + 内置WebSocket功能。能够经过一个线程处理N个并发请求(处理IO)。内部组件:内部本身实现socket、路由系统、视图、模板、cookie、csrf。
做用:一、对用户请求的数据进行校验;二、生成HTML标签。
form对象是一个可迭代对象。
choice的数据若是从数据库获取可能会形成数据没法实时更新。
django的信号其实就是django内部为开发者预留的一些自定制功能的钩子。
只要在某个信号中注册了函数,那么django内部执行的过程当中就会自动触发注册在信号中的函数。
在数据库某些表中添加数据时,能够进行日志记录。
防止用户直接向服务端发起POST请求。
对全部的post请求作验证,将django生成的一串字符串发送给咱们,一种是从请求体发过来,一种是放在隐藏的标签里面用的是process_view
方案:先发送GET请求时,将token保存到:cookie、Form表单中(隐藏的input标签),
之后再发送请求时只要携带过来便可。
ContentType contenttype是django的一个组件(app), 为咱们找到django程序中全部app中的全部表并添加到记录中。 可使用他再加上表中的两个字段实现:一张表和N张表建立FK关系。 - 字段:表名称 - 字段:数据行ID 应用:路飞表结构优惠券和专题课和学位课关联。
指针简介 : 指针是保存变量地址的变量;
-- 增加阅读难度 : 指针 和 goto 语句会增加程序的理解难度, 容易出现错误;
-- 限定参数 : 函数不接收参数, 使用 void 作为参数, 如果传入参数, 编译器就会报错;
(原文两个作用名称写反了,doniexun注)
-- void参数 : C 语言中参数是void, 传入参数不会出错, C++中传入参数会出错, 因此这里我们统一规定, 如果函数没有参数, 就定义为void;
-- 通用数据类型 : void * 指针可以存放任意类型数据的地址, 任何数据类型的指针都可以赋值给 void * 通用类型指针;
-- 任意类型 : 如果 函数 的 参数 和 返回值 可以是任意类型, 就可以使用 void * 作为函数的 参数 或者 返回值;
-- 存放内容 : 存放程序运行中 动态分配 内存的数据;
-- 申请过程 : OS中有一个记录空闲内存地址的链表, 如果程序员申请内存, 就会找到空间大于申请内存大小的节点, 将该节点从空间内存链表中删除, 并分配该节点;
-- 剩余内存处理 : 系统会将多余的部分重新放回 空闲内存链表中;
-- 首地址记录大小 : 分配内存的首地址存放该堆的大小, 这样释放内存的时候才能正确执行;
-- 分配, 释放方式 : 编译器分配内存, 程序退出时系统自动释放内存;
-- 特点 : 全局变量 和 静态变量存储在一个区域, 初始化的两种变量 和 未初始化的 存储在不同区域, 但是两个区域是相邻的;
-- 分配, 释放方式 : 编译器分配内存, 程序退出时系统自动释放内存;
-- 存放内容 : 存放 程序的二进制代码, 和一些特殊常量;
-- 生命周期 : 编译时分配内存, 程序退出后释放内存, 与 程序 的生命周期相同;
-- 生命周期 : 函数执行时分配内存, 执行结束后释放内存;
-- 特点 : 该分配运算由处理器处理, 效率高, 但是栈内存控件有限;
-- 谨慎使用 : 如果分配了 没有释放, 会造成内存泄露, 如果频繁 分配 释放 会出现内存碎片;
使用场景 : 如果 一个变量使用频率特别高, 可以将这个变量放在 CPU 的寄存器中;
extern变量概念 : 声明外部变量, 外部变量就是在函数的外部定义的变量, 在本函数中使用;
-- 作用域 : 从外部变量定义的位置开始, 知道本源码结束都可以使用, 但是只能在定义extern后面使用, 前面的代码不能使用;
extern变量作用 : 使用extern修饰外部变量, ① 扩展外部变量在本文件中的作用域, ② 将外部变量作用域从一个文件中扩展到工程中的其它文件;
-- 单个文件内声明 : 如果不定义在文件开头, 其作用范围只能是 定义位置开始, 文件结束位置结束;
-- 多个文件中声明 : 两个文件中用到一个外部变量, 只能定义一次, 编译 和 连接的时候, 如果没有这个外部变量, 系统会知道这个外部变量在别处定义, 将另一个文件中的外部变量扩展到本文件中;
-- 本文件中能找到 : 编译器遇到 extern 的时候, 现在本文件中找外部变量的定义的位置, 如果找到, 就将作用域扩展到 定义的位置 知道文件结束;
-- 本文件中找不到 : 如果本文件中找不到, 连接其它文件找外部变量定义, 如果找到, 将外部变量作用域扩展到本文件中;
static 变量 与 全局变量 相同点 : 全局变量是静态存储的, 存储的方式 和 位置基本相同;
static 变量 与 全局变量不用点 : 全局变量的作用域是 整个项目工程 横跨过个文件, 静态变量的作用域是 当前文件, 其它文件中使用是无效的;
变量存储位置 : 全局变量 和 静态变量 存放在 全局区/静态去, 局部变量存放在 栈区(普通变量, 指针变量内容) 和 堆区(指针变量指向的内容);
-- 局部变量 : 局部变量 加上 static , 相当于将局部变量的生命周期扩大到了整个文件, 作用域不改变;
-- 全局变量 : 全局变量 加上 static , 相当于将全局变量的作用域缩小到了单个文件, 生命周期是整个程序的周期;
-- 内部函数 : 单个文件中使用的内部函数, 仅在那个特定文件中定义函数即可;
-- 全局函数 : 如果要在整个工程中使用一个全局函数, 需要将这个函数定义在一个头文件中;
static函数与普通函数区别 : static 函数在内存中只保留一份, 普通函数 每调用一次, 就创建一个副本;
-- 申请响应 : 如果 stack 没有足够的剩余空间, 就会溢出; 堆内存从链表中找空闲内存;
-- 内存限制 : stack 内存是连续的, 从高位向低位扩展, 而且很小, 只有几M, 是事先定好的, 在文件中配置; heap 是不连续的, 从低位向高位扩展, 系统是由链表控制空闲程序, 链表从低地址到高地址, 堆大小受虚拟内存限制, 一般32位机器有4G heap;
-- & 可以使用的情况 : 取地址操作 只能用于内存中的对象, 如变量 或 数组, 栈内存 堆内存 都可以;
指针指向 : 每个指针都必须指向某种特定类型;
传值调用 : 以传值的方式将参数传递给函数, 不能直接修改主函数中变量的值, 仅仅是将副本传递给了函数;
传址调用 : 将 变量的指针 传递给函数, 当函数对指针进行操作的时候, 主函数中的值也进行了对应变化;
-- 传值调用 : swap_1 是传值调用, 传入的是 main 函数中的 a b 两个变量的副本, 因此函数执行完毕后, 主函数中的值是不变的;
需求分析 : 调用getint()函数, 将输入的数字字符 转为一个整形数据;
-- 使用场景 : 当进行输入的时候, 不能确定是否已经输入足够的字符, 需要读取下一个字符, 进行判断, 如果多读取了一个字符, 就需要将这个字符退回去;
注意的问题 : 出现问题, 暂时编译不通过, 找个C语言大神解决;
-- 可互相替代 : 数组下标执行的操作都可以使用指针替代;
-- 结论 : 通过数组和下标 实现的操作 都可以使用 指针和偏移量进行等价替换;
-- 形参指针 : 将数组传作为参数传递给函数的时候, 传递的是数组的首地址, 传递地址, 形参是指针;
-- 函数参数是数组 : 函数传入一个字符串数组参数, 返回这个字符串长度;
数组和指针参数 : 将数组名传给参数, 函数根据情况判断是作为数组还是作为指针;