搜索
查看: 1343|回复: 19
打印 上一主题 下一主题

编译通过,验证时输入有问题,求解。

[复制链接]
跳转到指定楼层
楼主
发表于 2014-8-30 15:47:18 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
5啊哈币
本帖最后由 嗨,强哥! 于 2014-8-30 16:06 编辑

[mw_shl_code=c,true]#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define TSIZE 48
struct film
{
        char title[TSIZE];
    int rating;
    struct film * next;
};

int main(void)
{
        struct film * head = NULL;
    struct film * prev, * current;
        char input[TSIZE];
   
    //收集并存储信息
    puts("Enter first movie title: ");
    while (gets (input) != NULL && input[0] != '\0');//gets()返回地址有可能是NULL?
    {
                current = (struct film *) malloc (sizeof (struct film));
        if (head == NULL)
                        head = current;
        else
                        prev -> next = current;
        current -> next = NULL;
        strcpy (current -> title, input);//实际运行时,输入在这里卡住,要敲两次回车才会继续,怎么回事?
        puts ("Enter your rating(0~10): ");
        scanf ("%d", current -> rating);
        while (getchar () != '\n')//这里没有看懂,难道是输入空行退出?
                        continue;
        puts ("Enter next movie title (empty line to stop): ");
        prev = current;
    }
     
    //给出电影列表
    if (head == NULL)
                printf ("No data enter.");
    else
                printf ("Here is the movie list:\n");
        current = head;
    while (current != NULL)
    {
                printf ("Movie: %s Rating: %d\n",current -> title, current -> rating);
        current = current -> next;
    }
        
    //任务完成,释放内存
    current = head;
    while (current != NULL)
    {
                free (current);   
        current = current -> next;     
    }      
    printf ("Bye.\n");  
     
        system("pause");
        return 0;
}
[/mw_shl_code]

最佳答案

查看完整内容

[mw_shl_code=c,true]while (gets (input) != NULL && input[0] != '\0');[/mw_shl_code] 目测多了分号 [mw_shl_code=c,true]scanf ("%d", current -> rating);[/mw_shl_code] 取地址运算符掉了 修改后的代码: [mw_shl_code=c,true]#include #include #include #define TSIZE 48 struct film { char title[TSIZE]; int rating; struct film * next; }; int main(void) { struct film * hea ...
沙发
发表于 2014-8-30 15:47:19 | 只看该作者
[mw_shl_code=c,true]while (gets (input) != NULL && input[0] != '\0');[/mw_shl_code]
目测多了分号
[mw_shl_code=c,true]scanf ("%d", current -> rating);[/mw_shl_code]
取地址运算符掉了
修改后的代码:
[mw_shl_code=c,true]#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define TSIZE 48
struct film
{
    char title[TSIZE];
    int rating;
    struct film * next;
};

int main(void)
{
    struct film * head = NULL;
    struct film * prev = NULL, * current;
    char input[TSIZE];
   
    //收集并存储信息
    puts("Enter first movie title: ");
    while (gets (input) != NULL && input[0] != '\0')
    {
        current = (struct film *) malloc (sizeof (struct film));
        if (head == NULL)
            head = current;
        else
            prev -> next = current;
        current -> next = NULL;
        strcpy (current -> title, input);
        puts ("Enter your rating(0~10): ");
        scanf ("%d", &current -> rating);
        while (getchar () != '\n')//输入空行退出
            continue;
        puts ("Enter next movie title (empty line to stop): ");
        prev = current;
    }
   
    //给出电影列表
    if (head == NULL)
        printf ("No data enter.");
    else
        printf ("Here is the movie list:\n");
    current = head;
    while (current != NULL)
    {
        printf ("Movie: %s Rating: %d\n",current -> title, current -> rating);
        current = current -> next;
    }
   
    //任务完成,释放内存
    current = head;
    while (current != NULL)
    {
        free (current);
        current = current -> next;
    }
    printf ("Bye.\n");
   
    system("pause");
    return 0;
}[/mw_shl_code]

点评

一个小小的;,就让程序程序莫名其妙的改变。它让while空循环。C语言真的是太信任程序员了,哈哈。。  发表于 2014-9-2 10:24
板凳
 楼主| 发表于 2014-8-30 16:11:26 | 只看该作者
这两天在学习链表,上面的例题是《C Primer Plus》490页例题,仔细核对了代码,没有发现错误。实际验证时输入却出现了问题,不知为何,特求大侠解答。万分感谢。

地板
发表于 2014-8-30 17:03:09 | 只看该作者
要注意编译器的warning
5#
发表于 2014-8-30 17:57:25 | 只看该作者
首先回答你注释里面那两个问题吧
1. gets可以返回NULL,在读取错误时;或者没有读取任何字符,直接碰到EOF的时候
2. while(getchar() != '\n')是清空缓冲区,例如一次的输入是
6 回车
scanf读取了那个6,剩下了一个回车,如果不清理掉,会被下一轮循环的gets读取
6#
发表于 2014-8-30 18:27:46 | 只看该作者
本帖最后由 rosynirvana 于 2014-8-30 20:58 编辑

另外,这段代码的问题挺多,首先就是不该用链表,链表的效率比大多数人想象的要低不少。
每个单元都要通过一个函数来分配内存,这个函数要在程序的堆内存里找一个合适的区域,如果堆内存不够了还要向操作系统申请增大,这些都要耗费不少时间。
不过这里先跳过这点,先说说其他问题

1. struct film
{
    char title[TSIZE];
    int rating;
    struct film * next;
};
这样定义非常死板,每次给节点分配内存,写入电影名字的时候,字符串的长度已经知道了,没必要在这里预设固定长度,建议写成
  1. typedef struct film{
  2.   char* title;
  3.   int rating;
  4.   struct film* next;
  5. }Film;
复制代码


struct film * head = NULL;
struct film * prev, * current;
这里,事实上不需要prev和current两个指针,只需要一个current就行了。两个指针只在插入删除这种稍微复杂点的操作里才是必要的。
这里把head设置成NULL的代价是,之后循环中每次都要判断是不是头节点,如果链表很长(不长也没有必要用链表,数组好多了),代价就比较高。
一般的解决方法是,把head设置成一个哑节点或者说哨兵或者说伪节点(英文说成dummy node / sentry)
head不存储任何数据,只是为了减少循环中一个if判断
代码是
  1. Film* head = malloc(sizeof(Film));
  2. Film* current;
  3. current = head
复制代码


gets是不好的,除了OJ那种只求速度另外明确知道数据范围的场合之外,不该用gets,应该用fgets
  1. while(fgets(input, TSIZE, stdin)){
  2.     int len = strlen(input);
  3.     char* buf;
  4.     if(len <= 1)
  5.       break;
  6.     /* fgets会把回车一起拷贝进来,注意 */
  7.    
  8.     buf = malloc(len);
  9.     /* 给电影名申请空间,一个char一个字节,所以内存大小就是电影名长度 */
  10.     memcpy(buf, input, len-1);
  11.     buf[len-1] = '\0';

  12.     current->next = malloc(sizeof(Film));
  13.     current = current->next;
  14.     current->title = buf;

  15.     /* 其他代码基本相同 */
  16. }
复制代码


在主函数返回前手动释放内存是不建议的,因为这件事情操作系统自己做会做得更好,除非是在非常低的层面上工作

如果要尝试着手动释放内存,注意这里结构体里的title也要回收
7#
发表于 2014-8-30 18:33:05 | 只看该作者
比较合理的做法是写一个自增长的数组,有心情了来写
8#
发表于 2014-8-30 18:35:17 | 只看该作者
rosynirvana 发表于 2014-8-30 18:27
另外,这段代码的问题挺多,首先就是不该用链表,链表的效率比大多数人想象的要低不少。
每个单元都要通过 ...

也就是说一般不应该在程序中调用free函数和delete操作符?
9#
发表于 2014-8-30 18:41:38 | 只看该作者
981013 发表于 2014-8-30 18:35
也就是说一般不应该在程序中调用free函数和delete操作符?

不是:
如果这时候除了释放堆内存和return 0之外没别的事情了

1. 释放内存 -> 主函数返回
2. 主函数直接返回,不释放内存,留给系统自己做

选第二种是比较合理的
10#
发表于 2014-8-30 18:49:09 | 只看该作者
另外也有人支持在return 0前面手动释放内存,他们说这是一种好习惯

习惯要分场合,在某些吃完饭要把盘子送到专门回收的地方,但是大多数经营类饭店不要你这么做
11#
发表于 2014-8-30 21:03:56 | 只看该作者
本帖最后由 rosynirvana 于 2014-8-30 21:20 编辑
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>

  4. #define TSIZE 48

  5. typedef struct film{
  6.         char* title;
  7.         int rating;
  8. } Film;

  9. typedef struct{
  10.         Film* data;
  11.         int cap;
  12.         int used;
  13. } ArrayList;

  14. int main()
  15. {
  16.         ArrayList films;
  17.         char input[TSIZE];
  18.         int i;
  19.         films.cap = 32;
  20.         films.used = 0;
  21.         films.data = malloc(sizeof(Film) * films.cap);

  22.         while(fgets(input, TSIZE, stdin)){
  23.                 int length = strlen(input);
  24.                 char* buf;

  25.                 if(length <= 1)
  26.                         break;

  27.                 buf = malloc(length);
  28.                 memcpy(buf, input, length-1);
  29.                 buf[length-1] = '\0';

  30.                 if(films.used == films.cap){
  31.                         films.data = realloc(films.data, 2 * films.cap * sizeof(Film));
  32.                         if(films.data == NULL)
  33.                                 exit(1);
  34.                         films.cap *= 2;
  35.                 }

  36.                 films.data[films.used].title = buf;

  37.                 puts ("Enter your rating(0~10): ");
  38.         scanf ("%d", &(films.data[films.used].rating));
  39.         films.used += 1;

  40.         while (getchar () != '\n')
  41.                 ;
  42.         puts ("Enter next movie title (empty line to stop): ");
  43.         }

  44.         if(films.used == 0)
  45.                 puts("No data entered.");
  46.         else
  47.                 puts("Here is the movie list:");

  48.         for(i = 0; i < films.used; ++i)
  49.                 printf("Movie: %s Rating: %d\n", films.data[i].title, films.data[i].rating);

  50.         for(i = 0; i < films.used; ++i)
  51.                 free(films.data[i].title);
  52.         free(films.data);

  53.         return 0;
  54. }
复制代码


用了realloc,不喜欢realloc的可以自己重新malloc然后拷贝数据
这个版本把ArrayList的几个用于维护的元数据放在了一个结构体里,所以看着比较啰嗦,我自己也不满意
重新写一个再做注释吧,这个仅供参考
12#
发表于 2014-8-30 21:21:06 | 只看该作者
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>

  4. #define TSIZE 48

  5. typedef struct film{
  6.         char* title;
  7.         int rating;
  8. }Film;

  9. int main()
  10. {
  11.         Film* films;
  12.         int cap = 2;
  13.         int pos = 0;
  14.         int i;
  15.         char input[TSIZE];
  16.         /* films指向数组的首地址,cap用于记录数组现在的容积,pos记录下一个写入的数组位置 */

  17.         films = malloc(sizeof(Film) * cap);
  18.         /* 初始化空间 */

  19.         while(fgets(input, TSIZE, stdin)){
  20.                 int length = strlen(input);
  21.                 char* buf;

  22.                 if(length <= 1)
  23.                         break;

  24.                 /* fgets会把回车拷贝到input内,如果长度等于1说明只有一个回车,就是空行 */
  25.                 buf = malloc(length);
  26.                 memcpy(buf, input, length-1);
  27.                 buf[length-1] = '\0';

  28.                 /*这段拷贝是代码中最容易出错的地方
  29.                 例如输入是ab回车,一共读入了3个字符,length等于3
  30.                 存储ab这个字符串,需要3字节,所以分配的长度也是3
  31.                 需要拷贝两个字符过去,最后再补上终止字符*/
  32.                 if(pos == cap){
  33.                         films = realloc(films, 2*cap*sizeof(Film));
  34.                         if(films == NULL)
  35.                                 exit(1);
  36.                         cap *= 2;
  37.                         /*如果数组已经满了,把数组的容积翻倍*/
  38.                         /*若没法成功分配内存,退出程序*/
  39.                 }

  40.                 films[pos].title = buf;
  41.                 puts("Enter your rating(0~10): ");
  42.                 scanf("%d", &(films[pos].rating));
  43.                 pos += 1;
  44.                 /*完整地读入了一个数据,写入位置往前进一格*/

  45.                 while(getchar() != '\n')
  46.                         ;

  47.                 puts("Enter next movie title (empty line to stop): ");
  48.         }

  49.         if(pos == 0)
  50.                 puts("No data entered.");
  51.         else
  52.                 puts("Here is the movie list:");

  53.         for(i = 0; i < pos; ++i)
  54.                 printf("Movie: %s Rating: %d\n", films[i].title, films[i].rating);

  55.         for(i = 0; i < pos; ++i)
  56.                 free(films[i].title);
  57.         free(films);
  58.         /*如果一定要手动释放内存*/

  59.         return 0;
  60. }
复制代码

点评

先上来对老友说声谢谢,这两天没有办法上网,所以今天才来回复。  发表于 2014-9-2 09:43
13#
发表于 2014-8-30 21:47:51 | 只看该作者
另外写了一个cpp的版本
这段代码其实很能体现出cpp对于c的优势
  1. #include <iostream>
  2. #include <vector>
  3. #include <string>

  4. using std::string;
  5. using std::vector;

  6. int main()
  7. {
  8.         vector<int> ratings;
  9.         vector<string> titles;

  10.         string input;

  11.         std::cout << "Enter first movie title: \n";

  12.         while(std::getline(std::cin, input)){
  13.                 if(input.size() == 0)
  14.                         break;

  15.                 titles.push_back(input);

  16.                 std::cout << "Enter your rating(0~10): ";

  17.                 int rating;
  18.                 std::cin >> rating;
  19.                 ratings.push_back(rating);

  20.                 std::getline(std::cin, input);

  21.                 std::cout << "Enter next movie title (empty line to stop): ";
  22.         }

  23.         if(ratings.empty())
  24.                 std::cout << "No data entered.\n";
  25.         else
  26.                 std::cout << "Here is the movie list:\n";

  27.         for(size_t i=0; i!=titles.size(); ++i)
  28.                 std::cout << "Movie: " << titles[i] << " Rating: " << ratings[i]
  29.                 << "\n";

  30.         return 0;
  31. }
复制代码


现成的数据结构(string和vector,这个问题最合适的数据结构就是vector,上面的C代码也是仿写了一个vector,名称用了Java中所谓ArrayList),更好用的IO库,很多人用cpp只是为了这些,而不是一些人挂在嘴边的“面向对象”
14#
 楼主| 发表于 2014-9-2 10:20:16 | 只看该作者
本帖最后由 嗨,强哥! 于 2014-9-2 10:21 编辑
981013 发表于 2014-8-30 17:02
目测多了分号
[ ...

1.目测多了分号
    ---谢谢,十分感谢!
        一个;,就是空语句啊!我是按照我自己的手写稿打字的,连笔误也打上来了。

2.取地址运算符掉了,这是网站的漏洞,我原来的代码就有&,发上来就没有了;还害得我发了2遍。
15#
 楼主| 发表于 2014-9-2 10:29:28 | 只看该作者
rosynirvana 发表于 2014-8-30 17:57
首先回答你注释里面那两个问题吧
1. gets可以返回NULL,在读取错误时;或者没有读取任何字符,直接碰到EOF ...

这两条我都翻书重新看了一遍        意思也基本上猜对了一些
只是while后多出的;    形成了空循环       让我无法相信自己的判断了
多谢rosy老友兼老师兼大侠的热心解答   后面还写了那么多   容我慢慢看
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

广播台
特别关注
快速回复 返回顶部 返回列表