user-0 提问: 关于C语言函数的的一些疑惑

回复:
user: user-0
content: 现在回头看，发现条理挺清晰的，可能那时候心比较乱，看得比较艰难，谢谢各位大牛

user: user-0
content: 而且这和我平时的风格有点不一样，我平时是这么写的，当时写这个为了验证循环链表是否正确，因为从链表到循环链表我就用了一条语句，总觉得怪怪的。 ps：回复太长了只能分段发 #include <stdio.h>   #include <stdlib.h>       typedef int ElemType;       /* 定义结点类型 */      typedef struct Node   {       ElemType data;       /* 定义数据域 */     struct Node *next;   /* 定义指针域 */ }Node,*LoopLinkList;            /* 循环单链表的创建,采用尾插法建立单链表 */   Node *LinkListCreatT()   {       Node *head,*r,*p;       head = (Node *)malloc (sizeof(Node));  /* 初始化链表 */ 	if(head == NULL) 	{ 		printf("地址分配失败！\n");	 	}     r = head ;  /* 中间变量 */     ElemType x;   	printf("请输入循环链表的元素(以非数字结束,如e):");     while(scanf("%d",&x) == 1)  /* 输入数字时scanf返回1 */     {           p = (Node *)malloc(sizeof(Node));   		         p->data = x;   		         p->next = r->next;           r->next = p;           r = p;  /*  钩链，新创建的结点总是作为最后一个结点  */     }       r->next = head;  /* 设置为循环链表 */     return head;    }    int main()   {       LoopLinkList list,start;        list = LinkListCreatT();        for(start = list->next ;start != list;start = start->next) 	{ 		if(start->next == list)   /* 判断尾节点的指针域是否为头结点 */ 			printf("循环链表的最后一个元素:%d",start->data);   	}     printf("\n");       return 0;   }

user: user-0
content: 非常感谢各位大牛的回答，这个问题是我在写循环链表的时候提出的，因为我参照了网上的例子，但是发现效率低得吓人，比自己写的还要慢（例子代码有注释），之前都没有遇到这样的问题，而且代码改动的难度也很大，与自己的思路不太一样。我发代码出来想问问可读性怎么样？因为阅读的时候有点费解，修改更难。  /*******************************************************************************/ #include <stdio.h> #include <stdlib.h> /*******************************************************************************/ int InPut();  ////集合元素输入 int Action(); ////选择操作函数 int IN_SET(struct Set* head);  ////测试集合元素子函数 void Check(struct Set* head);  ////测试集合元素主函数 void OutPut(struct Set* head); ////集合元素输出 void DeleteAll(struct Set* head);//释放内存空间 void aa(struct Set* head);     ////输出集合A void bb(struct Set* head);     ////输出集合B void cc(struct Set* head1, struct Set* head2); ////输出交集C void dd(struct Set* head1, struct Set* head2); ////输出并集D void ee(struct Set* head1, struct Set* head2); ////输出对称差E struct Set* JiaoJi(struct Set* head1, struct Set* head2); ////求交集C struct Set* BingJi(struct Set* head1, struct Set* head2); ////求并集D struct Set* DCC(struct Set* head1 , struct Set* head2);   ////求对称差E struct Set* INSERT_SET(struct Set* head);                 ////插入集合元素 struct Set* DeleteNode(struct Set* head, int data);       ////删除节点 struct Set* Sort(struct Set* head, struct Set* newnode);  ////按非递减方式排序 /*******************************************************************************/ struct Set  { 	int data; 	struct Set *next; }; /***主函数**********************************************************************/ int main()  { 	return Action(); } /***选择操作函数****************************************************************/ int Action() {     int choice,c; 	struct Set *a = NULL; 	struct Set *b = NULL; 	while(1) 	{ 		printf("   1 :   向集合A添加新元素\n"); 		printf("   2 :   向集合B添加新元素\n"); 		printf("   3 :   测试集合A的元素\n"); 		printf("   4 :   测试集合B的元素\n"); 		printf("   5 :   输出集合A的元素\n"); 		printf("   6 :   输出集合B的元素\n"); 		printf("   7 :   求A、B集合的交集并输出\n"); 		printf("   8 :   求A、B集合的并集并输出\n"); 		printf("   9 :   求A、B集合的对称差并输出\n"); 		printf("  10 :   退出\n\n\n\n请选择您的操作：");  		if(scanf("%d", &choice) == 1) 		{ 			switch(choice) 			{ 			case 1:   a = INSERT_SET(a); break; 			case 2:   b = INSERT_SET(b); break; 			case 3:   Check(a);          break; 			case 4:   Check(b);          break;      		case 5:   aa(a);             break;  			case 6:   bb(b);             break;  		 	case 7:   cc(a,b);           break; 			case 8:   dd(a,b);           break; 		    case 9:   ee(a,b);           break; 			case 10:  DeleteAll(a); DeleteAll(b); return 0;  /* 释放内存空间 */ 			default:  printf("您输入的数据不正确,请输入自然数(1～10)\n"); 			} 		} 		else               /* 输入异常处理，此处输入的数据为字符串，不能正常读取数据 */ 		{ 			printf("您输入的数据不正确!请输入整形数据!\n"); 		} 	    printf("\n\n\n"); 		system("pause");   /* 停顿 */ 		fflush(stdin);     /* 清除输入缓冲区,防止不合法数据录入,此函数只是C标准的扩充， */ 	    //while ( (c = getchar()) != '\n' && c != EOF ) ; 		system("cls");     /* 调用系统函数实现清屏 */  	}/*end of while*/ } /***函数功能：输入集合元素(集合元素唯一且以非递减方式储存到单链表中)********************/ struct Set *INSERT_SET(struct Set *head) { 	int NewData; 	char enter; 	printf("请输入集合的元素："); 	do                      /* 利用循环实现多个集合元素一起读入 */   	{ 		int i=0; 		NewData = InPut();  /* 调用集合元素输入函数 */ 	    scanf("%c",&enter); /* 利用输入缓冲区的回车键字符来控制循环 */ 		struct Set *p=NULL, *pr=head;   		p = (struct Set *)malloc(sizeof(struct Set));  		if(p == NULL) 		{ 			printf("内存不足！\n"); 			return head; 		} 		p->next = NULL;       /* 置新节点的指针域赋为空 */		 		p->data = NewData; 		while (pr != NULL)    /* 遍历链表,保证元素的唯一性 */ 		{ 			if(pr->data == NewData) 			{ 				printf("%d已经存在集合中,不再重复记录数据！\n",pr->data); 				i=1; 				break; 			} 			pr = pr->next; 		}	 		if(i==0)               /* 表示新输入的元素不在原集合中，数据添加成功        */ 		{ 			head=Sort(head,p); /* head为表头，p为新插入节点，调用非递减方式排序函数 */ 		} 	} while(enter!='\n'); 	return head; } /***函数功能：集合元素输入**************************************************************/ int InPut() { 	int NewData; 	while(1)            /* 此循环保证数据类型的合法性 */ 	{ 		if(scanf("%d", &NewData)==1) 		{ 			return NewData; 		} 	    char w;         /* 防止死循环，影响函数一次性读入多个集合元素的功能      */ 		scanf("%c",&w); /* 若这个数据不合法，去掉这个数据，再循环读入下一个数据  */ 	} } /***函数功能：以非递减方式排序**********************************************************/ struct Set *Sort(struct Set *head, struct Set *newnode) { 		struct Set *pr=head, *temp=NULL; 		if (head == NULL)       /* 链表表头为空 */ 			return newnode;     /* 返回新的表头 */ 		while (pr->data < newnode->data && pr->next != NULL)   		{ 			temp = pr;        			pr = pr->next;    		} 		if (pr->data > newnode->data ) 		{ 			if (pr == head)  /* 在首节点前插入新节点 */ 			{ 				newnode->next = head; 				head = newnode; 			} 			else             /* 在链表中间插入 */ 			{ 				newnode->next = temp->next; 				temp->next = newnode; 			} 		} 		else                /* 在表尾插入新节点 */ 		{ 			pr->next = newnode; 		} 		return head; } /***函数功能：测试集合元素主函数********************************************************/ void Check(struct Set *head) { 	char i;     printf("请输入您要测试的集合元素："); 	do                    /* 循环控制，实现多个集合元素一次性测试 */ 	{ 		int j=IN_SET(head); 		if(j==0)          /* 调用测试集合元素子函数 */ 			printf("已经在集合中!\n");   		else if(j==1) 			printf("不在集合中!\n"); 		scanf("%c",&i);  /* 回车表示输入结束 */ 	}while(i!='\n'); } /***函数功能：测试集合元素子函数(集合元素已经在集合中返回0，否则返回1)******************/ int IN_SET(struct Set *head) { 	int data; 	if(head == NULL) 	{ 		printf("\n对不起,该链表没有有数据!\n"); 		return 3; 	}		 	data = InPut();  /* 调用数据输入函数 */     while(head != NULL)     {  		if(head->data == data) 		{ 			printf("元素%d",data); 			return 0; 		} 		head = head->next;        } 	printf("元素%d",data);      return 1;      /* 测试的数据不在集合中 */ } /***函数功能：输出集合A*****************************************************************/ void aa(struct Set *head) { 	if (head == NULL) 	{ 		printf("集合A为空集!"); 		return; 	} 	else 	   printf("集合A={"); 	OutPut(head); 	printf("}"); } /***函数功能：输出集合B***************************************************************/ void bb(struct Set *head) { 	if (head == NULL) 	{ 		printf("集合B为空集!"); 		return; 	} 	else 	   printf("集合B={"); 	OutPut(head); 	printf("}"); } /***函数功能：输出交集C*****************************************************************/ void cc(struct Set* head1, struct Set* head2) { 	 head1=JiaoJi(head1,head2); /* 调用求交集函数 */ 	 if (head1 == NULL) 	 { 		printf("交集C为空集!"); 		return; 	 } 	 else 	    printf("交集C={"); 	 OutPut(head1);    /* 调用集合元素输出函数 */  	 printf("}"); 	 DeleteAll(head1); /* 释放整个链表的内存空间 */ } /***函数功能：输出并集D***************************************************************/ void dd(struct Set* head1, struct Set* head2) { 	 head1=BingJi(head1, head2); /* 调用求并集函数 */ 	 if (head1 == NULL)  	 { 		 printf("并集D为空集!"); 		 return; 	 } 	 else 	    printf("并集D={"); 	 OutPut(head1);     /* 调用集合元素输出函数   */  	 printf("}"); 	 DeleteAll(head1);  /* 释放整个链表的内存空间 */ } /***函数功能：输出对称差E*************************************************************/ void ee(struct Set* head1, struct Set* head2) { 	head1=DCC(head1, head2); /* 调用求对称差函数 */ 	if (head1 == NULL) 	{ 		printf("对称差E为空集!"); 		return; 	} 	else 	   printf("对称差E={"); 	OutPut(head1);    /* 调用集合元素输出函数    */ 	printf("}"); 	DeleteAll(head1); /* 释放整个链表的内存空间  */ } /***函数功能：输出集合元素(集合元素以非递增的方式输出)********************************/ void OutPut(struct Set *head) { 	if (head->next != NULL) 	{ 		OutPut(head->next);       /* 递归算法 */ 	} 	if (head->next == NULL)        /* 控制输出的第一个元素后面不能再跟逗号"," */ 		printf("%d",head->data); 	else 	    printf(",%d",head->data); /* 每输出一个元素前面加一个逗号","	      */ } /***函数功能：求集合A和集合B的交集C，并以非递减方式存储********************************/ struct Set *JiaoJi(struct Set *head1, struct Set *head2) { 	 struct Set *pr=head1,*p=head2,*c,*head,*temp; 	 head=c=(struct Set*)malloc(sizeof(struct Set)); 	 head->next = NULL; 	 while(pr!=NULL)     /* 以非递减方式遍历整个集合A,找出交集C并存储到链表C */ 	 { 		  p=head2;       /* 每次都要从表头开始,q为集合B表头                  */ 		  while(p!=NULL) /* 内循环遍历集合B                                  */ 		  { 			   if(pr->data==p->data) 			   { 				temp = (struct Set *)malloc(sizeof(struct Set)); 				temp->data = pr->data;  				temp->next = NULL; 				c->next = temp; 				c = temp;   				break; 			   } 			   p=p->next; 		  } 	      pr=pr->next; 	 } 	 head = head->next;     return head; } /***函数功能：求集合A和集合B的并集D******************************************************/ struct Set *BingJi(struct Set *head1, struct Set *head2)  { 	 struct Set *pr=head1,*p,*q=head2,*head,*d,*temp; /* head表示D链表表头 */ 	 head=d=(struct Set*)malloc(sizeof(struct Set)); 	 head->next = NULL; 	 while(q!=NULL) /* 以集合B来初始化并集D */   	 { 		 temp=(struct Set*)malloc(sizeof(struct Set)); 		 temp->data=q->data; 		 temp->next=NULL; 		 d->next=temp; 		 d=temp; 		 q=q->next; 	 } 	 head = head->next;   	 while(pr!=NULL)    /* 遍历整个集合A,使集合A中每个元素都与集合B中所有元素比较一下 */ 	 { 	     int i=0;       /* 标记作用                                                   */ 		 p=head2;       /* 每次都要从表头开始遍历链表，p集合B的表头                   */ 		 while(p!=NULL) /* 内循环遍历集合B,找出两个集合公共部分元素                   */   		 {  			 if(pr->data==p->data)   			 { 			     i=1; 				 break; 			 } 			 p=p->next; 		 } 		 if(i==0) 		 { 			 temp=(struct Set*)malloc(sizeof(struct Set)); 			 temp->data=pr->data; 			 temp->next=NULL; 			 head=Sort(head,temp); /*以集合B为基础，找出集合A中独有的元素，调用排序函数*/ 		 } 		 pr=pr->next; 	 } 	 return head; } /***函数功能：求集合A和集合B的对称差E***************************************************/ struct Set* DCC(struct Set * head1 , struct Set * head2) { 	struct Set *a=NULL, *b=NULL;  	a=JiaoJi(head1, head2); /* 求交集 */ 	b=BingJi(head1, head2); /* 求并集 */ 	while(a != NULL)  	{			 		b=DeleteNode(b,a->data); /* 调用删除节点函数 */ 		a=a->next; 	}     return b; } /***函数功能：删除节点函数**************************************************************/ struct Set* DeleteNode(struct Set* head, int data) { 	struct Set *p=head, *pr=head; 	while(data != p->data) 	{ 		pr = p; 		p = p->next; 	} 	if(p==head) 		head = p->next; 	else 		pr->next = p->next;			 	free(p); /* 释放已删除节点分配的内存 */ 	return head; } /***函数功能：释放内存空间**************************************************************/ void DeleteAll(struct Set* head) {  	struct Set* pr=NULL; 	while(head!=NULL) 	{   		pr=head->next; 		free(head);  /* 释放已申请的内存空间 */ 		head=pr; 	} }

user: user-1
content: 函数原层级调用非常多. 一般重复的代码尽量写成函数.

user: user-2
content: 引用 4 楼 hdt 的回复:[quote=引用 3 楼 cfjtaishan 的回复:] 这个不好说，根据实际情况来定吧；如果A，B两个功能函数是平级的，或者类似的。比如学生管理系统，查询和创建应该是平级的，都放到main函数里调用比较好；如果A，B两个功能是实现同一个功能，那么肯定是调用关系比较好； 函数定义和调用，还要考虑代码可读性，符合逻辑的使用，至于那种方式好，除了考虑上面的情况，还要考虑你的框架设计，接口设计，函数细分的程度。因此，还得具体问题具体分析！ 请举个例子，那个比较大的软件，是如你所说。所有函数彼此正交。互不调用。 [/quote] 函数的主要用途有两个，缺一不可。一个十分割功能。一个就是复用。 前一个目的降低软件的复杂度。 第二个增加代码编写效率。 还是上边那一句话，缺一不可。 而且你所说的增删改，应该不是函数。而是功能。或者叫模块。

user: user-3
content: 请牢记：源代码本身的书写是否结构化或面向对象或符合设计模式或敏捷…并不重要，重要的是你是否使用结构化或面向对象或符合设计模式或敏捷…的方法命名标识符、阅读、修改、检查、测试源代码。  意思是你程序结构看上去再合理，再简洁，也不一定比看上去一团乱麻的程序结构在运行或修改时更不易出错，更方便修改，出错了更容易找到哪里出错和具体出错的原因，更容易改正错误。  试对比 图书馆（对图书的分类够结构化了吧） 和 搜索引擎（可看作是扁平化任何结构数据，仅支持全文检索） 哪个处理信息更方便、更高效。  所以 与其费劲去重构代码让其看上去更简洁、更合理 不如费劲学习grep、sed、awk、……这类全文搜索和批处理编辑的工具。  结构越复杂，越难修改，越难除错。 有时（甚至大多数时候），看上去越合理、越简洁的代码，运行起来性能越差，出错时查找原因越难，找到出错原因后改正越费劲。  程序员要做的不是尽力避免错误，而是聚焦在快速发现并改正错误。真正以快速方式轻易解决错误，“快速的失败”远胜过“预防错误”。Fred George  前微软C#编辑器的开发主管Jay Bazuzi列出的一些有助于找到正确方向的问题；他觉得前同事们应该用这些问题来问自己；实际上不管在哪里工作的开发者们都应该经常问问自己这些问题： ◆“要保证这个问题不会再出现，我该怎么做？” ◆“要想少出些Bug，我该怎么做？” ◆“要保证Bug容易被修复，我该怎么做？” ◆“要保持对变化的快速响应，我该怎么做？” ◆“要保证我的软件的运行速度，我该怎么做？” 如果大多数团队都能不时问一下自己，必定会从中得益，因为这些都是真正强而有力的问题。

user: user-4
content: 引用 4 楼 hdt 的回复:[quote=引用 3 楼 cfjtaishan 的回复:] 这个不好说，根据实际情况来定吧；如果A，B两个功能函数是平级的，或者类似的。比如学生管理系统，查询和创建应该是平级的，都放到main函数里调用比较好；如果A，B两个功能是实现同一个功能，那么肯定是调用关系比较好； 函数定义和调用，还要考虑代码可读性，符合逻辑的使用，至于那种方式好，除了考虑上面的情况，还要考虑你的框架设计，接口设计，函数细分的程度。因此，还得具体问题具体分析！ 请举个例子，那个比较大的软件，是如你所说。所有函数彼此正交。互不调用。 [/quote] 我们平时写的学生管理系统，如果用链表做，查询和创建学生表都是对链表的操作，但是这两个接口函数还要做交集，这样逻辑上是不是就不很好理解（做交集调用是可以，没问题，因为函数调用没有限制彼此不能调用）；这两个功能同时被main调用或者再定义一个函数，但是对于这两个函数之间最好不要有调用吧。

user: user-2
content: 引用 3 楼 cfjtaishan 的回复:这个不好说，根据实际情况来定吧；如果A，B两个功能函数是平级的，或者类似的。比如学生管理系统，查询和创建应该是平级的，都放到main函数里调用比较好；如果A，B两个功能是实现同一个功能，那么肯定是调用关系比较好； 函数定义和调用，还要考虑代码可读性，符合逻辑的使用，至于那种方式好，除了考虑上面的情况，还要考虑你的框架设计，接口设计，函数细分的程度。因此，还得具体问题具体分析！ 请举个例子，那个比较大的软件，是如你所说。所有函数彼此正交。互不调用。

user: user-4
content: 这个不好说，根据实际情况来定吧；如果A，B两个功能函数是平级的，或者类似的。比如学生管理系统，查询和创建应该是平级的，都放到main函数里调用比较好；如果A，B两个功能是实现同一个功能，那么肯定是调用关系比较好； 函数定义和调用，还要考虑代码可读性，符合逻辑的使用，至于那种方式好，除了考虑上面的情况，还要考虑你的框架设计，接口设计，函数细分的程度。因此，还得具体问题具体分析！

user: user-2
content: 函数的功能就是把一些共有的功能抽象出来，达到一个复用的目的。节省时间。清晰功能。比如排序。 很多地方都会需要。所以把它抽象出来，避免重复。 所以你的第二个想法是错误的。 另外，你的第一个看法。问题在于，你不会看函数。一个函数如果他编写的没有问题，为什么要进入他呢。 我只要考虑他的输入，输出就可以了。只有当输出不符合我的要求的时候，也就是说它内部有问题的时候，我才要跟踪进去。

user: user-2
content: 都独立出来，相当于所有的函数是平级的，各个功能彼此是独立的。这做的到吗？