遇到C语言相关的两个问题让我心情压抑
2009-08-20 15:42
295 查看
一:C标准库中的 assert() 会粗暴地结束程序
assert()在发布版(release)的程序中被编译为空语句,它仅存在于调试版(debug)的程序中,它的意图很明显,就是及时提醒开发者注意程序中的非正常情况,并辅助开发者排除这种非正常情况,使程序逐步趋于完善。一般来说,一个assert()断言失败,必然是程序的运行状态超出了程序员的预期,或程序流程进入到目前代码尚未处理的一个分枝。在这种情况下,程序员要找出问题的根源并改进程序,就需要对assert()上下文进行分析。此时继续向下单步执行几句代码,可以搜集更多的信息,有助于更及时的解决问题。可是“微软公司提供的”C标准库的assert()呢?断言失败后,程序并没有中断在调用assert()的那一行代码处,而是中断在C运行库内部,而且程序马上就要中止。以前用习惯了MFC中的 ASSERT(),它在断言失败后允许用户选择中止程序、忽略断言、中断(可以继续执行),对程序员非常友好。现在用回C标准库中的assert(),有点不能适应。解决方案是,可以自己写一个 my_assert(),代码如下:
二:for循环的简单改写竟然也出了问题
以下典型的for循环代码,大家都是写滥了的,熟的不能再熟了:
如果上面代码中的 n 不是一个常量或变量,而是一个表达式的话(比如 p1->p2->n),那么每循环一次,n就会被求一次值,无疑加重了程序负担,在执行效率要求很高的情况下是不合适的。通常会把for循环稍加改写,从后往前循环(适用于不介意循环次序的情况),循环变量 i 从 n-1 递减至 0:
可是上面这种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 [b]在本文之后的评论中给出的另一种写法:[/b]
哈哈,很不错嘛,除了不是十分直观之外,一切都很理想。哦,我现在已经不压抑了,心情很舒畅。谢谢zhxk82。
2009.8.21凌晨liigo补记:诚如评论中有网友所说,第二条过于琐碎了,太纠缠于细节。其实我也不想过早优化,也更倾向于写直观的代码,不希望把事情复杂化,并且愿意相信编译器会处理的很好。最终我还是(有些不情愿地)把代码改回了最初的 for(unsigned int i = 0; i < n; i++),呵呵,然而还是有些情绪无法释怀,嗨。
2009.8.22夜,liigo再次声明:我写此文并不是对C语言进行指责和抱怨,却是为自己写不出漂亮、简单且直观的代码而自责(不知道各位有没有同感,写出漂亮的代码会让人心情舒畅)。文中表达了压抑的心情,同时也是对心中压抑情绪的释放,是一种自我解脱方式。最后我跳出圈外,说出我多年来总结的稍微有些禅意的结论:当你使用者一个编程语言,出入于它的社区,就会受其思维模式的影响;而进入另一个领域时,相应地思维模式也会自动切换。思维模式的不同,会导致过程的不同和结果的不同。我相信这是一种比较接近于客观的表述,不具有主观倾向性。
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 [b]在本文之后的评论中给出的另一种写法:[/b]
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语言相关的两个问题让我心情压抑
- 重新安装系统之后遇到的WK相关的两个问题
- 重新安装系统之后遇到的WK相关的两个问题
- 菜鸟在C语言编译,链接时可能遇到的两个问题
- UART遇到两个与 stty 相关问题
- 菜鸟在C语言编译,链接时可能遇到的两个问题
- 菜鸟在C语言编译,链接时可能遇到的两个问题
- 关于C语言,字符数组中越界的两个问题
- 今天的遇到的两个问题
- 【ERROR_7】php发送mail时,遇到的两个问题
- C语言 指针相关问题--调用函数实现空间动态申请
- [C++]数据结构:散列表的相关概念、两个主要问题以及散列查找成功(不成功)的平均查找长度
- 栈增长和大端/小端问题是和CPU相关的两个问题.
- vue中遇到的坑之变化检测问题(数组相关)
- 2013-5-25 1. “已有打开的与此命令相关联的 DataReader,必须首先将它关闭。"问题. 上周在项目开发中,遇到这样的一个问题,在前台展示页,两个WebPart调用后台的AP
- 两个字符串的相关问题
- 烧入时遇到的相关问题集合
- 使用c函数库的两个函数strtok, strncpy遇到的问题记录
- 关于安装配置Android相关资源遇到的问题及相关解决方案
- 开发Birt过程中遇到的两个困扰我很久的问题?