2017-05-11 40 views
0

我有结构旅行,我做这个旅行的链接列表,当我添加旅行到列表我没有问题,但是当添加第二次旅行它覆盖第一个。 我做的输入被添加两趟ID为1和2,当我重复他们,我看到有两次ID 2功能改变其他变量的数据

#include <stdio.h> 
#include <stdlib.h> 

char date[10]; 
char date2[10]; 
char newdate[10]; 
char newdate2[10]; 
int i,t,i2,t2; 
int ret; 

int option; 



typedef struct trip 
{ 
    int code; 
    char startdate[10]; 
    int duration; 
    double price; 
} Trip; 

typedef struct list 
{ 
    Trip* Trip; 
    struct list* next; 
} List; 


List* create_List(Trip* trip) 
{ 
    List* newList = malloc(sizeof(List)); 
    if (NULL != newList) 
    { 
     newList->Trip = trip; 
     newList->next = NULL; 
    } 
    return newList; 
} 

void delete_List(List* oldList) 
{ 
    if (NULL != oldList->next) 
    { 
     delete_List(oldList->next); 
    } 
    free(oldList); 
} 

List* add_List(List* wordList, Trip* trip) 
{ 
    List* newList = create_List(trip); 
    if (NULL != newList) 
    { 
     newList->next = wordList; 
    } 
    return newList; 
} 

void createTripData(Trip *trip); 

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

    List* trips; 
    int first = 1; 

    while(1) 
    { 
     printf("Select an option: \n 1-add new trip \n 2-Iterate\n"); 
     scanf("%d", &option); 

     if (option == 1) 
     { 
      Trip newTrip; 

      createTripData(&newTrip); 

      if(first == 1) 
      { 
       first = -1; 
       trips = create_List(&newTrip); 
      } 
      else 
      { 
       trips = add_List(trips, &newTrip); 
      } 
     } 

     else if (option == 2) 
     { 
      system("@cls||clear"); 
      List* iter; 
      for (iter = trips; NULL != iter; iter = iter->next) 
      { 
       printf("Id %d \n", iter->Trip->code); 
      } 
     } 


    } 
    return (0); 
} 

void createTripData(Trip *trip) 
{ 
      fflush(stdin); 
      printf("Enter id: "); 
      scanf("%d", &trip->code);// 
} 
+0

'fflush(stdin)'在除少数几个实现之外的所有行为都是未定义行为,并且是不可移植的。无论何时“函数改变其他变量的数据”,这通常意味着你正在写入超出内存块的范围,高兴地砸碎相邻变量的内存。仔细检查你使用的指针和你提供的解除引用。除非绝对需要,否则应避免使用*全局变量*这里不需要任何变量。 –

+2

Trip newTrip的范围在这个if块内。而且,你反复使用它。 – BLUEPIXY

+0

newTrip只是中间人,然后将该行程添加到列表*行程 –

回答

1

与您的代码的问题是newTrip变量。你可以而不是把它添加到列表中。它是一个本地变量的一部分主要。因此,当你退出main的那部分时,它会超出范围(即变为无效)。我会建议你重写createTripData喜欢:

Trip *trip createTripData() 
{ 
    Trip* t = malloc(sizeof(*t)); 
    if (!t) 
    { 
     // Ups... add error handling 
     exit(1); 
    } 

    printf("Enter id: "); 
    if (scanf("%d", &t->code) != 1) 
    { 
     // Ups... add error handling 
     exit(1); 
    } 

    return t; 
} 

,并调用它像:

if (option == 1) 
    { 
     Trip* newTrip = createTripData(); 

     if(first == 1) 
     { 
      first = -1; 
      trips = create_List(newTrip); 
     } 
     else 
     { 
      trips = add_List(trips, newTrip); 
     } 
    } 

除此之外,您可以通过处理空表盒内add_List简化代码。

List* trips = NULL;  // Notice this initialization 

while(1) 
{ 
    printf("Select an option: \n 1-add new trip \n 2-Iterate\n"); 
    scanf("%d", &option); 

    if (option == 1) 
    { 
     Trip* newTrip = createTripData(); 
     trips = add_List(trips, &newTrip); 
    } 
+0

非常感谢你,早些时候我看到甚至当我scanf即使我不使用add_List –

+0

@HristoVutov - 那是因为你使用了局部变量,新的id改变了列表中的那个。这个答案每次分配一个新的“Trip”,因此它不会发生。 – 4386427

+0

我知道这是复制,但最好是显示'List * trips = NULL;'特别是在帮助新的C程序员时,因为在'int * a,b,c;'('b'和'c'当然不是指针)。将''*''附加到'type'使其本身变得混乱,'int * a,b,c;'更不容易被误解。 –

0

功能:createTripData()调用scanf()但没有检查返回值(不是参数值),所以如果用户输入只是一个回车键或输入任何东西,但数字来那么代码接受没有任何输入,这是一个错误

功能:main()有两个参数,但都没有被使用。功能main()有两个有效签名:

int main(int argc, char *argv[]) 
int main(void) 

,因为不使用参数,则代码应该使用签名:

int main(void) 

功能:main()从用户通过获取菜单选择:

scanf("%d", &option); 

但是不检查返回值(不是参数值),所以当用户只输入'return'或任何字符o而不是一个数字,则使用字段option中的先前值。如果用户输入了除1或2之外的任何数字,则代码将通过while()循环“尖叫”,不执行任何操作或以前在变量option中的值。

I.E.总是检查从任何scanf()函数系列调用返回的值并处理错误。

在函数中:main(),假定用户输入1或2.这不是一个有效的假设。 (绝对不要相信用户做正确的事情)强烈建议将if(option == 1else if(option == 2)替换为switch() statement that includes a默认情况下用于输入除1或2之外的任何数字I.E.

switch(option) 
{ 
    case 1: 
     ... 
     break; 

    case 2: 
     ... 
     break; 

    default: 
     printf("INVALID option: %d, valid options are 1, 2\n", option); 
     break; 
} 

这个领域,在struct list

Trip* Trip; 

定义它是编程习惯很差命名变量一样的变量的类型。一个可能的解决办法:

Trip *myTrip; 

然后更新代码中的其他参考使用myTrip

它的编程习惯很差命名变量一样的变量的类型,唯一的区别是大写。这种做法导致混乱的人阅读的代码。

在功能:create_list()当调用malloc()失败会发生什么? NULL指针被返回到功能:add_List()它返回NULL指针功能:main(),其中(成功与否)覆盖在变量trips值。

声明:

fflush(stdin); 

同时允许在Visual Studio中,是不可移植。建议使用类似:关于调用scanf()

int ch; 
while((ch = getchar()) != EOF && '\n' != ch); 

,这里是处理错误事件的一种方法:

if(1 != scanf("%d", &trip->code)) 
{ // then error occurred as 'scanf' returns number of successful input/conversions 
    perror("scanf for code failed"); 
    exit(EXIT_FAILURE); 
} 

// implied else, scanf successful 

上述建议的方法立即退出程序(离开OS清理所有分配的内存,这将是编程习惯差)你可能想,而不是代码的循环,告知该问题的用户,清空标准输入,并允许用户再次尝试。

这样的说法:

printf("Select an option: \n 1-add new trip \n 2-Iterate\n"); 

将变得非常“困难”的阅读和理解,如果有菜单几种选择。建议利用将所有连续串在一起,写这样的说法的C-特点:

printf("Select an option: \n" 
     " 1-add new trip \n" 
     " 2-Iterate\n"); 

注:建议的格式也关注到打印页面的右侧边缘,使得一个更加美好布局。

功能:delete_List()不会被调用,所以在main()“菜单”缺少一个选项。

顺便说一句:函数:delete_List()使用递归,如果链表非常短,则可以。但是,如果链表中有很多条目(特别是在窗口中),溢出堆栈的可能性很高。我建议使用一个循环来遍历链表,通过每次进入free()保存->next指针,然后进行不要紧多少项在链表

以方便阅读和理解的: 1.单独的代码块(for,if,else,while,do ...通过一条空行 2.通过2或3个空白行分开函数(保持一致) 3.遵循公理:每行只有一条语句并且(最多)一个变量声明每行声明。

现在,你为什么看到你在问题中提到的问题:

传递到add_List()第二指针是在main()堆栈上未初始化的局部指针,而不是实际的指针链表这是宣布:List* trips;

建议使用独特的名称,以便您可以跟踪什么是指向什么。

此外,在函数中:create_List(),新节点被设置为指向main()中的未初始化指针,而不是指向main中trips中的所需条目。

请注意,在函数中:main(),在现代C编译器中,如果返回值始终为0,则不需要return语句。

注意:该语句return不是一个函数,因此没有必要(除非将多个表达式包装为单个结果值)才能在由return传回的值附近产生parens。

为了简化,建议消除可变first的,改变List* trips;List *trips = NULL;和更换:if(first == 1)与:if(!trips)

I.E.总是尝试编写仍然执行功能的最简单的代码。