遇到C语言相关的两个问题让我心情压抑

一:C标准库中的 assert() 会粗暴地结束程序

  assert()在发布版(release)的程序中被编译为空语句,它仅存在于调试版(debug)的程序中,它的意图很明显,就是及时提醒开发者注意程序中的非正常情况,并辅助开发者排除这种非正常情况,使程序逐步趋于完善。一般来说,一个assert()断言失败,必然是程序的运行状态超出了程序员的预期,或程序流程进入到目前代码尚未处理的一个分枝。在这种情况下,程序员要找出问题的根源并改进程序,就需要对assert()上下文进行分析。此时继续向下单步执行几句代码,可以搜集更多的信息,有助于更及时的解决问题。可是“微软公司提供的”C标准库的assert()呢?断言失败后,程序并没有中断在调用assert()的那一行代码处,而是中断在C运行库内部,而且程序马上就要中止。以前用习惯了MFC中的 ASSERT(),它在断言失败后允许用户选择中止程序、忽略断言、中断(可以继续执行),对程序员非常友好。现在用回C标准库中的assert(),有点不能适应。解决方案是,可以自己写一个 my_assert(),代码如下:

//.h #if _DEBUG || !NDEBUG #define my_assert(exp) do{ if(!(exp)){my_assert_failed(#exp, __FILE__, __LINE__); __asm int 3} }while(0) void my_assert_failed(const char* info, const char* file, unsigned int line); #endif #ifdef NDEBUG #undef my_assert #define my_assert(exp) (void*)(0) #endif //.c #if _DEBUG || !NDEBUG void my_assert_failed(const char* info, const char* file, unsigned int line) { printf("/007/nAssert failed: %s /n at file /"%s/", %d line. /n", info, file, line); } #endif

 

 

二:for循环的简单改写竟然也出了问题

  以下典型的for循环代码,大家都是写滥了的,熟的不能再熟了:

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

  如果上面代码中的 n 不是一个常量或变量,而是一个表达式的话(比如 p1->p2->n),那么每循环一次,n就会被求一次值,无疑加重了程序负担,在执行效率要求很高的情况下是不合适的。通常会把for循环稍加改写,从后往前循环(适用于不介意循环次序的情况),循环变量 i 从 n-1 递减至 0:

for(unsigned int i = n - 1; i >= 0; i--) { }

 

  可是上面这种for循环的改写有重大缺陷,就是当 n 为零时,其行为不是“不执行for循环”,而是“执行很多次for循环”。因为,当n为零时,循环变量 i 的初始值为 (unsigned int)-1,即4294967295,这个值大于零,因而for循环会一直执行,直到 i 递减至0。这种行为与前面的常规for循环的行为(当n为零时不执行循环),迥异。关键在于 i 的类型是unsigned int,如果是int就不会有问题了(但是如果n是无符号整数,i定义成有符号整数一样有大麻烦嘛)。代码写错了容易改正,我所压抑的是,找不到比较好的代码书写方式。我想到两种方案:方案1,在for之前先把n求值并放到另一个变量m中,把循环写为 for(unsigned int i =0; i < m; i++) {}。方案2,在上面第二种for循环(递减i的那个)外面套一个判断语句,if(n != 0) for(unsigned int i = n - 1; i >= 0; i--) {}。但总感觉都不是特别满意。一般来说,方案1不错了,是优选方案,可是它多用了一个变量,增大了栈空间占用,对我这个严重依赖递归的程序不适合,最终无奈选择了丑陋的方案2,但始终是心情压抑,因为没有写出漂亮的代码。天呐!我最后发现方案2也有重大缺陷,无符号整数类型的 i 永远大于等于0,for循环永远不会退出,死循环!无奈祭出方案3:for(unsigned int i = n; i > 0; i--) {},循环变量从n开始递减到0(不包括0)。可是在方案3的循环内部,用到 i 的地方都要写成 i - 1,依然额外增加了计算量(原来是每次循环对n重复求值,现在每次循环对 i - 1 求值),与改写for循环的初衷不符。

  写不出漂亮的代码就是不爽。我感觉自己是不是走火入魔了?在栈中定义一个变量多占用4个字节内存都不能接受?在循环中多计算一次加减法都不能接受?可同样是我,用JAVA或易语言写程序,绝对不会犯同样的毛病。编程语言的魔力真的很神奇,很神奇。

 

 

  下面隆重推出网友 zhxk82 在本文之后的评论中给出的另一种写法:

for(unsigned int i = n - 1; i < (unsigned int)-1; i--)

哈哈,很不错嘛,除了不是十分直观之外,一切都很理想。哦,我现在已经不压抑了,心情很舒畅。谢谢zhxk82。

 

 

2009.8.21凌晨liigo补记:诚如评论中有网友所说,第二条过于琐碎了,太纠缠于细节。其实我也不想过早优化,也更倾向于写直观的代码,不希望把事情复杂化,并且愿意相信编译器会处理的很好。最终我还是(有些不情愿地)把代码改回了最初的 for(unsigned int i = 0; i < n; i++),呵呵,然而还是有些情绪无法释怀,嗨。

 

2009.8.22夜,liigo再次声明:我写此文并不是对C语言进行指责和抱怨,却是为自己写不出漂亮、简单且直观的代码而自责(不知道各位有没有同感,写出漂亮的代码会让人心情舒畅)。文中表达了压抑的心情,同时也是对心中压抑情绪的释放,是一种自我解脱方式。最后我跳出圈外,说出我多年来总结的稍微有些禅意的结论:当你使用者一个编程语言,出入于它的社区,就会受其思维模式的影响;而进入另一个领域时,相应地思维模式也会自动切换。思维模式的不同,会导致过程的不同和结果的不同。我相信这是一种比较接近于客观的表述,不具有主观倾向性。

 

你可能感兴趣的:(编程,c,File,mfc,语言,编译器)