你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

C语言的万能“三板斧”该如何使用呢?

[复制链接]
gaosmile 发布时间:2020-4-23 17:54

作为嵌入式工程师,写一个效率高效,思路清晰的C语言程序是我们的终极目标,那么,怎么才能写好这样的程序呢?首先,我们要用C语言的思维方式来进行程序的构架构建;其次,要有良好的C语言算法基础,以此来实现程序的逻辑构架;最后,灵活运用C语言的指针操作。


4 A; a  n0 k: i1 k

虽然看起来以上的说法很抽象,给人如坠雾里的感觉,其实就是用C语言进行遇到问题、分析问题和解决问题的过程。那么,下文将给你介绍如何耍这“三板斧”。


$ X" |: ~  Q  O/ L2 V& m

嵌入式工程师在编写C语言程序的时候,要针对遇到的问题进行程序构架构建。

& Z9 M2 K6 r, {% S  X5 Z, M( A

比如我们要处理“猴子选大王”的经典问题:一群猴子,手拉手排成一个圆,从任意一只猴子开始从1开始报数,当遇到要排除的数(预先设定)时该猴子退出该圈,从下一只猴子开始继续从1报数,如此反复,最终剩下的猴子便是猴子的大王。那么,这“三板斧”该如何使用呢?

- a7 [. c9 \2 y$ g

5 `/ Y: X. o: Q; I# l0 a第一板斧,用C语言的思维方式进行程序构架构建
5 Q0 d( M& a2 R3 k' T, v" u9 `6 l4 N

. P+ ~9 l6 \) |" l/ h0 M程序分为三大部分:
) I% a: Z  l& }( j* w% X. R
5 p- R9 C1 L* {5 P$ d0 J
7 Z0 P& x5 b% Va、数据获取,为了程序的运行,上面的问题要获得猴子的总数,从那只猴子开始和剔除的个数;
3 u+ N2 e6 A. x, {+ Cb、数据运算,需要从一堆数据中剔除相应的数据,注意逻辑的正确;  M" {! S+ w7 |  U: `) a0 t
c、提高程序的运行速率,少用循环多用指针。
  M. b# b, T# H+ {; |0 r& [8 L' S% x6 k- ?8 B- @7 G% k) \
5 m4 L1 Z- M( c$ I$ k0 ~) ~. `
第二板斧,用C语言进行逻辑实现$ x7 O: P6 h  b# a$ ?9 h8 c+ b
, I/ D& I0 Q) r* N9 d* E" n" d4 I

+ d, l5 ^, h/ Q7 P* ga、 数据获取,通过printf和scanf进行参数的获取。! F0 U) f+ G4 Q
# f6 r$ _) _# ]2 K) m0 Z9 S! r
/* 读入问题条件 */
0 @3 W4 ?; |- ~printf("input total num:");2 K. p, ]; D$ K1 g: k8 J
scanf("%d", &n);
+ B$ t+ n* m* `/ ~" t' B0 ~printf("from which num begin:");& Z* |) W3 ~  v5 i; A2 {, {" G  M
scanf("%d", &k);
  x- T: V/ m7 N, ^6 yif(k>n||k==0)0 F( ?! o  [9 V  U8 z
{printf("please input the right begin num");
7 ?: h% R9 }/ S9 treturn 1; }% ~4 A- I; c" {( L% A( {' E5 p5 m  ~
printf("input the out num:");
+ w% D4 O! L8 }/ `scanf("%d", &m);7 i, {  r+ n$ S7 ^  c; N( D
if(m>n||m==0)7 T9 G# b$ c% \; G( H% t
{ printf("please input the right del num");& s5 K# w; m' _- b" f: ^( u
return 2; }
3 y" `0 x/ h+ s+ V* n  l! @7 P! k' l
8 @) F! z* h  u) F
1 _6 T0 t: R" x- n% Q- d3 E+ m
同时注意异常时的处理,比如上面两个if语句就是异常情况的判断,每种异常情况对应不同的返回值,这样便于程序过程的调试和数据的合法性。/ T3 ?/ M" v3 E" b4 L2 l+ @

( Y# C4 n: N/ K, H. D  c) B6 t+ i" |$ y0 }3 F' l% c0 r7 g2 _+ c
b、 /* 定义链表节点类型 */
1 g8 o" V$ j. a0 Z/ K6 g+ J0 {9 g: Z1 X

" D7 |, S; u3 |% Ntypedef struct node9 i, ?5 ?0 s9 c4 X# H
{
& J! ~% N6 B4 Tint data;9 H8 a; T/ }) b; i
struct node *next;5 ]6 Y4 G, m/ P% i1 U
}linklist;
% a, y, O8 F; A$ ~& h1 d构建循环链表进行“猴子”的圆圈建设。
$ `2 ^- H2 C/ Y0 t+ j/* 创建循环链表,头节点也存信息 */
- O' H& [4 y) M" G$ j. b; c* Lhead = (linklist*) malloc(sizeof(linklist));
7 k' O' k) {* }0 \, B  U/ `. Xp = head;
1 c: T! X8 c# i3 Qp->data = 1;6 P9 x/ }$ r4 t+ j
p->next = p;! r, ]/ K' S4 Z' k1 N6 u
/* 初始化循环链表 */1 z: z3 y' H6 R
for (i = 2; i <= n; i++)( J1 L( W, W! W9 ~' a  e* L
{
. l) c2 Y3 u  o' }( d' q% Js = (linklist*) malloc(sizeof(linklist));3 H# m6 L3 ]; i) X  O( ]8 l
s->data = i;: }1 u: y  T% J
s->next = p->next;
  b% q$ D/ W. p. ]. ^/ D9 ?& G' Op->next = s;5 f% q6 k1 P- {/ l) E
p = p->next;
: o  m2 a: E4 R5 ?$ m+ K0 f: s}
3 q! H8 f% T( s% f: `! o3 {7 p# U8 C" {* d$ K

通过该步骤后,head和p(present)都成了一个“猴子圈”的链表。在该链表的构建过程中需要注意一下几点:内存的开辟,此时遵守使用多少开辟多少的原则。


$ _3 Z/ a; h! _, O* _: ^$ i. T. @

如果一下开辟过多,会引起内存泄露的问题,但是,这个小程序是不会遇到这种问题了。其次是熟悉循环链表的构建方法:链表的尾巴指向链表的头。这个时候有心的话还会联想到双向链表的情况。

7 M$ }, U# K4 Y* I1 n; z
c、 /* 找到第 k 个节点 */
) A7 ]/ `& p+ w5 ]6 i' t( ]0 h4 Lp = head;
: O  y2 G2 A( }- mfor (i = 1; i <= k; i++)
3 e: Z7 F6 p- K9 D; W8 o7 b{1 K3 ~( D! o/ Z; [* k
p = p->next;
& b# J7 C* }6 m: q}
) E. e$ m% i. D" q$ b% Z" U" O& B

找到从第几个位置开始计数。此时,p指向开始的“猴子”。因为采用了链表方法,这个过程只需要关注p指针的next指向即可。

8 b7 [3 R: B: q
a、 保存初始的“猴子”圆圈参数。
& p  Z* G( C; a+ [/* 保存节点总数 */
. E; r. a% s$ m; etotal = n;$ N- o/ {9 O( _2 W
printf("\nthe out num:");' `0 _, q' J0 R$ [$ G0 q  B$ v% g8 W
q = head;! D6 b0 W1 J* O1 n3 e2 p
' V( ?4 A1 L- G

为什么要保留这个呢?首先要控制猴子数目,所以保留了全部的数目。其次用q(qurry)来保留剔除猴子前面的链表,并连接剔除猴子后的链表。这样,完成了循环链表的元素删除。

; b. F3 X& t" f% Q3 u2 M3 k
b、 猴子查数。
/ S; A- n4 e; g5 w& C

猴子查数是整个程序的关键,需要完成以下任务:a、找到开始的“猴子”数;b、删除该“猴子”;c、将删除掉的循环链表首尾连接起来。

7 m/ T4 @+ k2 b
/* 只剩一个节点时停止循环 */7 x& E9 n, j% A0 J% D
while (total != 1)
) y4 T5 Q* P- \1 J6 {0 ^" Z{* a. m" {6 A; V0 N
/* 报数过程,p指向要删除的节点 */
5 F3 `) s8 \: J3 r) K( n6 `6 v, _& ?1 tfor (i = 1; i < m; i++)/ k- w& U! Y) d- d" Q
{8 N' W* E# u$ y9 Z5 J
p = p->next;- E0 f. `) r- i" n
}
3 N# [' B! ?& V' H7 m4 j1 c/ W/* 打印要删除的节点序号 */9 e$ l1 P: V' z9 N; g: M5 v
printf("[%d] ", p->data);2 ~, |* h- o. [* Q$ s
/* q 指向 p 节点的前驱 */
* q" r4 E6 t1 p! Q" Q4 {) lwhile (q->next != p), P% M/ C' Z+ a$ l5 P
{. e- j! t* k; K7 u3 L$ O1 U* y1 r0 }
q = q->next;9 H# Z0 X7 g  e( B4 M
}
, N* b' I$ Y% T$ Y- Y" h, o% I/* 删除 p 节点 */$ e! J' r/ h9 h' k; w" L3 w4 S1 q5 t# d
q->next = p->next;
# S9 I4 P( c0 f. E3 N: p/* 保存被删除节点指针 */
7 ^' d* h1 q5 ys = p;
# v2 {2 J* {% u  N/* p 指向被删除节点的后继 */; ~2 e7 K4 `: k# a' q* s" O, m
p = p->next;
/ g* r4 U$ X8 Q4 Y  ~  f2 P8 m/* 释放被删除的节点 */2 u: Q. C: F) g7 D3 R2 l% N7 U
free(s);
/ n6 Y" ~5 o: Y; `/* 节点个数减一 */1 o) Z6 n( ~/ M' q' I
total--;
, R- u9 Y; o- a6 Z. l6 i}1 c& L0 {! w/ ^
/* 打印最后剩下的节点序号 */+ N5 u. c, h. U- y! Z9 V1 l: C
printf("\n\nthe last num:[%d] \n\n", p->data);
& |) k/ d' n$ F$ q# ]1 m! A- ^free(p);
, l% [& j. V1 L  p2 [1 e" b}
8 m) p, U0 s4 W
' Z* C( a3 M4 W8 R

通过以上数据运算,可以完成相应链表元素的删除,这或许就是C语言程序的魅力所在。


) {% }0 r/ ]& [+ _# y
) q+ ^, a2 v2 k: f第三把斧,使用C语言的指针
1 Y" S. T1 Y2 Q! T- K9 H$ g; P( `6 v% A& q0 v

比如本程序的链表指针的定义,p,s,q。

linklist *head, *p, *s, *q;


' z5 x" j/ A6 ?* |! K

我们知道,指针操作不但可以减少数据操作需要的内存,还可以提高程序的运行速度,这在另一方面达到了我们的“第三把斧”的目的。

: V! [7 J1 ?2 E4 @

指针的优势,或许在本程序中表现的不明显,在大量数据和对操作速度要求比较敏感的情况下会很明显,比如Linux内核中,就会有很好的体现。

; v/ o* J, @, h8 d# L

总之,对于嵌入式工程师,不但能设计出好的硬件电路,如果也能写出好的软件程序就会让我们的生活更加美好。工程师们,耍起这“C语言的三把斧”!


% A0 ?' t: [) k3 e5 b: G
: D. V) [7 v2 E; a. Z* k, C* B6 w3 O
赞 收藏 1 评论2 发布时间:2020-4-23 17:54

举报

2个回答
李康1202 回答时间:2020-4-24 09:59:17
顶一下
qq1786897183 回答时间:2020-4-25 12:18:36
也来顶一下
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版