免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 27581 | 回复: 14
打印 上一主题 下一主题

[C] 初中级C语言水平自测:srand((int)time(0)); 有没有错? [复制链接]

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
1 [报告]
发表于 2012-09-25 00:19 |显示全部楼层
这能有什么(技术性的)错?

1. p = 0 与 p = NULL (假设p是指针类型)难道还会有区别不成?
只有在编译器不知道类型的情况下 —— 也就是省略原型与可变长参数 —— 它们在C中才会有区别。

并且这种情况下写NULL还不一定就是好习惯:
i) 写0在C或C++里都错
ii) 而写NULL在C++中肯定错,在C里依然不一定对 —— 我觉得这还不如前面的都错的结果。
iii) 只有(T*)0才是在C与C++中都对的写法。


2. 关于(int),写为(unsigned)或者干脆不写直接srand(time(0))也行。
time_t规定必须是算术类型 —— 也就是说不会像pthread_t那样存在被定义为结构体的可能性 —— 肯定能转换。
不进行错误处理,直接将(time_t)-1当作种子也是可以接受的结果。

剩余的可能的错误就是time_t是浮点并且time(0)的返回值超出了unsigned的可表示范围。。。

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
2 [报告]
发表于 2012-09-25 01:00 |显示全部楼层
回复 6# pmerofc

确实应该改为(unsigned)或者干脆不写转型。
每次看到pthread_create( ... , (void*)f, (void*)x , ... ) 这样的代码就感到无奈,这究竟是谁教的。。。
—— 也就是说应该这么做。


至于这么做的理由,要UB的话都是,怎么会一个是一个不是?

还有种子的问题,不一定会减少一半:

1. time_t -> int -> unsigned(srand的参数) 不一定就是 time_t -> unsigned 的一半
得看time_t的类型,还有time的返回值范围。


2. 即使只有一半
srand(i), i = [0, UINT_MAX/2) 对比 srand(i), i = [0, UINT_MAX],后者也不一定就会有更好的分布。
得看srand,rand是怎么实现的。


3. 哪怕将代码改为srand(0)(不仅仅是一半,而是UINT_MAX分之一),也不能肯定程序就一定是错的。

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
3 [报告]
发表于 2012-09-27 15:45 |显示全部楼层

《C专家编程》中毁硬件的例子

http://book.51cto.com/art/200803/68542.htm

未定义的行为在IBM PC中引起CPU瘫痪!

未定义的软件行为引起CPU瘫痪的说法并不像它乍听上去那样牵强。

IBM PC的显示器以显示控制芯片所提供的水平扫描速率工作。回扫变压器(flyback transformer,一种产生高电压的装置,用于加速电子以点亮显示器上的荧光物质)需要保持一个合理的频率。

然而在软件中,程序员有可能把视频芯片的扫描速率设置成零,这样就会产生一个恒定的电压输出到回归变压器的输入端。这就使它起了电阻器的作用,只是把电能转换成热能,而不是传送到屏幕。这会在数秒之内就把显示器烧毁,那就是未定义的软件行为会导致系统瘫痪的理由。


嗯,其实我没看出这与undefined behavior有什么关系。。。

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
4 [报告]
发表于 2012-09-27 16:16 |显示全部楼层

避免所有undefined behavior?

要避免所有undefined behavior并不容易。。。


@starwing83,既然Lua的邮件列表里提到了,我在想是不是因为Lua源代码里已经这么写了。。。
存在lua number -> c integral, 并且没有检查范围。

我估计很多需要大量处理浮点数的库,也不会对每一个long double -> double, double -> float 都进行检查。

并且。。。  请各位摸着良心说。。。  自己写的代码里有没有这样的情况:
1. 有符号数相加(或相乘!)
2. 相加前没有检测
3. 也没有去证明加法的结果一定没有超出该有符号数的表示范围


这也是undefined behavior:
C99 6.5 Expressions

5 If an exceptional condition occurs during the evaluation of an expression
(that is, if the result is not mathematically defined or not in the range of representable values for its type),
the behavior is undefined.

C89 6.3 EXPRESSIONS
...
If an exception occurs during the evaluation of an expression
(that is, if the result is not mathematically defined or not representable),
the behavior is undefined.
...


然后,在知道这是undefined behavior的情况下,不能再以无知者无罪为借口的情况下,会去将代码里的每一处有符号数加法都:
1. 证明它一定不会溢出
2. 或者在相加前检测
吗? 对检测失败的情况又该怎么处理?


我感觉这代价就太大了一些。。。  会开始考虑换 i) 有异常处理的 ii) 可以重载 + 的 语言了。。。
除0(mathematically defined)我想一般都会处理,因为一般都会立即产生错误。。。



回到srand(time(0))。不知道具体implementation的情况下, time_t有可能是浮点数, 也就有可能产生undefined behavior。
但如果一定要避免, 该怎么做?

  1. time_t t = time(0);
  2. if ( 0 <= t && t <= UINT_MAX ) srand(t);
  3. else { /* 该怎么办。。。 */ }
复制代码

  1. srand( fmod( fabs(time(0)) , UINT_MAX - 1 ) );
复制代码
不如前面那个罗嗦, 但依然很罗嗦。。。 而且不一定就没有问题。。。 (要是UINT_MAX-1 比 DBL_MAX 大该怎么办。。。 有这种可能性么?)


并且还得分两种情况: i) 这是实际代码,需要很好的正确性的保障(但这种情况srand/rand合适么?) ii) 这是教学代码并且主要目的、重点根本就不在srand/rand上。。。
如果这样写, 初学者看到了就不会对此有疑问?

@pmerofc,就类似 char const* , 严格的说应该这么写。
但有时候代码的重点不在这里, 需要避免一下子给初学者抛出太多的信息, 而是应该各个击破, 于是可能就先选择 char* 了。
你也理解的对吧?

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
5 [报告]
发表于 2012-09-27 16:25 |显示全部楼层
回复 30# sacry

但C语言并没有说scran rate为0是undefined behavior。
嗯,其实C语言里根本就不会有scran rate这个概念。。。


就是说,这个例子并没有说明是C语言里的undefined behavior(floating->integral超出范围、或者其他)造成硬件损坏的。
假设scran rate是一个特殊的内存地址,并且是算术类型。设置它的值为0在C语言里是正确的行为。
是这个硬件的spec不允许而造成的损坏。


不知道原文是什么。。。 也有可能是翻译出的问题。。。

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
6 [报告]
发表于 2012-09-27 18:05 |显示全部楼层
回复 34# sacry

Great!

我勤快,弄个排字版:
Undefined Behavior Causes CPU Meltdown in IBM PC's!

The suggestion of undefined software behavior causing CPU meltdown isn't as farfetched as it first appears.
The original IBM PC monitor operated at a horizontal scan rate provided by the video controller chip. The flyback transformer (the gadget that produces the high voltage needed to accelerate the electrons to light up the phosphors on the monitor) relied on this being a reasonable frequency.
However, it was possible, in software, to set the video chip scan rate to zero, thus feeding a constant voltage into the primary side of the transformer. It then acted as a resistor, and dissipated its power as heat rather than transforming it up onto the screen. This burned the monitor out in seconds. Voilà: undefined software behavior causes system meltdown!


呃,观点还是前面的:
1. 这其实是在说"undefined software behavior", 以及与C语言的undefined behavior无关了
2. "set the video chip scan rate to zero" 的行为是良好定义的, 肯定能把它设置为0, 否则就不会烧毁了。
这段文字前面还有一段话:
since the behavior is undefined, the compiler is free to do anything and doesn't even have to warn you about it!

但设置为0, 编译器是必须老老实实做这件事的。


总之,C语言的undefined behavior引起严重事故的可能性不能说绝对没有。
但IBM的这个不能作为论据,它不是C语言的undefined behavior引起的,而且正是因为C语言是规规矩矩听了程序员的话、做了该做的事才会被烧毁的。。。

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
7 [报告]
发表于 2012-09-27 18:10 |显示全部楼层
回复 35# isaacxu

这个才有点C语言的undefined behavior引起事故的赶脚。。。

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
8 [报告]
发表于 2012-10-15 15:35 |显示全部楼层
回复 76# pmerofc

>> 它对实参的要求是一个time_t*类型的指针,而time(0)的实参0是int类型,两者类型显然不匹配。这个调用正规的C写法应该是time(NULL),time(0)通常是C++代码中的写法。

说这种话有失你水准。

至于(int)time(0),这是画蛇添足无误。
但它会不会错? 前面这么多楼都没能让你明白rand对问题产生的影响。。。
这最多算是有争议的。

换个类比。
假设一个libc的实现,里面的qsort可以正确的完成排序工作,但用的一个复杂度是平方级的算法。那这个qsort算不算错?
再假设一个libc++的实现,里面的std :: sort同样实现了一个平方级的正确的算法, 这个std :: sort 又算不算错?

qsort和sort,C和C++里显然会对其正确性有明确的要求。
但对性能, sort是被明确要求了的, 所以上面说的那个libc++就是的。
而对qsort的例子, 性能要求是很暗昧不明的, 因为C没有要求qsort的性能。 上面的libc完全符合C标准, 能说它错了吗?

rand类似, 在C和C++里没有对rand产生的分布有任何描述(也许C++11的新的random机制有)。
那既然用了rand, 这个程序本来对随机数的分布的要求就是暗昧的了。
能够准确刻画出的要求就是: 程序在某时刻运行得到结果A, 那程序在后续的某些次运行中能得到与A不同的结果。 除此之外你还能怎么刻画程序的行为?
而无论是(int)time(0)还是time(0)都达到这个要求了。



总之,错是个很严重的词。
首先肯定要将认为有问题的地方说出来 —— 这点你是做到了的。
然后说明这个行为到底违反了什么约定 —— 对这个case,争议的地方就是这里。
是不应该这么写。 就像那些没有意义的多余的memset一样。 但没法说这些代码是错的。

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
9 [报告]
发表于 2012-10-21 03:44 |显示全部楼层
回复 128# pmerofc

我说"有失你水准"是指"它对实参的要求是一个time_t*类型的指针,而time(0)的实参0是int类型,两者类型显然不匹配。"

要这么说的话char c = 'c'; 在C中类型也显然是不匹配的。 包括char z = '\0'; 在C里类型依然是不匹配的。
memcpy(buf, "source", size); 类型还是不匹配。

char Z = (char)0; memcpy(buf, (void const*)"source", size); 才是正确的风格?


至于哪个是C的风格,哪个是C++的风格。。。
风格问题对我来说实在是太无聊、太蛋疼了。原本就没有依据,争论也没意思。

论坛徽章:
2
青铜圣斗士
日期:2015-11-26 06:15:59数据库技术版块每日发帖之星
日期:2016-07-24 06:20:00
10 [报告]
发表于 2012-10-22 08:31 |显示全部楼层
回复 148# pmerofc

如果想让初学者知道这里存在转换,应该把原型贴出来,而不是把转换写到代码里。

0和NULL只是风格, 而memcpy(buf, (void *)"source", size); <- 就算得上烂习惯了。
比char const* p = "source"; memcpy(buf, p, size); 更烂。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP