Chinaunix

标题: 最近总犯低级错误 [打印本页]

作者: net_robber    时间: 2006-12-21 16:19
标题: 最近总犯低级错误
就刚才,一段调了一下午的代码

就是因为一个很低级的错误:



  1. char buf[256];
  2. memset(  buf,  0,  265 );
  3. strcpy( buf, str ......
  4. ......
  5. memset(  buf,  0,  265 );
  6. recv ( s,  buf, 265 .....
  7. ....
复制代码

作者: converse    时间: 2006-12-21 16:20
这个故事告诉我们定义一个宏或者是常量有多么重要....
作者: prc    时间: 2006-12-21 16:23
友情灌水
作者: sillydog825    时间: 2006-12-21 16:44
可以用sizeof吗?
作者: cjaizss    时间: 2006-12-21 16:56
原帖由 sillydog825 于 2006-12-21 16:44 发表
可以用sizeof吗?

当然可以,但是要注意sizeof求长度只对数组,传一个指针是不可求数组长度的
作者: lanying_wzw    时间: 2006-12-21 17:44
哈哈,是够弱的,2楼的老兄所言即是啊
作者: flw2    时间: 2006-12-21 17:58
这样的情况是用一个宏.而不是去 sizeof
作者: net_robber    时间: 2006-12-21 21:11
对,应该用宏

如果这段内存我是从堆里面动态申请的,就没有办法用sizeof了

申请常量是非常必要的
作者: whyglinux    时间: 2006-12-22 09:37
>> 这样的情况是用一个宏.而不是去 sizeof

>> 对,应该用宏

至少对楼主的程序而言,用 sizeof 比定义宏要更好一些。

能不用宏的情况下就不要使用宏,这是人们的经验总结,也应该作为一条应该坚持的规则。这是因为宏有太强的“穿透力”,从它定义的地方开始到一个编译单位结束它都在起作用,由此可能会带来各种意想不到的问题。

因此,如果在楼主的程序中使用宏,应该限定宏的作用范围,这样的使用才是比较安全的:
  1. #define BUFFER_SIZE 256
  2. char buf[BUFFER_SIZE];
  3. /* ... */
  4. #undef BUFFER_SIZE
复制代码

但是,特别是当宏作为 整型常量 使用的时候,由于总能找到代替宏的实现方案,所以在程序中 永远不要使用宏来定义整型常量

在 C 语言中,定义整型常量应该使用枚举(如 enum { BUFFER_SIZE = 256 };)。

在 C++ 中,除了枚举之外,还可以定义 const 常量来使用(int const BUFFER_SIZE = 256;)。
作者: 绿茶主演    时间: 2006-12-22 09:56
没看懂楼上的
作者: net_robber    时间: 2006-12-22 10:05
原帖由 whyglinux 于 2006-12-22 09:37 发表
>> 这样的情况是用一个宏.而不是去 sizeof

>> 对,应该用宏

至少对楼主的程序而言,用 sizeof 比定义宏要更好一些。

能不用宏的情况下就不要使用宏,这是人们的经验总结,也应该作为一条应该 ...



还能用 enum!!!???

意外收获,哈哈!!!

谢谢了

扔了一块砖,捡了个玉回来。

赚了,哈哈!!
作者: moaotian    时间: 2006-12-22 10:20
原帖由 whyglinux 于 2006-12-22 09:37 发表
>> 这样的情况是用一个宏.而不是去 sizeof

>> 对,应该用宏

至少对楼主的程序而言,用 sizeof 比定义宏要更好一些。

能不用宏的情况下就不要使用宏,这是人们的经验总结,也应该作为一条应该 ...




说这话的完全是一个激进的程序设计者,其实对于宏的讨论已经不是一时半会的事情了,根本也不会有是否要禁止的必要,就像goto语句的存在和消灭,至今也不会有什么结果。
宏的作用不只是定义常量那么简单,另外在定义常量方面其独有的优势也是不可比拟的
#define  SECONDS_IN_ONE_YEARS   (365*24*60*60)UL,请问即不用宏,又不能占用CPU运行时,那么你打算怎么存储?---难道是调出计算器,计算出365*24*60*60的结果res,然后const unsigned long SeconsInOneYears = res?????
还有其他更多的实例说明,排斥语言特性或叫诋毁其实是一个程序员在追起编程理念时的不成熟表现,语言永远是辅助的,错也不在语言上,就像现在的骂人脏话,难道是脏话出了问题???其实是说的人自己的问题
作者: albcamus    时间: 2006-12-22 11:32
我还干过这种事呢:


  1. for ( i = 0; i < 2n; i++)
  2. {
  3.   ...
  4. }
复制代码


然后对着这行代码发了2小时呆,不懂为什么编译器说有错
作者: r2007    时间: 2006-12-22 11:53
原帖由 albcamus 于 2006-12-22 11:32 发表
我还干过这种事呢:


  1. for ( i = 0; i < 2n; i++)
  2. {
  3.   ...
  4. }
复制代码


然后对着这行代码发了2小时呆,不懂为什么编译器说有错

数学学多了
作者: net_robber    时间: 2006-12-22 11:59
albcamus:

你的签名我怎么看不懂呢????
作者: whyglinux    时间: 2006-12-22 12:21
To moaotian

>> 说这话的完全是一个激进的程序设计者

要看清我说的话的意思。我说的是“能不用宏的情况下就不要使用宏”。

>> 排斥语言特性或叫诋毁其实是一个程序员在追起编程理念时的不成熟表现

问题在于:为了解决某一问题,如果有两种实现方案可供选择,一种安全,一种可能带来不可预测的问题,你会怎样进行选择?在方案选择上你会给出什么样的建议?

在整型常量的表示上,一个用宏,一个用 enum,你认为哪种更安全?既然其中 enum 更加安全,不容易出问题,在这种情况下还有必要非得使用宏吗?

在一些系统级代码中,其早期的代码中对整型常量大量使用宏定义。不过到了现在,其中的非常多的实现已经将整变量的宏定义用枚举类型来代替了。为什么要这样做?

>> 宏的作用不只是定义常量那么简单,另外在定义常量方面其独有的优势也是不可比拟的

同意你的这前半句话,但是不同意你的后半句话,因为在定义整型常量方面和 enum 相比宏在功能实现上不具备优势,反而没有 enum 安全。你在上面所说的理由也站不住脚,因为 enum 常量也可以写成常量表达式(见下面)。

#define SECONDS_IN_ONE_YEARS   (365*24*60*60)UL 的写法有问题,(x)UL 是什么语法?应该这样定义吧:
#define SECONDS_IN_ONE_YEARS   (365UL*24*60*60)。

如果用 enum 的话,可以这样写:
enum { SECONDS_IN_ONE_YEARS  = 365UL*24*60*60 };。

最后再强调一遍:在程序中 永远不要使用宏来定义整型常量。如果你不赞成这个观点,或者你在程序中使用了宏来定义整型常量,那才是真正不够成熟的表现。
作者: zx_wing    时间: 2006-12-22 12:37
原帖由 r2007 于 2006-12-22 11:53 发表

数学学多了


牛B
作者: zx_wing    时间: 2006-12-22 12:47
原帖由 whyglinux 于 2006-12-22 12:21 发表
To moaotian

>> 说这话的完全是一个激进的程序设计者

要看清我说的话的意思。我说的是“能不用宏的情况下就不要使用宏”。

>> 排斥语言特性或叫诋毁其实是一个程序员在追 ...


>>永远不要使用宏来定义整型常量
个人认为理论上是这样的,但在实际编码时,往往宏来的要方便一些。其实很多大系统都有也有很多用宏定义的常量,例如linux、xen开源虚拟机。

虽然都说不要用宏定义一个常量,因为这是不安全的。但我还是想知道在什么时候不安全,有谁能为我举一个例子吗?(例子应该是这种用法不安全,不是宏写的不安全哈)。
作者: whyglinux    时间: 2006-12-22 14:37
>> 个人认为理论上是这样的,但在实际编码时,往往宏来的要方便一些。

有这种感觉,往往说明还没有体会到使用宏带来的危害性,或者还缺乏 enum 的使用经验。要习惯用 enum。

>> 其实很多大系统都有也有很多用宏定义的常量,例如linux、xen开源虚拟机。

很多是历史遗留问题(例如,在 C 标准制定之前 enum 还不存在,宏是可用的唯一手段),即使以后想对此修改,由于可能带来兼容性的问题还有巨大的工作量,都会使修改望而止步。多注意一下一些历史不是那么悠久的一些软件的代码,如 GTK+。

另外,你也不能保证程序员不犯错误,更不用说使用宏对程序本身来说并不是一种错误。

>> 虽然都说不要用宏定义一个常量,因为这是不安全的。但我还是想知道在什么时候不安全,有谁能为我举一个例子吗?(例子应该是这种用法不安全,不是宏写的不安全哈)。

宏的危害之一是会污染名字空间。例如,如果在自己的程序中声明了和宏相同名字的变量,可能会造成莫名其妙的编译错误。
作者: zx_wing    时间: 2006-12-23 14:15
原帖由 whyglinux 于 2006-12-22 14:37 发表
>> 个人认为理论上是这样的,但在实际编码时,往往宏来的要方便一些。

有这种感觉,往往说明还没有体会到使用宏带来的危害性,或者还缺乏 enum 的使用经验。要习惯用 enum。

>> 其实很多大系统 ...


>>宏的危害之一是会污染名字空间。

枚举如何做到不污染名字空间呢?


>>例如,如果在自己的程序中声明了和宏相同名字的变量,可能会造成莫名其妙的编译错误。

如果只是编译和链接时的错误,那就不算什么了。
作者: r2007    时间: 2006-12-23 14:23
原帖由 zx_wing 于 2006-12-23 14:15 发表


>>宏的危害之一是会污染名字空间。

枚举如何做到不污染名字空间呢?


>>例如,如果在自己的程序中声明了和宏相同名字的变量,可能会造成莫名其妙的编译错误。

如果只是编译和链接时的 ...

这么说只能无语了。

记得那个著名的对乔治.马洛里的采访—— 问:“你为什么要登山?” 答:“因为它就在那儿!”
Beacuse it's there.
作者: zx_wing    时间: 2006-12-23 14:27
原帖由 r2007 于 2006-12-23 14:23 发表

这么说只能无语了。

记得那个著名的对乔治.马洛里的采访—— 问:“你为什么要登山?” 答:“因为它就在那儿!”
Beacuse it's there.


呵呵,我有点刨根问底了哈。
唉,我看书就是这个习惯,再经典的教材,再大牛的人说的话。要是不能给个程序出来解释问题,我就不怎么相信。因为没有程序说明光靠脑袋里面想怎么都觉得靠不住,往往以为是这样子了,放到程序里却发现其实不是这样。
作者: net_robber    时间: 2006-12-23 14:41
在没有出现问题的时候,任何言论和做法都是正确的。

就好像说杀人可能会牙疼。

谁又来保证不会呢???

存在即是道理

OK,就此结束!
作者: whyglinux    时间: 2006-12-23 14:59
假设出于需要,在程序中添加了位于   /************************/ 之间的一段代码。在修改的代码中,用宏和 enum 定义 X 可给 test() 的执行带来不同的结果。


  1. #include <stdio.h>

  2. int X = 10;

  3. void test();

  4. int main( void )
  5. {
  6.   test();

  7.   /************************/
  8.   /* 后面维护程序的人添加了下列代码 */
  9. #if 1
  10. #define X 100
  11. #else
  12.   enum { X = 100 };
  13. #endif

  14.   printf( "%d\n", X );
  15.   /************************/

  16.   return 0;
  17. }

  18. void test()
  19. {
  20.   printf( "%d\n", X ); /* 写这个函数的人认为他使用的是全局的 X */
  21. }
复制代码

[ 本帖最后由 whyglinux 于 2006-12-23 15:00 编辑 ]
作者: zx_wing    时间: 2006-12-23 15:09
原帖由 whyglinux 于 2006-12-23 14:59 发表
假设出于需要,在程序中添加了位于   /************************/ 之间的一段代码。在修改的代码中,用宏和 enum 定义 X 可给 test() 的执行带来不同的结果。

[code]
#include <stdio.h>

int X = 10 ...


好!这个例子非常明白的说明问题了。
果然还是要用代码才能说明问题。谢了:wink:
作者: yiciyuan    时间: 2006-12-23 22:44
一点也不奇怪,你还没我犯错误的低级

  1. if (num == Max);
  2. {
  3.       DoSomething();
  4. }
复制代码


折腾我一个多小时,就是不明白DoSomething()为啥老是执行,最后才知道 if 判断允许空操作



作者: bierdaci    时间: 2006-12-26 00:29
if (num == Max);
{
      DoSomething();
}
这大概是很多写程序的人常犯的错误了
if (num == Max) {
    DoSomething();
}
这种风格也许会少犯些这样的错误
作者: yu330    时间: 2006-12-26 10:14
原帖由 bierdaci 于 2006-12-26 00:29 发表
if (num == Max);
{
      DoSomething();
}
这大概是很多写程序的人常犯的错误了
if (num == Max) {
    DoSomething();
}
这种风格也许会少犯些这样的错误



我还是喜欢
if (num == Max)
{
      DoSomething();
}
这种风格。
作者: cocoa1227    时间: 2006-12-26 10:50
原帖由 whyglinux 于 2006-12-23 14:59 发表
假设出于需要,在程序中添加了位于   /************************/ 之间的一段代码。在修改的代码中,用宏和 enum 定义 X 可给 test() 的执行带来不同的结果。

[code]
#include <stdio.h>

int X = 10 ...



版主内功深厚啊......................................
作者: moaotian    时间: 2006-12-26 11:28
whyglinux ,很高兴你能提出你的见解,我认为能在一个程序论坛能如此激烈和深刻的讨论一个问题是多么的美好,就像CSDN那种教学站点,我是实在没什么兴趣和他们唠叨,对于你提出的反驳相当的中肯和犀利,你所说的我认为很是正确,当然对于宏,我想我们应该采取容忍的态度,相信我,这不是试图说服你或者其他人要在怎么作,而是通过这种友善的争论——就像XP团队的第二条理念所说的:XP团队的程序员是要通过争论,甚至是面红耳赤的场面来讨论问题的,因为那就像头脑风暴一样有效。
曾经当我在vxWorks下编程时,我不小心看到了他的源代码的实现,我被其中巧妙的宏使用所吸引
知道后来我为PDA编写基于Wince的程序时,在C#环境下进行工作,我使用了大量的const来定义常量,我不得不说这两中方式都很棒,都是保证我的程序能轻易延展的好的方法。
当然,有一天,我为msp430和f020编写代码的时候我发现,硬件工程师告知我,由于其核心代码太大,留给我为GPRS编写驱动程序所需的内存空间仅有1K时,我不得不尽量的试图减少我对内存的扩展使用,那么当我的工作进行到最后发现内存还需要40bits时,我不得不尝试把所有常量的定义改为了enmu,这其实是个很好的主义,但是对于GPRS中需要的大量String信息,我还是不得不用#define来代替……或者这实在说明不了什么,因为我确实在其后的日子里在PC上编译程序时,我仍是很理智和谨慎的使用着C++中的template(我的系统STL大量使用)的同时,更习惯的使用了const定义,还是像原来那么美妙。
那么,#define就像他曾经存在过一样,我用他解决这问题,const却是像现在一样,是我的首选。
或者更多时候,我的程序——也不排斥任何能实现目的的设想
作者: wolf0403    时间: 2006-12-26 12:35
就像CSDN那种教学站点,我是实在没什么兴趣和他们唠叨
攻击 CSDN 不能证明你水平有多高。
作者: moaotian    时间: 2006-12-26 14:19
原帖由 wolf0403 于 2006-12-26 12:35 发表
就像CSDN那种教学站点,我是实在没什么兴趣和他们唠叨
攻击 CSDN 不能证明你水平有多高。



就像你的名字后缀的括号里描述的那样,你很称职
作者: newsoil    时间: 2006-12-26 17:45
用宏多好就是管理起来也快好多。
虽然多写了几个字母。
作者: 虑而后能得    时间: 2008-07-24 15:32
标题: 回复 #2 converse 的帖子
heh   多谢 由加深了一下体会
作者: sunorr    时间: 2008-07-24 15:57

作者: jigloo    时间: 2008-07-24 16:02
csdn怎么了,看看csdn的数据结构和算法版,CU比的上?
作者: kingsu    时间: 2008-07-24 16:24
此贴名为:天使之间的战争
作者: 节约用水    时间: 2008-07-24 16:30
版主不少啊
作者: lqseu    时间: 2008-07-24 18:04
呵呵,我也常常把字母或者数字敲反呢
作者: cheng_lai_shun    时间: 2008-07-24 18:59
我也会犯这样的错误!有时候头皮都抓破!
作者: cheng_lai_shun    时间: 2008-07-24 19:00
定义宏果然重要
作者: blackuhlan    时间: 2008-07-24 21:33
函数原型:
int check_str( char *pszSource, int len);
调用:
if( (check_str( buf ), sizeof(buf) )
{
...
}

调试了5遍不明白它怎么老是返回真
作者: lenovo    时间: 2008-07-24 22:44
原帖由 blackuhlan 于 2008-7-24 21:33 发表
函数原型:
int check_str( char *pszSource, int len);
调用:
if( (check_str( buf ), sizeof(buf) )
{
...
}

调试了5遍不明白它怎么老是返回真

你什么意思?
能编译通过么?
作者: root@China    时间: 2008-07-24 23:29
难得糊涂
作者: blackuhlan    时间: 2008-07-24 23:32
原帖由 lenovo 于 2008-7-24 22:44 发表

你什么意思?
能编译通过么?

在同一个代码中,没有申明原型,能编译通过
作者: xp5211314    时间: 2008-07-25 00:07
原帖由 blackuhlan 于 2008-7-24 21:33 发表
函数原型:
int check_str( char *pszSource, int len);
调用:
if( (check_str( buf ), sizeof(buf) )
{
...
}

调试了5遍不明白它怎么老是返回真



if( (check_str( buf ), sizeof(buf) )
应为 if( check_str( buf , sizeof(buf)))

作者: blackuhlan    时间: 2008-07-25 00:29
标题: 回复 #46 xp5211314 的帖子
是的,你说的很对




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2