第30篇 代码习惯 初学C与数据结构有感

为什么为这个"小事儿"单开一篇呢,因为我昨天写个很简单的代码,结果VS不报错,但是结果却是一直不正确,自己找了两个小时,无论这么找都找不到错误,连续两个多小时,就是不知道哪里错了,给我整的如坐针毡、如芒在背、如鲠在喉,最后没招了去请教老师,结果老师浏览了半分钟,直接笑了:因为我落下了一个头文件的声明......

因此有了这篇文章:

目录

一、新手最容易轻视的编码习惯:不是格式,是和机器的契约

[1.1 依赖契约:调用任何库能力,必有溯源](#1.1 依赖契约:调用任何库能力,必有溯源)

[1.2 可读性契约:能跑的代码≠合格的代码](#1.2 可读性契约:能跑的代码≠合格的代码)

[1.3 防御性习惯:永远不要假设输入是合法的](#1.3 防御性习惯:永远不要假设输入是合法的)

二、零基础构思代码:拒绝提笔就写,适配新手的思考流程

[2.1 第一步:厘清边界,先想「不做什么」](#2.1 第一步:厘清边界,先想「不做什么」)

[2.2 第二步:契约先行,先定输入输出,再写逻辑](#2.2 第二步:契约先行,先定输入输出,再写逻辑)

[2.3 第三步:拆分逻辑,拒绝多层嵌套](#2.3 第三步:拆分逻辑,拒绝多层嵌套)

[2.4 第四步:取舍思维:新手不求最优,只求可控](#2.4 第四步:取舍思维:新手不求最优,只求可控)

三、立足初学视角:跳出代码,看懂底层本质

[3.1 代码写给人看,顺带写给机器执行](#3.1 代码写给人看,顺带写给机器执行)

[3.2 所有低级bug,根源都是轻视规则](#3.2 所有低级bug,根源都是轻视规则)

[3.3 慢即是快:构思耗时,是性价比最高的投入](#3.3 慢即是快:构思耗时,是性价比最高的投入)

四、现阶段我的编码自律清单(适配C+初学数据结构)

五、结语:初学阶段,秩序大于技巧

参考文献(保留入门向权威,剔除超纲文献)


写代码的底层秩序,藏在最简单的习惯里

目前我仅系统学完C语言,刚刚入门数据结构与基础算法,没有接触Java、前端、架构、业务开发,最开始写代码只在乎能不能跑、能不能出结果 。调用printf忘了引<stdio.h>、使用指针不判空、写排序代码堆砌if嵌套,代码能编译就沾沾自喜,报错了就盲目调试。

最近重读软件工程经典书籍里面向入门者的章节,才猛然意识到:编码能力的差距,从来不在于会不会冷门语法、能不能写出最优算法,而在于底层习惯与思考方式。大家觉得大神代码简洁稳健,并不是对方掌握了高深知识,而是把C语言最基础的规则,做成了贯穿全程的思维秩序。本文立足C语言初学者视角,结合入门级权威论述,抛开后端架构、工程框架等超纲知识,聊聊新手写代码最该在意什么、如何构思代码,既有底层实操,也有顶层思考。

一、新手最容易轻视的编码习惯:不是格式,是和机器的契约

初学阶段我们总觉得:编码习惯是缩进对齐、变量起名这种无关紧要的小事,编译器不报错就行。但《程序设计实践》针对入门开发者明确提出:**编程语言基础语法规则,本质是开发者与计算机的双向契约,轻视契约,所有bug都是必然结果1。**放在目前仅掌握C语言的阶段,这句话格外直白。

1.1 依赖契约:调用任何库能力,必有溯源

我最开始踩过最多的坑,就是最简单的问题:调用库函数,随手省略头文件。很多新手包括我,觉得printfstrcpyqsort好像不用引头文件也能跑,因此轻视一行代码。

C语言真实踩坑案例(初学高频bug) :初学排序,直接裸写qsort排序整型数组,不引入<stdlib.h>,DEV-C++编译器只会抛出黄色警告,不会报错,程序可以正常运行。换到gcc编译器编译,直接编译失败、内存乱码。

《C程序设计语言》里解释过这个底层逻辑:C语言早期允许函数隐式声明,编译器默认所有未声明函数返回值为int2 一旦库函数返回值不是整型,机器直接错乱。看似是少写一行头文件,本质是违背依赖契约:我调用了系统提供的外部能力,却拒绝告知编译器我知晓它的规则。

落实到我现阶段唯一的编码准则:C语言所有外部函数,必须显性绑定头文件,没有例外

  • 输入输出 → 强制引入 <stdio.h>

  • 字符串操作 → 强制引入 <string.h>

  • 排序、内存申请malloc → 强制引入 <stdlib.h>

这件小事给我的启发很大:编程没有随心所欲,哪怕最简单的代码,都要遵守既定契约;机器不会包容开发者的懒惰,所有偷懒的隐式行为,迟早变成bug反噬自己。

1.2 可读性契约:能跑的代码≠合格的代码

刚开始学C,我特别迷恋精简代码,觉得代码越短、行数越少,水平越高。直到翻看《Clean Code》入门章节:对于编程初学者,代码可读懂优先级,永远高于代码精简;机器能看懂不算本事,人能看懂才是编码的第一要义3

结合我初学算法的场景举个最简单的例子:输入三个数字求最大值

新手炫技写法(能运行,但极差)

cpp 复制代码
int main()
{ int a,b,c;scanf("%d%d%d",&a,&b,&c); printf("%d",a>b?a>c?a:c:b>c?b:c); }

这段代码没有任何语法错误,编译运行完全正常,但存在两个致命问题:第一,隔天我自己回看,也要反复拆解三元运算符逻辑;第二,如果需要改成求最小值、增加判空逻辑,极容易改错。

初学者规范写法(贴合当前能力)

cpp 复制代码
// 初学C:分步编写,拆分逻辑,优先保证易懂
int getMax(int a, int b, int c)
{ 
int temp = a > b ? a : b; return temp > c ? temp : c; 
} 
int main()
{ 
int a,b,c; scanf("%d%d%d", &a, &b, &c); 
printf("%d", getMax(a,b,c)); 
return 0; 
}

仅仅拆分出一个简单自定义函数,代码行数变多,但是逻辑清晰。结合最小惊讶原则延伸思考:写代码不要制造阅读负担,哪怕是写给未来的自己。初学阶段不必追求代码极简,要追求逻辑直白,这是程序员最基础的修养。

1.3 防御性习惯:永远不要假设输入是合法的

这是我初学指针、顺序表最深的感悟。《高效程序员的45个习惯》入门章节提到:新手写代码,默认一切输入都是完美的;成熟开发者写代码,默认一切输入都是错误的4

刚学C语言指针的时候,我写字符串拷贝,直接裸写指针赋值,从来不会判空:

cpp 复制代码
// 错误写法:初学高频崩溃代码 
void copyStr(char *src, char *dst){ strcpy(dst, src); }

一旦传入空指针NULL,程序直接闪退,黑屏崩溃,这类报错没有提示,排查难度极大。

改成适配我当前水平的防御性写法,只增加两行判断,就能规避全部崩溃问题:

cpp 复制代码
// 适配C初学者:最简防御编码 
void copyStr(char *src, char *dst)
{ 
// 提前拦截非法指针,不执行后续逻辑 
if(src == NULL || dst == NULL) 
    return; 
strcpy(dst, src); 
}

延伸到我正在学的数据结构:写顺序表初始化、链表节点插入,第一步永远不是赋值,而是判断指针是否为空。防御性编码不需要高深算法,只是提前敬畏边界,规避已知风险

二、零基础构思代码:拒绝提笔就写,适配新手的思考流程

我之前最大的陋习:拿到题目、课堂作业,立刻打开编辑器敲代码,边写边改、边改边调。《人月神话》入门导读直言:越是基础代码,越不能仓促编写;编码耗时很短,构思改错的成本,永远高于编写成本5

结合我目前只精学了C、初学数据结构的现状,我总结了一套新手专属构思流程,不涉及架构设计、模块拆分,简单四步,完全不超知识范围:

厘清边界→约定输入输出→拆分简单逻辑→补全容错代码。

2.1 第一步:厘清边界,先想「不做什么」

绝大多数新手拿到算法题、编程作业,第一反应永远是:怎么写代码实现功能? 但是我现在改掉了这个习惯,动笔前第一个问题反问自己:哪些功能我不做、哪些逻辑我不写。很多代码写崩、越写越乱,不是不会语法,是想得太多、贪心太重。

就用课堂实操案例举例:作业要求编写C语言代码,校验手机号是否合法

  • 新手错误旧思路(贪心编码):作业只要求判断手机号,我却额外画蛇添足:上网查正则表达式、兼容虚拟号码、兼容港澳台、海外手机号,想要代码一次性适配所有场景。但是我目前压根没学正则、不懂字符编码,强行堆砌看不懂的代码,最后变量混乱、逻辑报错,代码彻底写崩。

  • 规范新思路(划定边界) :读懂题目+认清自身能力,给自己划定红线:现阶段我只会基础C语法、字符串遍历,本次代码只做一件事:判断输入是否为11位纯数字。除此之外,海外号码、特殊号段、虚拟号码,全部不处理、直接判定非法。

我之前看不懂的「代码腐化、边界不清」,换成大白话特别好理解:代码腐化,就是本来只需要写10行简单代码,非要强行叠加一堆自己不会、不需要的功能

初学阶段我们最大的误区:不甘心只做最简功能,总想写全能、完善、高大上的代码。看似上进心强,实则本末倒置:语法基础不稳、知识储备不够,强行堆砌额外逻辑,只会变量混乱、循环嵌套、bug满天飞。写代码第一步不是实现功能,是学会克制,主动砍掉多余需求,这就是厘清边界的意义。

2.2 第二步:契约先行,先定输入输出,再写逻辑

这是我改良最大的习惯,也是C语言底层思维:对应头文件声明逻辑,先定函数签名,再写函数体,本质就是新手版的契约思维。

以前写链表长度统计:直接在main函数里堆砌循环、遍历代码,代码杂乱无法复用;

现在构思顺序:先写函数入参、返回值,空代码占位,填充逻辑:

cpp 复制代码
// 第一步:先定契约,只定义入参、返回值,不写遍历逻辑
// 入参:链表头指针;返回值:链表节点个数
int getLinkLength(struct Node *head)
{
    // 暂时空实现,先固定接口
    return 0;
}

哪怕后续内部遍历逻辑写错、重写,函数的输入输出不会变。这也让我彻底读懂了C语言头文件的意义:头文件就是对外契约,.c文件是内部实现,契约不变,内部随便修改。原来最难的编程思维,早就藏在大一C语言第一课里。

2.3 第三步:拆分逻辑,拒绝多层嵌套

初学算法最容易踩坑:多层if嵌套、循环嵌套,代码缩进七八层,写到最后自己绕晕。《重构:改善既有代码的设计》面向入门者给出最简规则:如果一段C代码缩进超过三层,一定是拆分思路出错6

落实到数据结构学习:编写顺序表增删查改,不要全部塞进main函数。查询单独写函数、删除单独写函数,哪怕函数只有三行代码,也要拆分。

我慢慢意识到:单一职责不分工程大小,哪怕几十行的随堂代码,也要各司其职。拆分不是炫技,是降低自己的调试成本。

2.4 第四步:取舍思维:新手不求最优,只求可控

初学数据结构,我常常陷入内耗:冒泡排序效率太低,要不要手写快排?数组存取太慢,要不要手写哈希?

《软件架构要义》入门卷有一句话点醒了我:编程所有阶段,取舍永远优于极致最优;初学者首要目标是代码可控,而非算法极致高效7

适配我现阶段的取舍准则:

  • 能写数组绝不强行写链表,保证逻辑可控;

  • 会写冒泡排序,不必强行优化快排,先吃透基础原理;

  • 库函数能用就用,不重复造轮子,不盲目手写底层逻辑。

三、立足初学视角:跳出代码,看懂底层本质

目前我没有学习网络、操作系统、后端架构,但是仅靠C语言和基础算法,也能摸到编程顶层逻辑,不需要超纲知识,全部来自最简单的编码感悟。

3.1 代码写给人看,顺带写给机器执行

以前我认为:编译器能跑就行,注释多余、变量起名随意。随便起名a、b、tmp,写满全局变量。

现在彻底改观:机器不关心变量名叫head还是a,不关心代码有没有换行、有没有注释;缩进、命名、头文件、拆分函数,全部都是写给人看,写给隔天的我自己看

Uncle Bob面向编程新人的访谈提到:**新手编写的代码,最大受众不是CPU,是未来调试代码的自己8。**放在C语言学习里,这句话无比贴切。

3.2 所有低级bug,根源都是轻视规则

空指针崩溃、越界报错、隐式声明告警、头文件缺失,我复盘所有踩过的坑,没有一个是算法太难、语法晦涩导致的,全部是轻视基础规则、偷懒导致。

C语言语法极简,规则直白,但它是一门极其看重秩序的语言。指针有指针边界、库函数有依赖规则、内存有申请释放契约。编程能力的分水岭,从来不是刷题数量,而是能不能敬畏最简单的规则。

3.3 慢即是快:构思耗时,是性价比最高的投入

刚开始刷题,我追求敲代码速度,五分钟写完一道排序题,调试两小时;现在反过来,花三分钟梳理输入、边界、拆分逻辑,五分钟写完代码,一次编译通过。

《程序设计实践》给出新手建议:**放慢编码速度,是入门阶段最高效的成长方式1。**编程不是打字比赛,敲键越快,思维越浮躁;越是基础阶段,越要沉下心搭建秩序。

四、现阶段我的编码自律清单(适配C+初学数据结构)

结合自身学习进度,剔除全部超纲内容,定下五条入门铁律,长期执行:

  1. 依赖绝不偷懒:调用任意C标准库函数,补齐对应头文件,杜绝隐式声明;

  2. 下笔先定契约:自定义函数先写返回值、形参,空实现占位,写完接口再补逻辑;

  3. 指针必先判空:链表、顺序表、字符串指针,使用前强制判空,规避闪退bug;

  4. 拒绝嵌套堆砌:代码缩进不超过三层,拆分短小函数,不写炫技式精简代码;

  5. 克制贪心编码:不写超出当前知识范围的逻辑,吃透基础,不盲目追求高效算法。

五、结语:初学阶段,秩序大于技巧

回到最初最简单的问题:刚学C语言、入门数据结构,写代码到底要注意什么?

表层,是补齐头文件、指针判空、规范命名这些琐碎习惯;

中层,是动笔前梳理边界、先定接口、拆分逻辑的思考方式;

顶层,是敬畏规则、克制贪心、重视秩序的编程本心。

现阶段我不用钻研架构、不用深耕工程化、不用学习冷门技术。写好每一行C语言代码,守住最简单的编码契约,搭建属于自己的代码秩序,就是工程师成长最扎实的起点。算法决定代码快慢,语法决定代码对错,秩序决定我们能走多远。


参考文献(保留入门向权威,剔除超纲文献)

1 布莱恩·克尼汉, 罗布·派克. 《程序设计实践》(The Practice of Programming), 1999(保留入门软件工程基础论述)

2 布莱恩·克尼汉, 丹尼斯·里奇. 《C程序设计语言》, 1978(C语言经典权威著作)

3 Robert C. Martin. 《Clean Code:代码整洁之道》, 2008(节选入门编码规范章节)

4 Venkat Subramaniam. 《高效程序员的45个习惯》, 2006(入门编码思维章节)

5 Frederick Brooks. 《人月神话》(The Mythical Man-Month), 1975(入门导读卷)

6 Martin Fowler. 《重构:改善既有代码的设计》, 1999(新手代码拆分准则)

7 Len Bass. 《软件架构要义》, SEI软件工程研究院, 2021(入门取舍思维节选)

8 Robert C. Martin. Software Craftsmanship Interview, AlgoCademy, 2025(面向编程新人访谈)

相关推荐
qq_241585612 小时前
可用在中断中浮点数打印类似printf
c语言
C语言小火车3 小时前
C++ 快速排序(Quick Sort)深度精讲:分治思想、Lomuto 分区法及三数取中优化,面试手撕必会
c语言·开发语言·c++·面试·排序算法·快速排序
tachibana24 小时前
hot100 回文链表(234)
java·网络·数据结构·leetcode·链表
aaaameliaaa5 小时前
进制练习题【找出只出现一次的数字、交换两个变量(不创建临时变量)、统计二进制中1的个数、打印整数二进制的奇数位和偶数位、求两个数二进制中不同位的个数】
c语言·数据结构·笔记·算法
灯厂码农5 小时前
C语言内存管理——内存对齐与共用体union
linux·服务器·c语言
伏 念7 小时前
AI Coding 零基础实战教程
c语言·进程·预处理
你家人养牛7 小时前
OOC Relation Plugin:C 语言面向对象 开发的高效辅助工具
c语言·vscode
想你依然心痛8 小时前
嵌入式C代码规范:MISRA-C 2012核心规则解读——类型安全与未定义行为深度剖析
c语言·安全·代码规范
星空露珠8 小时前
迷你世界UGc3.0脚本Wiki[剧情动画模块管理接口 Timeline]
开发语言·数据结构·算法·游戏·lua