12个C语句问题,你能给出怎样的解答?

billow_zhang 2009-07-15 10:12:41
下面是我从实际的系统摘录的12个问题程序。既然在系统中存在并运行,当然是编译通过了的。你能看出它们都有什么问题吗?想想看,是否你也会写这样的程序。我在 http://blog.csdn.net/billow_zhang/archive/2009/07/15/4351756.aspx中给出了我的解答。有异议的可以给出评论。

1.
if ( strlen(s) == 0 )
if ( strlen(s) )
if ( strcmp(s, “”) == 0 )

2.
memcpy(s1, s2, 1);

3. .
for ( i = 0; i < strlen(s); i++ )

4.
printf("OK\n");

5.
char sbuf[1024];

memset(sbuf, 0, sizeof sbuf);
sprintf(sbuf, "select %s from %s where %s = ", col_name, table_name, key);
strcat(sbuf, value);


6.
str[strlen(str)] = '\0';

7.
sprintf(curtime, "%02d:%02d:%02d:%03d", tt->tm_hour, tt->tm_min, tt->tm_sec, tb.millitm);
curtime[12] = 0;

8.
if ( ch == 1 ) {
..........
}
if ( ch == 2 ) {
...........
}
if ( ch == 3 ) {
..........
}

9.
nRet=lockWait(c_sem_id);
if(nRet!=0)
{
usleep(100000);
nRet=lockWait(c_sem_id);
if(nRet!=0)
{
usleep(100000);
nRet=lockWait(c_sem_id);
if(nRet!=0)
{
logger->fatal("lock Virtual teller config failed”);
nRet=unlockWait(c_sem_id);
return -1;
}
}
}

10
#define SBUF_DBLMIN (-1e20) /* 最小double值 */

char sbuf[16];
double double_val;

………..接收一字符串到sbuf中。
double_val = atof(sbuf);
if ( double_val == SBUF_DBLMIN )
return -1;

11
下面是我从一个复杂的输出一系列二进制数据为16进制字符的一段简化的程序,其中c为简化的这个二进制数据的一个字节。

char sbuf[3];
char c;

c已经存放了一个字节的二进制数。
sprintf(sbuf, “%.2x”, c);

12
void rtrim( char* str )
{
while( strlen(str) && str[strlen(str)-1] == ‘ ‘ )
str[strlen(str)-1] = '\0';
}

void ltrim( char* str )
{
while( str[0] == ‘ ‘ )
strcpy( str, str+1 );
}





...全文
270 30 打赏 收藏 转发到动态 举报
写回复
用AI写文章
30 条回复
切换为时间正序
请发表友善的回复…
发表回复
mu_yang 2009-07-20
  • 打赏
  • 举报
回复
billow_zhang先生过奖
主要是主帖的内容好
主帖本身就是经过认真思考得到的
同时也留下了继续思考的空间

C善于表现美
所以代码从思想到形式都应该是美的
在这一点我和billow_zhang先生是一致的
我们都属于C语言的美学派(呵呵,如果有这个派的话)

遗憾的是
神州大地充斥着C语言的丑学派的代码
这个丑学派
如果不能说它的头子是潭浩强
至少可以说它的根子是潭浩强
最保守的,可以说潭浩强是根子之一
这里暂且不谈他对语言理解上的谬误和硬伤
mu_yang 2009-07-19
  • 打赏
  • 举报
回复
把代码写的乱七八糟
然后把一起交给编译器打理
指望编译器优化出一个好的程序
我不能认同这种程序员
billow_zhang 2009-07-19
  • 打赏
  • 举报
回复
给上面的朋友集中的写了一篇答复:http://blog.csdn.net/billow_zhang/archive/2009/07/19/4361464.aspx

本想结贴,看看在坛子里还靠前。谢谢朋友们的评论。
先再留两天吧!

最感谢的是mymtom和mu_yang朋友。他们的评论最认真。

狐帝 2009-07-18
  • 打赏
  • 举报
回复
[Quote=引用 19 楼 mymtom 的回复:]
3.
for ( i = 0; i < strlen(s); i++ )
每次循环的循环判断中都有strlen的函数调用,显然降低效率。
改善如下:
len = strlen(s);
for ( i = 0; i < len; i++ )


这个很更有意思了,编译器不会把strlen(s)的值放在寄存器里?
当然前提是你在循环中没有改变s,

[/Quote]
被你说着了,某些编译器还真有可能不把strlen(s)的值放在寄存器里,除非你启用某种级别的优化。我前不久就遇到过类似的情况,当时没想到这个编译器这么笨(当然也许他有另外的考虑),曾经搞得我一头雾水。


[Quote=引用 22 楼 udsking 的回复:]
1.
# define isNotEmpeyStr(s) ( (s)[0] != ‘\0’ )
# define isEmptyStr(s) ( (s)[0] == ‘\0’ )

这样可以用上面两个宏来判断字符串是否为空,或者字符串是否不为空。
对于
char *str = (char*)malloc(3);
str[0] = 0x00;
str[1] = 'a';
str[2] = 'b';
我想你这个宏是判断不出来的吧?我只想说,并不是起始字符是0的就一定是空字符串。
[/Quote]
C语言定义字符串的结尾字符就是0x00,所以,strlen找到0x00字符时,就会认为这是字符串的结尾。如果str[0]为0x00,那么strlen(str)的返回值就是0,也就是说,str是空字符串。所以,我认为用(str[0] == 0)来判断字符串是否为空是正确的。


[Quote=引用 22 楼 udsking 的回复:]
4.
printf("OK\n");
对于没有格式控制的输出,最好使用puts,fputs等替代。这样有更高的效率。
puts("OK");
我没跟puts和printf里面具体汇编指令是什么,但这两个语句汇编代码是一样的,
9:        printf("OK\n");
0040F538  push        offset string "OK\n" (00420020)
0040F53D  call        printf (004011a0)
0040F542  add        esp,4
10:      puts("OK");
0040F545  push        offset string "OK" (0042001c)
0040F54A  call        puts (004010b0)
0040F54F  add        esp,4
8.

[/Quote]
由于都只用了一个实参,所以调用调用本身看上去是一样的。但是printf为了实现格式化输出,要做很多工作,比puts麻烦多了。所以,如果只是为了输出确定的字符串,的确用puts更加高效。
cheng_fengming 2009-07-18
  • 打赏
  • 举报
回复
好贴,先顶一下再说!
tkminigame 2009-07-18
  • 打赏
  • 举报
回复
觉得最后一个这样写更高效
char* ltrim(char *str)
{

while( *str == ' ' )
str++;

return str;


}
tkminigame 2009-07-18
  • 打赏
  • 举报
回复
学习,lz有经验。
mu_yang 2009-07-18
  • 打赏
  • 举报
回复
[Quote=引用 14 楼 billow_zhang 的回复:]
7. mu_yang朋友不会做算术了?
"%02d:%02d:%02d:%03d"

2 + 1 + 2 + 1 + 2 + 1 + 3 = ?

反正我算的结果是12. 你算的是几啊? 这进一步说明了curtime[12]这样写法的恶劣,让人连算术都算不准了.

[/Quote]

愚昧
我没注意那两个 “:”
见笑了

不过我想到一个新问题
实际上"%02d:%02d:%02d:%03d"的长度不一定是12
很可能大于12
这种情况下我认为12的作用更坏
heguodong 2009-07-17
  • 打赏
  • 举报
回复
9.
nRet=lockWait(c_sem_id);
if(nRet!=0)
{
usleep(100000);
nRet=lockWait(c_sem_id);
if(nRet!=0)
{
usleep(100000);
nRet=lockWait(c_sem_id);
if(nRet!=0)
{
logger->fatal("lock Virtual teller config failed”);
nRet=unlockWait(c_sem_id);
return -1;
}
}
}
这种涉及到判断的,尽量使用短路
const int trytimes = 3;
int try = 0;

while( ( (nret = lockWait(c_sem_id)) != 0 ) && (try++ < trytimes) )
usleep(1e5);

if ( nret >= 0 )return -1;//在这里短掉,程序流线性更好,参考<代码大全>
unlockWait(c_sem_id);
logerr->fatal("lock Virtual teller config failed");
heguodong 2009-07-17
  • 打赏
  • 举报
回复
8.
if ( ch == 1 ) {
..........
}
if ( ch == 2 ) {
...........
}
if ( ch == 3 ) {
..........
}
这类问题最需要处理的以多态取代条件式子,或者用状态取代这种魔法数
microsoftq 2009-07-17
  • 打赏
  • 举报
回复
[Quote=引用 15 楼 heguodong 的回复:]
8.
if ( ch == 1 ) {
..........
}
if ( ch == 2 ) {
...........
}
if ( ch == 3 ) {
..........
}
这类问题最需要处理的以多态取代条件式子,或者用状态取代这种魔法数
[/Quote]

同意,感觉这样草率地用if else 或者 switch 在很多地方欠妥当。
mymtom 2009-07-17
  • 打赏
  • 举报
回复
7.
sprintf(curtime, "%02d:%02d:%02d:%03d", tt->tm_hour, tt->tm_min, tt->tm_sec, tb.millitm);
curtime[12] = 0;
写这个程序段的人很小心,已经习惯了。这个没有坏处,如果不信,请看:


cat mymtom.c && cc mymtom.c -o mymtom && ./mymtom
#include <stdio.h>

int
main(void)
{
char buf[16];
(void)sprintf(buf, "%02d:%02d:%02d:%03d", 1, 2, 999, 888);
(void)printf("%s\n", buf);

return 0;
}
01:02:999:888

mymtom 2009-07-17
  • 打赏
  • 举报
回复
3.
for ( i = 0; i < strlen(s); i++ )
每次循环的循环判断中都有strlen的函数调用,显然降低效率。
改善如下:
len = strlen(s);
for ( i = 0; i < len; i++ )


这个很更有意思了,编译器不会把strlen(s)的值放在寄存器里?
当然前提是你在循环中没有改变s,
mymtom 2009-07-17
  • 打赏
  • 举报
回复
1.
if ( strlen(s) == 0 )
if ( strlen(s) )
if ( strcmp(s, “”) == 0 )

改善:
# define isNotEmpeyStr(s) ( (s)[0] != ‘\0’ )
# define isEmptyStr(s) ( (s)[0] == ‘\0’ )

这个,根本不是问题,21世纪的编译器编译出来的代码不会有问题。
在不打开优化的情况都都没有去调用strlen,strcmp

2.
memcpy(s1, s2, 1);
对于一个字节的拷贝,竟然也调用memcpy。看来,C语言可以删除赋值的语法功能了。
改善:
s1[0] = s2[0];


gcc 4.x.x 在-O1的情况下,不会调用memcpy,直接赋值。
udsking 2009-07-17
  • 打赏
  • 举报
回复
1.
# define isNotEmpeyStr(s) ( (s)[0] != ‘\0’ )
# define isEmptyStr(s) ( (s)[0] == ‘\0’ )

这样可以用上面两个宏来判断字符串是否为空,或者字符串是否不为空。
对于
char *str = (char*)malloc(3);
str[0] = 0x00;
str[1] = 'a';
str[2] = 'b';
我想你这个宏是判断不出来的吧?我只想说,并不是起始字符是0的就一定是空字符串。

2.
memcpy(s1, s2, 1);
对于一个字节的拷贝,竟然也调用memcpy。看来,C语言可以删除赋值的语法功能了。
改善:
s1[0] = s2[0];

不同编译器有自己不同的优化方法。
3.应该和2是相同的问题,编译器可能会对其进行优化的,要不也不可能会有volatile变量了吧。

4.
printf("OK\n");
对于没有格式控制的输出,最好使用puts,fputs等替代。这样有更高的效率。
puts("OK");
我没跟puts和printf里面具体汇编指令是什么,但这两个语句汇编代码是一样的,
9: printf("OK\n");
0040F538 push offset string "OK\n" (00420020)
0040F53D call printf (004011a0)
0040F542 add esp,4
10: puts("OK");
0040F545 push offset string "OK" (0042001c)
0040F54A call puts (004010b0)
0040F54F add esp,4
8.
if ( ch == 1 ) {
..........
}
if ( ch == 2 ) {
...........
}
if ( ch == 3 ) {
..........
}

改善如下:
if ( ch == 1 ) {
..........
} else
if ( ch == 2 ) {
...........
} else
if ( ch == 3 ) {
……………
}

如果,中间的处理不复杂的话,可以使用switch语句。
对于互斥条件可以像你这么更改,楼上也有说过的。

其他的还是基本同意~只是发表一下个人的拙见!欢迎楼主指导
billow_zhang 2009-07-17
  • 打赏
  • 举报
回复
回8楼的linyt朋友:
你说的很有道理.缓冲区的溢出成为程序的重要隐患之一. 这也是snprintf这样的函数诞生的原因.
但平时的程序中, sprintf还是会经常使用的. 否则取消它就是了.
比如:

char sbuf[64];

sprintf(sbuf, "%d", i);
这里显然是安全的,如果使用, snprintf(sbuf, sizeof sbuf, "%d", i); 实在显得没有必要.

回到我的例子.我知道会有缓冲区的问题, 所以 sbuf我定义长达1024, 隐含的意思是这里缓冲区有足够长.所以,我这里讨论问题的重点不在于缓冲区的溢出问题.
但我认为你把这个问题提出来很好.

在平时确认缓冲区足够大的话,使用sprintf的理由就是,它比snprintf更简单,效率更高.
平时,我曾经告诉我手下的程序员们,如果你认为,你的逻辑里缓冲区只用10个字节,你就定义成32个字节长度. 至少是3倍以上.其实平时在使用很少的字节的地方,我都会定义成1024长度,特别是函数的局部变量,定义大一些又不会浪费内存, 不用白不用.

尽管这里缓冲区不是讨论的重点,但既然有朋友提出来了,也就是顺便提醒大家注意这个问题.
谢谢linyt朋友,给出很有意义的精彩的评论.

billow_zhang 2009-07-16
  • 打赏
  • 举报
回复
回2楼:
1. 想想看,当一个字符串有80个字节的话,使用strlen需要扫描这80个字节,而我只是要判断这个字符串是否为空,判断第一个字节就够了.判断80个字节,你不觉的很傻吗? 这样的程序当然丑陋了,可不简单是习惯问题.另外,多进行了函数调用,又是一个不利的因素.
2. 你要那么说的话,那我就是吹毛求疵了.不过我还看过这样的程序:
double d1, d2;

memcpy(&d1, &d2, 8);
是不是你会认为double的长度可能会变化,所以需要使用memcpy啊!?

5. 我碰到的程序就是sbuf立即使用的情况啊!我的问题当然就局限于我搜集的这些真实案例.
8. 同意你的观点.但是否使用switch不是这个问题的核心.核心问题是: 当第一个判断为真的话,下面的判断已经是多余的了.使用else语句可以避免这个多余的判断.这里是被我简化了,实际的程序一共有8个判断.

回mu_yang朋友:
4. 不知道你看过printf的源码没有.其实没看过,也能根据它的功能想象到,因为它需要进行输出的格式控制,所以会扫描格式控制字串,寻找控制字符,并匹配后面的参数.而如果,你的输出并非需要格式控制,不需要printf的格式化功能,当然使用puts或fputs效率更高了.

谢谢mu_yang朋友在我文章的评论,我已经给了答复,请见文章的评论.


billow_zhang 2009-07-16
  • 打赏
  • 举报
回复
回9楼mu_yang朋友: 十分感谢你的评论和关注.

5.
所谓strcat感觉自然的问题,那就是认识问题了.特别是对于C语言,追求效率已经是高级程序员们追求程序美的特征之一.往往追求的那点效率确实是微不足道的.所以,他们会感觉到在可以使用strcpy的地方,却使用strcat是感觉不美的,反倒是不自然的.就好像我常常看到许多程序定义空间: char buf[1000], 而我觉得 char buf[1024]是更美的.这应该是感觉问题,而不是是否拿得准的问题.当然,我理解,strcat让人一下子就能看出是向末尾添加字串,有更好的可读性. 但是,对我来说,这个优点还是战胜不了我对它的缺乏效率的丑陋感.顺便说一下,现实中的那个程序是一个很复杂的字符串编辑,充满了大量的strcat, 最后编辑的字符串有几乎1k字节. 我想,那个最后的strcat该有多累啊?!

7. mu_yang朋友不会做算术了?
"%02d:%02d:%02d:%03d"

2 + 1 + 2 + 1 + 2 + 1 + 3 = ?

反正我算的结果是12. 你算的是几啊? 这进一步说明了curtime[12]这样写法的恶劣,让人连算术都算不准了.

8. 这里的例子是被我简化了.实际的情况是:

if ( 很复杂的判断语句1 ) {
.....这里也有较长的语句;
}

if ( 很复杂的判断语句2 ) {
......
}

..........

所以这个语句是不能使用switch语句的. 当时程序有bug, 我调试了几乎一天的时间.这个语句的逻辑问题,让我有所误解.我将那些判断语句仔细做了分析,发现其中的判断语句完全是互斥的,也就是我简化的那个情况, 可以用else来进行分割,立刻让我清楚了判断逻辑,将程序的问题缩小了范围. 这也是我说,加上else逻辑更清楚了的意思.也就是说,经过了第一个判断为真的话,就不会运行到第二个判断了.而如果没有使用else的话,这个关系就不清楚了.可能由于我简化后的例子太简单了,让你感觉不到这个问题.

9. 浮点数确实有难以捉摸的地方,但这不是回避它的理由. 况且我这里完全是使用它的一个表达方法的,实际上还是整型数.很多朋友都说惧怕C语言里的指针,尽量不使用指针.那你说,这些朋友还能算是C程序员吗?至少不能算是专业程序员了.
所以惧怕不是理由,惧怕是说明你对它的了解缺乏信心.另外,你说的 100000与1e5不是一回事,把我吓一跳,还专门在机器上去试了一下,后来还仔细数了一下100000是不是0的个数写错了.
另外,这个问题的核心,不知道你没考虑成熟什么,是说有比我写的程序有更好的方法吗?其实,这个问题,你只要认识到原来的写法有问题,就已经够了.当然,我也期盼着你能写出比我更好的程序.






海枫 2009-07-16
  • 打赏
  • 举报
回复
[Quote=引用楼主 billow_zhang 的帖子:]

char sbuf[1024];

memset(sbuf, 0, sizeof sbuf);
sprintf(sbuf, "select %s from %s where %s = ", col_name, table_name, key);
strcat(sbuf, value);

[/Quote]

这样的代码非常不安全,是源于sprintf和strcat函数会产生缓冲区溢出,应尽理用snprintf和strncat(或strlcat)来代替它们,更改后的代码应类似如下:

char sbuf[1024];

//snprintf函数保证sbuf是'\0'结尾的
snprintf(sbuf, sizeof(sbuf),"select %s from %s where %s = ", col_name, table_name, key);
//如果生成的字符刚好是sizeof(sbuf)个字符,则需要把最后一个字符置为'\0'
strncat(sbuf, value, sizof(sbuf));
sbuf[sizeof(sbuf) - 1 ] = '\0'

或者最后两句完全可以这样写成一句:
//strlcat的语义与snprintf类似,目标字符串一定是'\0'结尾的
strlcat(sbuf, value, sizeof(sbuf));

这样就很安全了,如果value是用户的输入,那么当用户输入很长的串时,此前的代码可能会产生缓冲区溢出。安全问题,不容忽视!
billow_zhang 2009-07-16
  • 打赏
  • 举报
回复

会7楼liushac朋友:开始关注这个,说明你开始关注程序之美的问题了.

加载更多回复(10)

69,369

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧