Chinaunix

标题: 《C程序设计伴侣》里的月薪10000的代码 [打印本页]

作者: mirnshi    时间: 2012-10-06 20:33
标题: 《C程序设计伴侣》里的月薪10000的代码


月薪10000的程序员该挖个洞钻进去了。
作者: starwing83    时间: 2012-10-06 20:38
gets大亮………………一百字补丁.exe
作者: mirnshi    时间: 2012-10-06 20:47
starwing83 发表于 2012-10-06 20:38
gets大亮………………一百字补丁.exe


这可不是初学者的程序,这是月薪10000的。
你觉得处理好gets的程序员,价码几何?
作者: starwing83    时间: 2012-10-06 20:49
回复 3# mirnshi


    额,处理不好gets无颜做程序员吧……
作者: pmerofc    时间: 2012-10-06 20:53
提示: 作者被禁止或删除 内容自动屏蔽
作者: pmerofc    时间: 2012-10-06 20:59
提示: 作者被禁止或删除 内容自动屏蔽
作者: mirnshi    时间: 2012-10-06 21:05


嗯,这个更令人愉悦的代码。

不过,出书总是勇气可嘉的。小朋友们千万别当真,10000月薪的程序员写这类代码是要被鄙视而且打板子的
作者: 0xC1988    时间: 2012-10-06 21:06
最后的
  1. fp = NULL;
复制代码
大亮
作者: bruceteen    时间: 2012-10-06 21:40
本帖最后由 bruceteen 于 2012-10-06 22:48 编辑

//////////////////////////////////////////////////////////////
作者: mirnshi    时间: 2012-10-06 21:50
starwing83 发表于 2012-10-06 20:49
回复 3# mirnshi


刚才去品悟C了,也是N多溢出。赶紧自证一下,有溢出的不是你写的。



作者: 群雄逐鹿中原    时间: 2012-10-06 21:51
程序员这种超强体力劳动,过劳死不计其数的职业,月薪1W为业界楷模,真太悲催了
作者: mci2004    时间: 2012-10-06 21:55
BUGS
       Never  use gets().  Because it is impossible to tell without knowing the data in advance how many char-
       acters gets() will read, and because gets() will continue to store characters past the end of the  buf-
       fer,  it  is  extremely  dangerous  to  use.  It has been used to break computer security.  Use fgets()
       instead.

这是不用gets()的原因吗? 大牛们,吐槽的时候能不能把原因带详细一点,很多菜鸟在看。谢谢了
作者: starwing83    时间: 2012-10-06 22:32
回复 10# mirnshi


    我没写代码,只写了几篇论述性的文字
作者: ethantsien    时间: 2012-10-06 23:42
一看gets就知道这出书的根本没做过任何c项目
作者: pmerofc    时间: 2012-10-07 00:03
提示: 作者被禁止或删除 内容自动屏蔽
作者: pmerofc    时间: 2012-10-07 20:22
提示: 作者被禁止或删除 内容自动屏蔽
作者: gtkmm    时间: 2012-10-07 21:00
月薪10000的也是渣程序员了吧.
作者: pmerofc    时间: 2012-10-07 21:14
提示: 作者被禁止或删除 内容自动屏蔽
作者: cokeboL    时间: 2012-10-07 21:16
乔帮主神一般的代码,再送她个外号,叫“乔不思”吧。
作者: pmerofc    时间: 2012-10-07 21:27
提示: 作者被禁止或删除 内容自动屏蔽
作者: pmerofc    时间: 2012-10-07 21:39
提示: 作者被禁止或删除 内容自动屏蔽
作者: lancer31    时间: 2012-10-07 22:13
我一直觉得if (NULL == xxx)写法很恶心

当然,这是一个有争议的话题,因为其最早貌似好像大概是由某个大牛首次这样写的,然后其它小牛、嫩牛就开始东施效颦

据说是为了防止出现xxx=NULL的误写,但是既然都记得扭转脑子把xxx放在后面,那么怎么可能会把==误写成=?所以我觉得它恶心。
作者: pmerofc    时间: 2012-10-07 22:28
提示: 作者被禁止或删除 内容自动屏蔽
作者: lancer31    时间: 2012-10-07 22:50
把警告全开的话,误写成if (xxx = NULL) 是能够被检测出来的
所以if (NULL == xxx)这种写法 剩下的唯一作用就是装逼
作者: 0xC1988    时间: 2012-10-07 22:51
回复 18# pmerofc


   
在if(NUL!=fp)之前,不会出现fp为NULL的情况?


小乔的这个回复虽然无法论证if(NULL!=fp)的必要性,但是论证了常量前置的必要性了 也许小乔换个键盘就不会出现这种问题了,就可以写if(fp!=NULL)
作者: sonicling    时间: 2012-10-07 22:51
回复 20# pmerofc


    虽然那段代码不值10000,但是这4点也不算什么错误

丑不丑陋、有没有意义是主观态度,因人而异。

那句printf放哪里也是功能决定的。不带参数的cat命令没有任何提示,都没人说什么,我第一次用它都不知道怎么退出来(CTRL+D)。

多余的if判断。。。任何带有“消除公共子表达式”优化功能的编译器都能把它优化掉。

至于多不多余,只要能说出正当理由来就算行。

记得以前在公司,我写了个这样的代码:
  1. class B : public A {/*...*/ };

  2. B::B() [b]: A()[/b]
  3. {
  4.    //...
  5. }
复制代码
boss说粗体部分多余了,然后我一句话没说,老老实实改了,因为连狡辩的理由都没有。
作者: lancer31    时间: 2012-10-07 22:54
我自认对最大的开源代码Linux内核看得还是比较多,但真的很少看到有这样写if (NULL == xxx),当然我不敢说完全没有,毕竟Linux内核源码那么多。
作者: dengxiayehu    时间: 2012-10-07 23:33
lancer31 发表于 2012-10-07 22:50
把警告全开的话,误写成if (xxx = NULL) 是能够被检测出来的
所以if (NULL == xxx)这种写法 剩下的唯一作 ...

受林博士的高质量编程影响,以前也这么干,倒也不是为装X,居然习惯了。
别扭地倒腾回去写上NULL == ,确实费劲。

现在觉着这样写比较丑陋,语句拖得老长,还不如 !fp 来的短炼。

风格问题见仁见智,觉着“顺”就行了,不会这个也有标准吧

作者: pmerofc    时间: 2012-10-08 00:28
提示: 作者被禁止或删除 内容自动屏蔽
作者: Ager    时间: 2012-10-08 01:36
本帖最后由 Ager 于 2012-10-08 01:40 编辑
lancer31 发表于 2012-10-07 22:13
据说是为了防止出现xxx=NULL的误写,但是既然都记得扭转脑子把xxx放在后面,那么怎么可能会把==误写成=?所以我觉得它恶心。


顶一下 @lancer31 大侠:)

P.S.:曾经的受Perl毒害骚年代码:
  1. #define PERLC

  2. #include <stdio.h>

  3. #ifdef PERLC
  4. #define eq ==
  5. #define ne !=
  6. #define var void*
  7. #endif

  8. int main(void) {

  9.         var $_;

  10.         printf("%d --- %d\n", $_ ne $_, $_ eq $_);

  11.         return 0;

  12. }
复制代码

作者: sonicling    时间: 2012-10-08 02:01
本帖最后由 sonicling 于 2012-10-08 02:14 编辑

@pmerofc
好吧,这的确是个问题。
作者: starwing83    时间: 2012-10-08 02:47
回复 24# lancer31


    警告真的是个好东西~~

我不止一次因为警告的原因发现代码里面的memcmp被我顺手写成了memcpy。

所以自己的代码能用const就尽量带上const,如果写起来觉得麻烦干脆就直接定义CXXX的typedef了~
作者: Ager    时间: 2012-10-08 02:52
本帖最后由 Ager 于 2012-10-08 02:52 编辑
dengxiayehu 发表于 2012-10-07 23:33
受林博士的高质量编程影响,以前也这么干,


国内受影响养成这个习惯的,可能更大的源头是《C陷阱与缺陷》。

如果-Wall的话,一些正确的 if(t=s) 也会遭遇警告,不过这样也不坏,呵呵:)




作者: starwing83    时间: 2012-10-08 06:52
回复 33# Ager


    事实上我个人推荐如果真的要在if里面写赋值,通常是这么个写法:

  1. if ((d = s) != 0)
复制代码
或者简单地打两个括号:

  1. if ((d = s))
复制代码
都可以避免那个警告的。
作者: lancer31    时间: 2012-10-08 07:20
各位早 没想到这么多人回复我啊
作者: lancer31    时间: 2012-10-08 07:44
更多的是初学者才这么用:if (NULL == xxx)
真正的老手也许会图新鲜使用几次 之后会发现得不偿失 所以也就摒弃不用了
作者: lancer31    时间: 2012-10-08 07:46
代码除了编译为程序 也是给人看的 让人脑子扭转看着 一片恶心 不是什么好事
作者: mirnshi    时间: 2012-10-08 10:18
lancer31 发表于 2012-10-08 07:44
更多的是初学者才这么用:if (NULL == xxx)
真正的老手也许会图新鲜使用几次 之后会发现得不偿失 所以也就 ...


这个只是个人风格问题,无伤大雅。不能因为喜欢古典而批评摇滚。
作者: lilinly225    时间: 2012-10-08 10:28
哈哈哈, 我笑了
作者: lilinly225    时间: 2012-10-08 10:34
这就是“名校”生的好处,所以上学一定要上名校,考试一定要考公务员,出书一定要出“伴侣”类的书籍(里面一般再带些少儿不宜的内容,卖点就更高了),
作者: mirnshi    时间: 2012-10-08 10:37
Ager 发表于 2012-10-08 02:52
国内受影响养成这个习惯的,可能更大的源头是《C陷阱与缺陷》。

如果-Wall的话,一些正确的 if(t=s) ...

-Wall 依赖于编译器的版本。低版本的gcc可以完全通过,到了高版本就会出现警告错误了。不过-Wall可以最大限度地指出程序的隐患。
作者: noword2k    时间: 2012-10-08 11:08
明显的缓存区溢出漏洞啊。
作者: koolcoy    时间: 2012-10-08 11:28
0xC1988 发表于 2012-10-06 21:06
最后的大亮

一般情况下这么写不会影响性能{:3_189:}

  1. hoolala@ubuntu:/tmp$ cat t.c
  2. #include <stdio.h>

  3. int main() {
  4.         FILE* file = fopen("file.txt", "w");
  5.         fclose(file);
  6.         file = NULL;
  7.         return 0;
  8. }
  9. hoolala@ubuntu:/tmp$ gcc t.c -S -O2
  10. hoolala@ubuntu:/tmp$ cat t.s
  11.         .file        "t.c"
  12.         .section        .rodata.str1.1,"aMS",@progbits,1
  13. .LC0:
  14.         .string        "w"
  15. .LC1:
  16.         .string        "file.txt"
  17.         .section        .text.startup,"ax",@progbits
  18.         .p2align 4,,15
  19.         .globl        main
  20.         .type        main, @function
  21. main:
  22. .LFB22:
  23.         .cfi_startproc
  24.         subq        $8, %rsp
  25.         .cfi_def_cfa_offset 16
  26.         movl        $.LC0, %esi
  27.         movl        $.LC1, %edi
  28.         call        fopen
  29.         movq        %rax, %rdi
  30.         call        fclose
  31.         xorl        %eax, %eax       <<======== 这里是设定返回值
  32.         addq        $8, %rsp           <<======== 退栈操作
  33.         .cfi_def_cfa_offset 8
  34.         ret                       <<=========  函数返回,可见file = NULL这句完全被优化掉了
  35.         .cfi_endproc
复制代码

作者: starwing83    时间: 2012-10-08 11:43
回复 43# koolcoy


    不是性能问题。这么写的人明显脑子不清醒。你敢交给他重要任务么
作者: koolcoy    时间: 2012-10-08 11:45
本帖最后由 koolcoy 于 2012-10-08 11:46 编辑

回复 44# starwing83

我只是说一下,书上的代码你跟它扣质量多没意思啊,闭上眼睛一抓都是一大把问题的
   
作者: starwing83    时间: 2012-10-08 11:50
回复 45# koolcoy


    想当年看APUE,那个代码是多么优美啊……别说抓bug,有些代码你就那么看都不一定能明白它的原理,还得配上文字说明。
作者: dengxiayehu    时间: 2012-10-08 12:39
starwing83 发表于 2012-10-08 11:43
回复 43# koolcoy

初衷可能是抱着“指针在不用时要将其栓住(置为NULL),避免野指针”。

不过在这里多此一举,比较教条。
作者: starwing83    时间: 2012-10-08 12:52
回复 47# dengxiayehu


    关键是,这条规则应该是,非局部的指针应该置为NULL,避免野指针。因为局部指针本身在生命期之后都会被释放掉的。另一个可能的问题是局部指针有可能在其后继续被使用。这种情况下适用的是另一个教条:尽量限制局部变量的作用域。

教条主义其实不是错,可是不分青红皂白地教条就只能叫愚蠢了。
作者: mirnshi    时间: 2012-10-08 13:56
starwing83 发表于 2012-10-08 11:43
回复 43# koolcoy


不是不清醒,而是没有跨过那个门槛。
作者: yybmsrs    时间: 2012-10-08 14:34
回复 30# Ager


    大侠,变量名不是不能带$符号吗?测试了下好像可以啊。。。
作者: apachewebser    时间: 2012-10-08 14:41
没看懂                 
作者: apachewebser    时间: 2012-10-08 14:41
没仔细看                  
作者: pmerofc    时间: 2012-10-08 16:19
提示: 作者被禁止或删除 内容自动屏蔽
作者: Ager    时间: 2012-10-08 20:43
本帖最后由 Ager 于 2012-10-08 20:44 编辑

@yybmsrs

yybmsrs 发表于 2012-10-08 14:34
回复 30# Ager

大侠,变量名不是不能带$符号吗?测试了下好像可以啊。。。


有钱($)能使鬼(GNU)推磨,听说过没?

好吧,正经点,你在编译选项中,-pedantic或-pedantic-errors,钱就不管用了:)




作者: pmerofc    时间: 2012-10-08 21:18
提示: 作者被禁止或删除 内容自动屏蔽
作者: pmerofc    时间: 2012-10-08 21:29
提示: 作者被禁止或删除 内容自动屏蔽
作者: sonicling    时间: 2012-10-08 21:57
回复 55# pmerofc


    这么简单的程序,拿放大镜找错误有点吹毛求疵之嫌。改出花来也没实际意义。

值不值10000月薪,大家心里自有公论,一笑了之就行了。

说实话,本人觉得那个月薪1000的看架势有拿月薪10000的潜质。
作者: pmerofc    时间: 2012-10-08 22:03
提示: 作者被禁止或删除 内容自动屏蔽
作者: whereisps    时间: 2012-10-08 23:33
裹脚布该扔掉就扔掉,搞得随处溢出。全世界的Linux都禁止使用gets,现在却还有人不厌其烦地用gets。基本的IO函数都用不明白,都泻了,还出书,坑爹呀!找个泥坑洗洗,别丢人现眼了。

Never use gets().  Because it is impossible to tell without knowing the data in advance how many characters gets() will read, and because gets() will continue to store characters past the end of the buffer, it is extremely dangerous to use.  It has been used to break computer security.  Use fgets() instead.
作者: whereisps    时间: 2012-10-08 23:35
pmerofc 发表于 2012-10-08 21:29
C11之前 gets() 一直是C语言的标准函数

C89那个年代,似乎没有什么人对这个函数提出异议



作者: lancer31    时间: 2012-10-08 23:57
本帖最后由 lancer31 于 2012-10-08 23:59 编辑
whereisps 发表于 2012-10-08 23:33
裹脚布该扔掉就扔掉,搞得随处溢出。全世界的Linux都禁止使用gets,现在却还有人不厌其烦地用gets。基本的I ...


水平太矬,不说的话,反正大家也都不知道,但一显摆就露馅了,所以你也别恶心了,我们早就恶心过了。


作者: Ager    时间: 2012-10-09 00:53
本帖最后由 Ager 于 2012-10-09 00:59 编辑
mci2004 发表于 2012-10-06 21:55
BUGS

这是不用gets()的原因吗? 大牛们,吐槽的时候能不能把原因带详细一点,很多菜鸟在看。谢谢了
  1. /* EX2-5.c */
  2. #include <stdio.h>
  3. #include <string.h>
  4. int main() {
  5.         char buffer[256];
  6.         printf("Enter your name and press enter\n");
  7.         gets(buffer);

  8.         printf("\nYour name has %d characters and spaces!", strlen(buffer));
  9.         return 0;
  10. }
  11. /* End of Program */

复制代码



作者: starwing83    时间: 2012-10-09 02:11
回复 56# pmerofc


    对于非玩具级别的程序来说,替代gets的函数是早就有了,libedit,libreadline和liblinenoise都是的。他们都是安全的获得一行输入的最理想方式。而且还附带很多功能,诸如命令行编辑、历史、补全等等。
作者: Ager    时间: 2012-10-09 04:14
本帖最后由 Ager 于 2012-10-09 04:21 编辑
starwing83 发表于 2012-10-09 02:11
对于非玩具级别的程序来说,替代gets的函数是早就有了,libedit,libreadline和liblinenoise都是的。他们都是安全的获得一行输入的最理想方式。而且还附带很多功能,诸如命令行编辑、历史、补全等等。
  1. /*
  2. *   cc -g tinypipe.c -o tinypipe -ledit -ltermcap
  3. *   */

  4. #include <stdlib.h>
  5. #include <stdio.h>
  6. #include <unistd.h>
  7. #include <histedit.h>

  8. void tinypipe(const char *line) {

  9.         char buf[512];
  10.         FILE *pp;
  11.         if ((pp = popen(line, "w")) == NULL) {
  12.                 printf("Error during Pipe Open!\n");
  13.                 exit(1);
  14.         }
  15.         while (fgets(buf, sizeof buf, pp)) {
  16.                 printf("%s", buf);
  17.         }
  18.         if (pclose(pp) == -1) {
  19.                 printf("Error during Pipe Close!\n");
  20.                 exit(1);
  21.         }

  22. }

  23. char * prompt(EditLine *e) {
  24.         return "TinyPipe> ";
  25. }

  26. int main(int argc, char *argv[]) {

  27.         EditLine *el;

  28.         History *myhistory;

  29.         int count;
  30.         const char *line;
  31.         int keepreading = 1;
  32.         HistEvent ev;

  33.         el = el_init(argv[0], stdin, stdout, stderr);
  34.         el_set(el, EL_PROMPT, &prompt);
  35.         el_set(el, EL_EDITOR, "vi");

  36.         myhistory = history_init();
  37.         if (myhistory == 0) {
  38.                 fprintf(stderr, "history could not be initialized\n");
  39.                 return 1;
  40.         }

  41.         history(myhistory, &ev, H_SETSIZE, 2000);

  42.         el_set(el, EL_HIST, history, myhistory);

  43.         while (keepreading) {
  44.                 line = el_gets(el, &count);

  45.                 if (count > 0) {
  46.                         history(myhistory, &ev, H_ENTER, line);
  47.                         tinypipe(line);
  48.                 }
  49.         }

  50.         history_end(myhistory);
  51.         el_end(el);

  52.         return 0;
  53. }
复制代码
随手写个小Pipe(绝对是玩具,各位不必当真),从SW大虾那里续个貂,只是为了推介一下BSD Command Line Editor Library。

不知这个属于几个钱程序员档次…… 呵呵:)








作者: mci2004    时间: 2012-10-09 10:16
fgets 就是很好的替代品了吧。 《C专家编程》大概第二章吧,就有写。话说当年这个gets() 引发了一场互联网的安全危机。
作者: mirnshi    时间: 2012-10-09 10:31
回复 64# Ager


知道libreadline的很多,知道libedit的就少了。去GPL还有很长的路要走。
代码写得很整洁,推介BSD的,按照BSD的代码规范来写,就锦上添花了。


作者: Ager    时间: 2012-10-09 10:41
mirnshi 发表于 2012-10-09 10:31
回复 64# Ager

知道libreadline的很多,知道libedit的就少了。去GPL还有很长的路要走。
代码写得很整洁,推介BSD的,按照BSD的代码规范来写,就锦上添花了。


顶一下 …… :)

但不知国内行情如何。看当下,小乔代表1100万学子共续前缘,图灵公司祭谭子大帜,这路要怎么走……?


作者: mirnshi    时间: 2012-10-09 11:21
Ager 发表于 2012-10-09 10:41
顶一下 …… :)

但不知国内行情如何。看当下,小乔代表1100万学子共续前缘,图灵公司祭谭子大帜, ...


出书是为了赚钱而已,国内的书鲜有良品。写C程序防止溢出是基本原则之一,诸如gets之类的溢出都搞不定,还谈什么好书。将语法弄得滚瓜烂熟,写程序有溢出,叫卖声再高也叫毁人。


作者: lenky0401    时间: 2012-10-09 12:29
C版可喜可贺啊 有这么多书出版
作者: sublx    时间: 2012-10-09 21:25
pmerofc 发表于 2012-10-07 21:27
  这段代码有几处低级错误
  
  1.


1.
  
  char str[100] = "";
  这个初始化是毫无意义的
  这一点比前面的所谓“月薪1000元的程序员的代码”还要差


请教:char str[100] = ""; 比 char str[100];差在哪里?
作者: pmerofc    时间: 2012-10-09 21:32
提示: 作者被禁止或删除 内容自动屏蔽
作者: pmerofc    时间: 2012-10-09 21:34
提示: 作者被禁止或删除 内容自动屏蔽
作者: pmerofc    时间: 2012-10-09 21:39
提示: 作者被禁止或删除 内容自动屏蔽
作者: mci2004    时间: 2012-10-09 21:43
pmerofc 发表于 2012-10-09 21:39
fgets()在一定程度上确实可以替代gets()
但是它和gets()有一点差别
gets()不会存储\n


‘\n’  嗯 , 这个确实忽略了,多谢指点
作者: bruceteen    时间: 2012-10-09 21:57
pmerofc 发表于 2012-10-09 21:39
fgets()在一定程度上确实可以替代gets()
但是它和gets()有一点差别
gets()不会存储\n

fgets必须被设计为保留\n,否则没法知道是缓存满了,还是行结束了。
作者: sublx    时间: 2012-10-09 21:58
pmerofc 发表于 2012-10-09 21:32
因为没必要做这个初始化


没必要初始化,也不见得比另外一种写法“差”吧...
作者: isaacxu    时间: 2012-10-09 22:00
尽管让普通的程序员了解代码安全的问题需要一个过程,但gets()真不该用了,具体的可以参考一下Secure Coding in C and C++这本书。书中有关gets()的内容,被美国的
国土安全部门放在其网站上,原文:The gets() function is a common source of buffer overflow vulnerabilities and should never be used. The fgets() and gets_s() functions each offer a more secure solution.
作者: 346196247    时间: 2012-10-09 22:02
本帖最后由 346196247 于 2012-10-09 22:52 编辑

大神们都是,代码比武啊实力啊,
,虽然我看不懂
作者: pmerofc    时间: 2012-10-09 22:12
提示: 作者被禁止或删除 内容自动屏蔽
作者: pmerofc    时间: 2012-10-09 22:15
提示: 作者被禁止或删除 内容自动屏蔽
作者: starwing83    时间: 2012-10-09 22:29
回复 80# pmerofc


    我觉得倒是够了,只需要你自己能处理\n即可。perl的方案是一个trim函数,你可以让你的处理方式是whitespace-clean的就行了。
作者: pmerofc    时间: 2012-10-09 22:33
提示: 作者被禁止或删除 内容自动屏蔽
作者: mci2004    时间: 2012-10-09 22:50
回复 22# lancer31


    赞一个
作者: mirnshi    时间: 2012-10-09 22:53
回复 81# starwing83

fgets是取代gets的最简单的方式,而且功能上最近。
另一种方式就是用scanf了,稍微复杂一些。
   
作者: starwing83    时间: 2012-10-09 23:09
回复 82# pmerofc


    学一下APUE。

单独起一章论述gets的坏处,最后给一个简单的安全实现,比如叫readline吧,自己实现一个超级简单的。接口可以这么设计:

  1. char *readline(const char *prompt);
复制代码
返回的指针是个内存,你可以自行free掉。

然后以后的章节,就直接包含#include "readline.h"即可。

然后你可以在网上提供已经写好的代码包神马的。
作者: pmerofc    时间: 2012-10-09 23:18
提示: 作者被禁止或删除 内容自动屏蔽
作者: starwing83    时间: 2012-10-09 23:37
回复 86# pmerofc


    这里有个非常简单的readline实现,应该很容易解释吧?
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>

  4. #define RL_BLOCKSIZE   80

  5. char *readline(const char *prompt) {
  6.     char *buff = (char*)malloc(RL_BLOCKSIZE);
  7.     size_t capacity = RL_BLOCKSIZE;
  8.     size_t len = 0;

  9.     printf("%s", prompt != NULL ? prompt : "> ");

  10.     while (fgets(buff + len, capacity - len, stdin) != NULL) {
  11.         char *newbuff;
  12.         if ((len += strlen(buff + len)) != capacity - 1) {
  13.             if (buff[len - 1] == '\n')
  14.                 buff[len - 1] = '\0';
  15.             return buff;
  16.         }
  17.         if ((newbuff = realloc(buff, capacity*=2)) == NULL)
  18.             break;
  19.         buff = newbuff;
  20.     }

  21.     free(buff);
  22.     return NULL;
  23. }

  24. /* usage */

  25. int main(void)
  26. {
  27.     char *s;
  28.     while ((s = readline(NULL)) != NULL) {
  29.         printf("s = %s\n", s);
  30.         free(s);
  31.     }
  32.     return 0;
  33. }
复制代码

作者: mirnshi    时间: 2012-10-10 09:35
starwing83 发表于 2012-10-09 23:37
回复 86# pmerofc


C很灵活,解决问题的方法很多,为了解决gets问题,readline这条路稍微有些长了。
作者: pmerofc    时间: 2012-10-10 10:25
提示: 作者被禁止或删除 内容自动屏蔽
作者: starwing83    时间: 2012-10-10 11:05
本帖最后由 starwing83 于 2012-10-10 11:07 编辑

回复 89# pmerofc


    其实写书也并非非要严格的介绍所有的细节,你可以这么写:

因为gets的不安全性,所以在实际的代码中,当需要用户输入完整的一行时,我们会使用其他的函数来达到这个目的。一个实际的例子是由libreadline库提供的readline函数。这个函数会返回一个包含了动态分配的、用户完整输入的行的指针。本书的附录A给出了一个最简单的readline的实现,如果要编译本书中给出的例子,直接将这个函数粘贴到文件的最开始即可。大家在学习C语言的大部分知识点以后,可以去附录A大致了解这个函数是如何实现的。以后当我们的代码需要处理用户整行的输入时,都会使用这个函数。现在你只需要知道有这么一个函数可以安全地完成你想要的功能就可以了。
作者: pmerofc    时间: 2012-10-10 11:20
提示: 作者被禁止或删除 内容自动屏蔽
作者: starwing83    时间: 2012-10-10 11:31
回复 91# pmerofc


    写书又不需要便捷………………

我以前做过一个EditPlus+GCC的开发环境。你也可以大致上配一个,然后放到网站上,然后偷偷地链接一个-lreadline
作者: starwing83    时间: 2012-10-10 11:36
话说,要求最简单的话…………

char buff[1024];

if (fgets(buff, 1024, stdin) != NULL) {
   size_t len = strlen(buff);
   if (buff[len-1] == '\n')
       buff[len-1] = '\0';
   /* use it */
}

应该没法更简单了吧…………
作者: mirnshi    时间: 2012-10-10 11:39
starwing83 发表于 2012-10-10 11:36
话说,要求最简单的话…………

char buff[1024];


复杂。用scanf,更简单。
作者: starwing83    时间: 2012-10-10 12:03
本帖最后由 starwing83 于 2012-10-10 12:10 编辑

回复 94# mirnshi


    怎么弄?

scanf("%1024[^\n]%*[\n]", buff);

这样?

问题是,如果最后一次人家不输入\n输入的是EOF你就傻逼了……

UPDATE:试验了一下,输入EOF好像木有问题,奇怪了,当初是遇到啥情况了……反正这么写scanf在某种情况下似乎会出错……

哦哦哦,想起来了,是这样的,如果用户不输入,直接按回车,就完蛋了……因为这个回车没地方吃…………

继续UPDATE:试过了,scanf(" %1024[^\n]"是可以的,注意那个空格……
作者: mirnshi    时间: 2012-10-10 12:43
回复 95# starwing83

呵呵,一个scanf要update二次。另,注意scanf的返回值。通常情况下,使用scanf族,需要检查返回值的。很多人经常忽略函数返回值,比如月薪10000的程序员。

补充:由于scanf可以限长获取字符串,在某些情况下,余留的数据可以用下面的语句清空:
while ((ch = getchar()) != EOF && ch != '\n');





   
作者: starwing83    时间: 2012-10-10 13:02
回复 96# mirnshi


    这样难道不比我的麻烦么?

还有,ACMer就没有不检查scanf的返回值的……想WA还是想SE了…………已经习惯做题的时候先打while (scanf("%d%d%d", &k, &m, &n) == 3) {...了……
作者: mirnshi    时间: 2012-10-10 15:28
回复 97# starwing83

一行scanf也很复杂?
   
作者: starwing83    时间: 2012-10-10 16:10
回复 98# mirnshi


    我是说,要写while((ch = getchar()) == '\n。。。。啥的那一长串……
作者: mirnshi    时间: 2012-10-10 16:25
回复 99# starwing83

这个是为了清缓冲的。fgets读超长,也会留下未读数据的。
   




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