C 函数更改其他变量的数据
我有一个结构旅行,我为这次旅行制作了一个链表,当我将旅行添加到列表中时,我没有问题,但是当添加第二次旅行时,它会覆盖第一次旅行。 我所做的输入是添加两个id为1和2的行程,当我迭代它们时,我看到有两个id为2的行程C 函数更改其他变量的数据,c,linked-list,structure,C,Linked List,Structure,我有一个结构旅行,我为这次旅行制作了一个链表,当我将旅行添加到列表中时,我没有问题,但是当添加第二次旅行时,它会覆盖第一次旅行。 我所做的输入是添加两个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
#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);//
}
代码中的一个问题是newTrip变量。您不能以尝试的方式将其添加到列表中。它是一个局部变量,位于主变量的一部分。因此,它超出了范围,即当您退出main的该部分时,它变得无效。我建议您重写createTripData,如下所示: 把它叫做:
if (option == 1)
{
Trip* newTrip = createTripData();
if(first == 1)
{
first = -1;
trips = create_List(newTrip);
}
else
{
trips = add_List(trips, newTrip);
}
}
此外,您还可以通过处理add_list中的空列表来简化代码
函数:createTripData调用scanf,但无法检查返回值而不是参数值,因此如果用户只输入返回键或输入除数字以外的任何内容,则代码接受nothing输入,这是一个错误 函数:main有两个参数,但都没有使用。函数main有两个有效签名:
int main( int argc, char *argv[] )
int main( void )
由于未使用参数,代码应使用签名:
int main( void )
功能:main通过以下方式从用户处获取菜单选择:
scanf( "%d", &option );
但不检查返回值而不是参数值,因此当用户仅输入“返回”或数字以外的任何字符时,将使用字段选项中的先前值。如果用户输入除1或2以外的任何数字,则代码会在while循环中“尖叫”,不执行任何操作或按照变量选项中以前的值执行操作
也就是说,始终检查调用任何scanf系列函数的返回值并处理错误
在function:main中,假设用户输入了1或2。这不是一个有效的假设。永远不要相信用户会做正确的事情强烈建议用switch语句替换if option==1和if option==2,其中包含用户输入除1或2以外的任何数字时的adefault`case,即
switch( option )
{
case 1:
...
break;
case 2:
...
break;
default:
printf( "INVALID option: %d, valid options are 1, 2\n", option );
break;
}
此字段位于结构列表的定义中
将变量命名为与变量类型相同的变量是非常糟糕的编程实践。一个可能的解决方案:
Trip *myTrip;
然后更新代码中的其他引用以使用myTrip
将变量命名为与变量类型相同的变量是非常糟糕的编程实践,唯一的区别是大小写。这种做法导致人们在阅读代码时感到困惑
函数中:创建\u列表调用malloc失败时会发生什么?一个空指针返回到function:add_List,它将该空指针返回到function:main,该函数是否成功覆盖变量trips中的值
声明:
fflush(stdin);
虽然允许在visual studio中使用,但不可移植。建议使用以下方法:
int ch;
while( (ch = getchar()) != EOF && '\n' != ch );
关于对scanf的调用,以下是处理错误事件的一种方法:
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
上述建议的方法会立即退出程序,留下操作系统来清理所有分配的内存,这可能是一种糟糕的编程实践,而不是编写一个循环来通知用户问题,清空stdin并允许用户重试
本声明:
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中的“menu”缺少一个选项
顺便说一句:函数:delete_List使用递归,如果链表很短,则可以使用递归。但是,如果链表中有很多条目,那么特别是在windows上,堆栈溢出的可能性很高。我建议使用循环遍历链接列表,在保存->下一个指针后将每个条目传递给free,这样无论链接列表中有多少条目
为便于阅读和理解:
1.单独的代码块用于,if,else,while,do。。。while、switch、case、default通过单个空行
2.由2或3个空行分隔的功能应一致
3.遵循公理:每行只有一条语句,每条语句最多有一个变量声明
现在,你为什么看到你在问题中提到的问题:
传递给add_List的第二个指针指向main中堆栈上未初始化的本地指针,而不是指向链接列表的实际指针,该链接列表由:List*trips
建议使用唯一的名称,以便跟踪指向的内容
另外,在function:create_List中,新节点被设置为指向main中未初始化的指针,而不是指向main中trips中所需的条目
注意,在现代C编译器中,在function:main中
,如果返回值始终为0,则不需要return语句
注意:语句return不是函数,因此除非将多个表达式包装到单个结果值中,否则不需要在return传回的值周围使用paren
为了简化,建议先删除变量,更改列表*行程;列出*trips=NULL;并将:iffirst==1替换为:if!旅行
也就是说,始终尝试编写仍能执行功能的最简单代码。fflushstdin在除极少数实现外的所有实现中都是未定义的行为,并且不可移植。每当函数更改其他变量的数据时,这通常意味着您的写入超出了内存块的范围,很高兴地破坏了相邻变量的内存。仔细检查指针的使用情况和所做的取消引用。除非绝对需要,否则避免使用全局变量-此处不需要全局变量;在这个if块内。此外,您可以反复使用它。newTrip只是一个中间人,然后将行程添加到List*Trips中,但它的范围仅限于if块内。一旦退出if`块,存储在trips中的内存地址将被释放,以供系统重用,当然,系统在每次迭代中都会重复使用。因此,您可能最终将输入的所有值存储在同一地址,并在trips中分配该地址,这使得您试图访问超出if语句范围的内存地址,并立即进入未定义行为。此外,您根本不需要选项。只需初始化列表*trips=NULL;注意:“*”与变量一起使用,而不是与类型一起使用。然后,您只需要检查trips==NULL{..create 1st node..}否则{..add node to end..}非常感谢,之前我看到,即使在扫描新id时,也会更改列表中的id,即使我不使用add_List@HristoVutov-那是因为你使用了局部变量。这个答案每次分配一个新的行程,因此它不会发生。我知道这是复制,但最好显示List*trips=NULL;特别是在帮助新的C程序员时,因为在int*a,b,C;b和c当然不是指针。在类型上附加“*”会导致混淆,int*a、b、c;更不容易被误解。@DavidC.Rankin-我只是从OPs问题中抄来的。我没有注意到这件事。我认为你们不会达成共识@DavidC.Rankin IMO List*trips=NULL;很好,你应该避免写int*a,b,c;不管星星的位置如何
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
printf("Select an option: \n 1-add new trip \n 2-Iterate\n");
printf("Select an option: \n"
" 1-add new trip \n"
" 2-Iterate\n");