关于C语言函数的的一些疑惑

mukes 2017-06-22 02:13:53
在C语言中:
1.在定义函数时可以在函数体中调用另一个函数,因此阅读代码是看到此处要跳到另一个函数看,而调用函数时也需要调用另一个函数。
2.可以把函数都独立出来,然后再mian函数里再调用。
这两种哪种比较好呢?(从可读性、性能等方面分析)
谢谢。
...全文
219 11 打赏 收藏 转发到动态 举报
写回复
用AI写文章
11 条回复
切换为时间正序
请发表友善的回复…
发表回复
mukes 2017-06-22
  • 打赏
  • 举报
回复
现在回头看,发现条理挺清晰的,可能那时候心比较乱,看得比较艰难,谢谢各位大牛
mukes 2017-06-22
  • 打赏
  • 举报
回复
而且这和我平时的风格有点不一样,我平时是这么写的,当时写这个为了验证循环链表是否正确,因为从链表到循环链表我就用了一条语句,总觉得怪怪的。 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;  
}    
mukes 2017-06-22
  • 打赏
  • 举报
回复
非常感谢各位大牛的回答,这个问题是我在写循环链表的时候提出的,因为我参照了网上的例子,但是发现效率低得吓人,比自己写的还要慢(例子代码有注释),之前都没有遇到这样的问题,而且代码改动的难度也很大,与自己的思路不太一样。我发代码出来想问问可读性怎么样?因为阅读的时候有点费解,修改更难。

/*******************************************************************************/
#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;
	}
}

xiaohuh421 2017-06-22
  • 打赏
  • 举报
回复
函数原层级调用非常多. 一般重复的代码尽量写成函数.
真相重于对错 2017-06-22
  • 打赏
  • 举报
回复
引用 4 楼 hdt 的回复:
[quote=引用 3 楼 cfjtaishan 的回复:] 这个不好说,根据实际情况来定吧;如果A,B两个功能函数是平级的,或者类似的。比如学生管理系统,查询和创建应该是平级的,都放到main函数里调用比较好;如果A,B两个功能是实现同一个功能,那么肯定是调用关系比较好; 函数定义和调用,还要考虑代码可读性,符合逻辑的使用,至于那种方式好,除了考虑上面的情况,还要考虑你的框架设计,接口设计,函数细分的程度。因此,还得具体问题具体分析!
请举个例子,那个比较大的软件,是如你所说。所有函数彼此正交。互不调用。 [/quote] 函数的主要用途有两个,缺一不可。一个十分割功能。一个就是复用。 前一个目的降低软件的复杂度。 第二个增加代码编写效率。 还是上边那一句话,缺一不可。 而且你所说的增删改,应该不是函数。而是功能。或者叫模块。
赵4老师 2017-06-22
  • 打赏
  • 举报
回复
请牢记:源代码本身的书写是否结构化或面向对象或符合设计模式或敏捷…并不重要,重要的是你是否使用结构化或面向对象或符合设计模式或敏捷…的方法命名标识符、阅读、修改、检查、测试源代码。 意思是你程序结构看上去再合理,再简洁,也不一定比看上去一团乱麻的程序结构在运行或修改时更不易出错,更方便修改,出错了更容易找到哪里出错和具体出错的原因,更容易改正错误。 试对比 图书馆(对图书的分类够结构化了吧) 和 搜索引擎(可看作是扁平化任何结构数据,仅支持全文检索) 哪个处理信息更方便、更高效。 所以 与其费劲去重构代码让其看上去更简洁、更合理 不如费劲学习grep、sed、awk、……这类全文搜索和批处理编辑的工具。 结构越复杂,越难修改,越难除错。 有时(甚至大多数时候),看上去越合理、越简洁的代码,运行起来性能越差,出错时查找原因越难,找到出错原因后改正越费劲。 程序员要做的不是尽力避免错误,而是聚焦在快速发现并改正错误。真正以快速方式轻易解决错误,“快速的失败”远胜过“预防错误”。Fred George 前微软C#编辑器的开发主管Jay Bazuzi列出的一些有助于找到正确方向的问题;他觉得前同事们应该用这些问题来问自己;实际上不管在哪里工作的开发者们都应该经常问问自己这些问题: ◆“要保证这个问题不会再出现,我该怎么做?” ◆“要想少出些Bug,我该怎么做?” ◆“要保证Bug容易被修复,我该怎么做?” ◆“要保持对变化的快速响应,我该怎么做?” ◆“要保证我的软件的运行速度,我该怎么做?” 如果大多数团队都能不时问一下自己,必定会从中得益,因为这些都是真正强而有力的问题。
自信男孩 2017-06-22
  • 打赏
  • 举报
回复
引用 4 楼 hdt 的回复:
[quote=引用 3 楼 cfjtaishan 的回复:] 这个不好说,根据实际情况来定吧;如果A,B两个功能函数是平级的,或者类似的。比如学生管理系统,查询和创建应该是平级的,都放到main函数里调用比较好;如果A,B两个功能是实现同一个功能,那么肯定是调用关系比较好; 函数定义和调用,还要考虑代码可读性,符合逻辑的使用,至于那种方式好,除了考虑上面的情况,还要考虑你的框架设计,接口设计,函数细分的程度。因此,还得具体问题具体分析!
请举个例子,那个比较大的软件,是如你所说。所有函数彼此正交。互不调用。 [/quote] 我们平时写的学生管理系统,如果用链表做,查询和创建学生表都是对链表的操作,但是这两个接口函数还要做交集,这样逻辑上是不是就不很好理解(做交集调用是可以,没问题,因为函数调用没有限制彼此不能调用);这两个功能同时被main调用或者再定义一个函数,但是对于这两个函数之间最好不要有调用吧。
真相重于对错 2017-06-22
  • 打赏
  • 举报
回复
引用 3 楼 cfjtaishan 的回复:
这个不好说,根据实际情况来定吧;如果A,B两个功能函数是平级的,或者类似的。比如学生管理系统,查询和创建应该是平级的,都放到main函数里调用比较好;如果A,B两个功能是实现同一个功能,那么肯定是调用关系比较好; 函数定义和调用,还要考虑代码可读性,符合逻辑的使用,至于那种方式好,除了考虑上面的情况,还要考虑你的框架设计,接口设计,函数细分的程度。因此,还得具体问题具体分析!
请举个例子,那个比较大的软件,是如你所说。所有函数彼此正交。互不调用。
自信男孩 2017-06-22
  • 打赏
  • 举报
回复
这个不好说,根据实际情况来定吧;如果A,B两个功能函数是平级的,或者类似的。比如学生管理系统,查询和创建应该是平级的,都放到main函数里调用比较好;如果A,B两个功能是实现同一个功能,那么肯定是调用关系比较好; 函数定义和调用,还要考虑代码可读性,符合逻辑的使用,至于那种方式好,除了考虑上面的情况,还要考虑你的框架设计,接口设计,函数细分的程度。因此,还得具体问题具体分析!
真相重于对错 2017-06-22
  • 打赏
  • 举报
回复
函数的功能就是把一些共有的功能抽象出来,达到一个复用的目的。节省时间。清晰功能。比如排序。 很多地方都会需要。所以把它抽象出来,避免重复。 所以你的第二个想法是错误的。 另外,你的第一个看法。问题在于,你不会看函数。一个函数如果他编写的没有问题,为什么要进入他呢。 我只要考虑他的输入,输出就可以了。只有当输出不符合我的要求的时候,也就是说它内部有问题的时候,我才要跟踪进去。
真相重于对错 2017-06-22
  • 打赏
  • 举报
回复
都独立出来,相当于所有的函数是平级的,各个功能彼此是独立的。这做的到吗?

69,371

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧