我的第一篇编程日志——初始化问题

一直没有意识写编程日志,直到最近才发现了编程日志对一个程序员的重要性。记得前一阵学习Android编程的时候,总会被各种各样的问题——包括让人头痛的异常(Exception)和奇葩的问题(连不上USB调试什么的)绊住脚。这时候第一件事就是百度。惊喜的是,对于其中的大多数的问题,总有人先我解决了它,并把它详细的写在了CSDN或者StackOverFlow这样的网站上(这也得益于Java是个发展了20年的成熟语言)。那个时候我在想,这些人真实好有奉献精神,如果是我,一定不会想到会把它们放到网上,让别人通过检索工具找到,帮助更多的程序员 - -!

是的,我一直这么想。在我5月3号花了整整3天,提交了50多次才成功的通过了消解算法的题目之后,我依然这么想。那个时候,我已经是一个编了超过五千行代码的老新手了,却被小小的消解算法困了超过72个小时!那个时候,我把这归功于“消解算法的难度”,但现在回想起来,用了不同的方法重写了4遍,却一直都在同一个小小的地方犯了错。但那时却没有意识到,直到5月11号程设大赛上,花了3个小时辗转反侧在2048那道简单至极的题目上,我想尽了自己能想到的特殊情况,还开始对题目质疑了起来,看着周围已满是这道题的气球,心里越来越紧张,到最后险些要放弃。我甚至假装去上厕所,拿着手机把把2048玩了很多遍去找它的规则,当然这最后并没有带来帮助。然而最后一刻,当我忽然“又”发现了程序中的一个错误的时候,我并没有在意,因为那时对诸如此类的错误已经修改了一个又一个,我只是改了后不报希望的按Alt+A、Alt+C、Alt+V,然后再如前4个小时一直在做的F5、F5、F5……但是,这一次,绿色的AC让我兴喜若狂!虽然是个简单的题目,没有什么值得惊喜,但是总算熬过了漫无边际的Debug的折磨,让我最后只能遗憾收场。

其实,这未曾不是一次遗憾。

那段程序修改前是:

variable = 0;
for(...)
{
   ...
}
修改后是:

for(...)
{
   variable = 0;
}

一个再简单不过的初始化错误。我承认想避开这样的错误是需要经验的。可是,哪怕编过一个月C程序的程序员怕也碰到歌哦这样的问题了吧。一个多么普遍多么常见的错误,却在比赛上纠缠了我3个小时之久,无不让我彻悟——上一学期的所谓“五千行的编程经验”其实只是“会”与“不会”C语言语法的区别。

所以从那时起,我便决定开始写编程日志了。花在日志上的时间一定是有必要的,它能让你思索找寻解决或解决的问题真正成为你的”编程经验“,而不只是Accept这么简单。

好,现在我要正式开始我的第一篇编程日志了——初始化问题

把声明的所有的变量初始化绝对是个好习惯,永远也不要觉得这是个多余的步骤。这一点我是非常老实的遵循的,所以也算是一点点经验了。

基本的数据类型一般都初始化成 0,int float double等数值类型的数组可以用{0}来初始化,字符串按理说是不用初始化的,但是我有时也喜欢用{'\0'}来初始化,这里还有个问题,如果把字符串当字符数组那么一个一个赋值的话一定要注意每次随手后面跟个'\0',不管你是不是要求长、打印这些操作,这也是经验!经验就是如果你不这么做,总有一天你会遇到相同的问题然后求爷爷拜祖宗绞尽脑汁还得回来这么改的错误。这可以推广到很多情况,比如说指针在数组中移动一次就检测一下是否越界了等等,这些我会在后面的日志中详说。

因为使用指针后太容易出现无效内存引用,很长一段时间变成都不敢用指针。尤其是自己电脑上的GCC4.8不报错,网教却频频报错。询问了才知道网教的GCC是加了补丁的。虽然不明白GCC还能加补丁是什么高端的玩意,但一定是这样有更多限制了,给调试带来了更大的困难,编程的时候也要愈发细心。所以,防止出错的第一步就是声明指针的时候初始化。明确让指针指向某一位置,或赋值NULL。当然,初始化的注意对指针的使用来说是远远不够的,指针的熟练准确使用也是一个漫长的积累经验的过程。

结构体在定义的时候没有初始化的机会,只有声明时才能初始化(当然这于其他数据类型一样)。个人对结构用的不太熟练,每次初始化都要查书。结构体初始化的方式有很多种,用的比较多的是这几种:

typedef struct
{
    int a ;
}myData;

myData a1 = {.a = 0}    //1st way
myData a2 = {0};    //2nd way
myData arr[10] = {0};   //for array
并且因为第二种方法数组也能初始化,所以用这个比较多。更多的方法可以看书,个人觉得C Primer Plus里面写的较全。

当然数据初始化方法没有什么好说的,问题是数据初始化掌握不好会给带来很大很大的问题,而且久久无法解决!!再来看下校赛2048的题目(因为是个极其简单的题目却困了三个小时,让整个团队无法继续。这件事一直让我无法解脱,真的对不起霸哥和小可爱@.@)

题目是这样的:

2048是比较流行的一款数字游戏。

    游戏规则很简单:
  开始时在4*4的棋盘内随机出现两个数字,出现的数字仅可能为2或4,玩家可以选择上下左右四个方向,若棋盘内的数字出现位移或合并,视为有效移动,玩家选择的方向上若有相同的数字则合并,每次有效移动可以同时合并,但不可以连续合并,合并所得的所有新生成数字相加即为该步的有效得分,玩家选择的方向行或列前方有空格则出现位移,每有效移动一步,棋盘的空位(无数字处)随机出现一个数字(依然可能为2或4),棋盘被数字填满,无法进行有效移动,判负,游戏结束。棋盘上出现2048,判胜,游戏结束。

现在给定一个4*4的矩阵代表当前的游戏局面(保证合法),请问玩家是否还可以操作?如上图则还可以向右或向左或向上操作。

输入:

第一行一个整数T(T<=50),表示数据组数。

以下T组数据,每组数据包含一个4*4的矩阵,保证其中数字合法,如果这个格子为空则用0表示。

输出:

每组数据输出一行,如果不能操作输出"NO"(不包含引号),否则输出"YES"。

输入样例

2
8 4 2 2

2 4 16 32

4 8 16 16

256 1024 512 64

2 4 2 4

4 2 4 2

2 4 2 4

4 2 4 2

输出样例


YES

NO

这道程序也是你在玩2048的时候每次滑动之后程序都要做的一步,相当于2048的一个小小的函数(想到国防科大的同学在我面前得瑟他编的控制台2048,作为计算机专业学子深感渣渣无可攀就)。同时这道题作为自动化学院的c语言编程题,再次被碾压到极致。

不过说回来题目本身非常简单,大体就是判断四周有没相同的数即可,同时注意一下2048出现游戏即结束即可,其实应该学了循环的初学者就能立即完成的(包括2048这个游戏也是吧。。。)但是为什么它困了我三个小时呢?因为一个可恶的flag.

这个程序有多组用例,如果有空即可走,有相同的可走,但是一旦出现2048即使有空有相同的也判定游戏结束。但是不知道自己脑残要用个flag标记2048的出现。于是就声明了flag

flag = 0;
for(int i = 0;i < n ;i ++)
{
    ...
}

于是,可怕的事情发生了,flag一旦变成1就永远是1了,也就是会从第一个2048出现以后,后面的用例一定全是NONONONONO……其实这个错误太常见了,我甚至有印象在我去过的为数不多的几次C语言课堂中都听到过老师讲这个错误。当时没有在意,觉得自己绝对不会犯这种错,也许那时候以来我都没有犯过,却在如此重要的比赛上被它拦住的去路。如果在比赛中没有出现,也一定会在今后的编程中出现,所以我一定要让这个简单的错误彻底终结!

所以,总结的经验是,谨慎起见,有多组测试用例的时候,还是把所有的变量声明放在循环内较为保险;一旦把一个变量放在的外面,一定要再三思考这样是否妥当,这个变量是否真的只用一次初始化?如果一个测试用例完成后,它残留的值不会影响到下一次测试么?一定会在每次测试都赋新值么?尤其是flag这种东西,只赋1不赋0的变量,要谨慎再谨慎!!!不过重要的是养成习惯,对初始化保持敏感,包括对初始化写的位置保持敏感!


你可能感兴趣的:(C编程日志)