《C编程.开始C》10.程序和功能

在C编程中,所有可执行代码都驻留在一个函数中。函数是一个命名的代码块,它执行任务然后将控制权返回给调用者。注意,其他编程语言可以区分“功能”,“子程序”,“子程序”,“程序”或“方法” – 在C中,这些都是功能。

在程序的单次执行期间,通常从几个不同的地方执行(调用)多次函数。完成子程序后,程序将分支(返回)到呼叫后的点。

函数是一种强大的编程工具。

作为一个基本示例,假设您正在编写一个程序来计算给定(x,y)点到x轴和y轴的距离。您需要计算整数x和y的绝对值。我们可以像这样编写它(假设我们在任何库中都没有预定义的绝对值函数):

#include  <stdio.h>

/ *这是计算整数绝对值的函数。* / 
int  abs (int  x )
{ 
        if  (x > = 0 ) return  x ; 
        别的 回来 - x ; 
}

/ *这是使用两次上面定义的函数的程序。* / 
int  main ()
{ 
        int  x , y ;
        
        printf (“在2平面中键入点的坐标,比如P =(x,y)。首先x =” ); 
        scanf (“%d” , &x ); 
        printf (“Second y =” ); 
        scanf (“%d” , &y );
        
        printf (“P点到x轴的距离是%d。\ n 它到y轴的距离是%d。\ n ” , abs (y ), abs (x ));

        返回 0 ; 
}

以下示例说明了函数作为过程的用法。考虑一下,您正在制作一个程序,告诉学生他们是否在该学科中获得批准。我们可以创建一个函数并根据需要多次调用它,而不是重复相同的代码。

#include <stdio.h>

/ *这里是定义'check'函数的地方。* / 
void  check (int  x )
{ 
        if  (x < 60 )
                printf (“抱歉!你需要再次尝试这个课程。\ n ” ); 
        else 
                printf (“享受你的假期!你被批准了。\ n ” ); 
}

/ *这里程序实际启动,并使用check函数三次。* / 
int  main ()
{ 
        int  a , b , c ;
        
        printf (“在数学中输入你的成绩(整数)。\ n ” ); 
        scanf (“%d” , &a ); 检查(a );
        
        
        printf (“在科学中输入你的成绩(整数)。\ n ” ); 
        scanf (“%d” , &b ); 
        检查(b );
        
        printf (“在编程中输入你的成绩(整数)。\ n ” ); 
        scanf (“%d” , &c ); 
        检查(c );

        / *这个程序应该用更有意义的东西代替。* / 
        return  0 ; 
}

请注意,在上面的程序中,’check’函数没有结果值。它只执行一个程序。

这正是功能的用途。

更多关于功能

功能就像一个黑盒子。它接受输入,用它做某事,然后吐出答案。

请注意,函数可能根本不接受任何输入,或者根本不返回任何内容。在上面的例子中,如果我们要创建该循环的函数,我们可能不需要任何输入,并且我们根本不返回任何内容(文本输出不计算 – 当我们说返回时我们的意思是说有意义程序可以使用的数据)。

我们有一些术语来指代功能:

  • 一个函数,称之为f,使用另一个函数g,据说称为 g。例如,f调用g来打印十个数字的正方形。
  • 函数的输入被称为其参数
  • 函数g ^给出某种答案回˚F据说回到这个问题的答案。例如,g返回其参数的总和。

在C中编写函数

通过实例学习总是好的。让我们编写一个返回数字平方的函数。

int  square (int  x )
{ 
   int  square_of_x ; 
   square_of_x  =  x  *  x ; 
   return  square_of_x ; 
}

要理解如何编写这样的函数,可能有助于查看此函数的整体功能。它接受一个int,x和square,将它存储在变量square_of_x中。现在返回此值。

函数声明开头的第一个int是函数返回的数据类型。在这种情况下,当我们对整数求平方时,我们得到一个整数,并且我们返回这个整数,因此我们将int写为返回类型。

接下来是函数的名称。对于您可能编写的函数,使用有意义的描述性名称是一种好习惯。在写入函数之后命名函数可能会有所帮助。在这种情况下,我们将函数命名为“square”,因为它就是它的作用 – 它正方形一个数字。

接下来是函数的第一个也是唯一一个参数int,它将在函数中称为x。这是函数的输入

在括号之间是函数的实际内容。它声明了一个名为square_of_x的整数变量,用于保存x的平方值。请注意,变量square_of_x 只能在此函数中使用,而不能在外部使用。我们稍后会详细了解这类事情,我们会看到这个属性非常有用。

然后我们将x乘以x或x平方分配给变量square_of_x,这是该函数的全部内容。以下是一份退货声明。我们想要返回x的平方值,所以我们必须说这个函数返回变量square_of_x的内容。

我们的支架关闭,我们已经完成了宣言。

以更简洁的方式编写,此代码执行与上述完全相同的功能:

int  square (int  x )
{ 
   return  x  *  x ; 
}

注意这应该看起来很熟悉 – 你已经在编写函数了,实际上 – main是一个总是写的函数。

一般来说

一般来说,如果我们要声明一个函数,我们写

 类型 名称type1  arg1type2  arg2,...)
 {
   / * 代码 * /
 } 

我们之前曾说过一个函数可以不带参数,或者什么都不返回,或者两者兼而有之。如果我们希望函数不返回什么,我们会写什么?我们使用C的void关键字。void基本上意味着“没有” – 所以如果我们想编写一个不返回任何内容的函数,例如,我们写

void  sayhello (int  number_of_times )
{ 
  int  i ; 
  for (i = 1 ;  i  <=  number_of_times ;  i ++ ) { 
     printf (“Hello!\ n ” ); 
  } 
}

请注意,上面的函数中没有return语句。由于没有,我们将void写为返回类型。(实际上,可以在过程结束之前使用过程中的return关键字返回调用者,但是不能返回值,就像它是函数一样。)

一个不带参数的函数怎么样?如果我们想这样做,我们可以写一些例子

float  calculate_number (void )
{ 
  float  to_return = 1 ; 
  int  i ; 
  for (i = 0 ;  i  <  100 ;  i ++ ) { 
     to_return  + =  1 ; 
     to_return  =  1 / to_return ; 
  } 
  return  to_return ; 
}

请注意,此函数不接受任何输入,而只返回由此函数计算的数字。

当然,您可以将参数中的void return和void结合起来以获得有效的函数。

递归

这是一个执行无限循环的简单函数。它打印一行并调用自身,再次打印一行并再次调用自身,这一直持续到堆栈溢出并且程序崩溃。调用自身的函数称为递归,通常你会有一个条件,它会在一小步有限的步骤之后停止递归。

      //不要跑这个!
void  infinite_recursion ()
{ 
    printf (“Infinite loop!\ n ” ); 
    infinite_recursion (); 
}

可以像这样进行简单的检查。请注意,使用++深度,因此增量将在值传递到函数之前发生。或者,您可以在递归调用之前在单独的行上递增。如果你说print_me(3,0); 该函数将打印行递归3次。

void  print_me (int  j , int  depth )
{ 
   if (depth  <  j ) { 
       printf (“Recursion!depth =%dj =%d \ n ” ,depth ,j );  // j保持其值
       print_me (j , ++ depth ); 
   } 
}

递归最常用于诸如目录树扫描之类的作业,寻找链表的结尾,解析数据库中的树结构以及将数字分解(以及查找素数)等。

静态函数

如果仅从声明它的文件中调用函数,则将其声明为静态函数是合适的。当函数声明为static时,编译器将知道以某种方式编译目标文件,以防止从其他文件中的代码调用该函数。例:

static  int  compare ( int  a , int  b  )
{ 
    return  (a + 4  <  b )? a  : b ; 
}

使用C函数

我们现在可以编写函数了,但是我们如何使用它们?当我们写main时,我们将函数放在包含main的括号之外。

当我们想要使用该函数时,比如说,使用上面的calculate_number函数,我们可以编写类似的函数

 漂浮 f ; 
 f  =  calculate_number ();

如果函数接受参数,我们可以编写类似的东西

 int  square_of_10 ; 
 square_of_10  =  square (10 );

如果函数没有返回任何内容,我们可以说

 say_hello ();

因为我们不需要变量来捕获它的返回值。

来自C标准库的功能

虽然C语言本身不包含函数,但它通常与C标准库链接。要使用此库,您需要在C文件的顶部添加一个#include指令,该指令可能是C89 / C90中的以下之一:

<assert.h><ctype.h><errno.h><float.h><limits.h><locale.h><math.h><setjmp.h><signal.h><stdarg.h><stddef.h><stdio.h><stdlib.h><string.h><time.h>

The functions available are:

<assert.h><limits.h><signal.h><stdlib.h>
assert(int)(constants only)int raise(int sig). Thisvoid* signal(int sig, void (*func)(int))atof(char*), atoi(char*), atol(char*)strtod(char * str, char ** endptr ), strtol(char *str, char **endptr), strtoul(char *str, char **endptr)rand(), srand()malloc(size_t), calloc (size_t elements, size_t elementSize), realloc(void*, int)free (void*)exit(int), abort()atexit(void (*func)())getenvsystemqsort(void *, size_t number, size_t size, int (*sortfunc)(void*, void*))abs, labsdiv, ldiv
<ctype.h><locale.h><stdarg.h><string.h>
isalnum, isalpha, isblankiscntrl, isdigit, isgraphislower, isprint, ispunctisspace, isupper, isxdigittolower, toupperstruct lconv* localeconv(void);char* setlocale(int, const char*);va_start (va_list, ap)va_arg (ap, (type))va_end (ap)va_copy (va_list, va_list)memcpy, memmovememchr, memcmp, memsetstrcat, strncat, strchr, strrchrstrcmp, strncmp, strccollstrcpy, strncpystrerrorstrlenstrspn, strcspnstrpbrkstrstrstrtokstrxfrm
errno.hmath.hstddef.htime.h
(errno)sin, cos, tanasin, acos, atan, atan2sinh, cosh, tanhceilexpfabsfloorfmodfrexpldexplog, log10modfpowsqrtoffsetof macroasctime (struct tm* tmptr)clock_t clock()char* ctime(const time_t* timer)double difftime(time_t timer2, time_t timer1)struct tm* gmtime(const time_t* timer)struct tm* gmtime_r(const time_t* timer, struct tm* result)struct tm* localtime(const time_t* timer)time_t mktime(struct tm* ptm)time_t time(time_t* timer)char * strptime(const char* buf, const char* format, struct tm* tptr)time_t timegm(struct tm *brokentime)
float.hsetjmp.hstdio.h
(constants)int setjmp(jmp_buf env)void longjmp(jmp_buf env, int value)fclosefopen, freopenremoverenamerewindtmpfileclearerrfeof, ferrorfflushfgetpos, fsetposfgetc, fputcfgets, fputsftell, fseekfread, fwritegetc, putcgetchar, putchar, fputchargets, putsprintf, vprintffprintf, vfprintfsprintf, snprintf, vsprintf, vsnprintfperrorscanf, vscanffscanf, vfscanfsscanf, vsscanfsetbuf, setvbuftmpnamungetc

可变长度参数列表

具有可变长度参数列表的函数是可以采用不同数量的参数的函数。C标准库中的一个示例是printf函数,它可以根据程序员想要使用它的方式获取任意数量的参数。

C程序员很少发现需要使用可变长度参数编写新函数。如果他们想要将一堆东西传递给一个函数,他们通常会定义一个结构来保存所有这些东西 – 可能是一个链表或一个数组 – 并用参数中的数据调用该函数。

但是,您可能偶尔会发现需要编写支持可变长度参数列表的新函数。要创建可以接受可变长度参数列表的函数,必须首先包含标准库头stdarg.h。接下来,像往常一样声明函数。接下来,添加省略号(“…”)作为最后一个参数。这向编译器指示要遵循变量参数列表。例如,以下函数声明用于返回数字列表的平均值的函数:

  float  average  (int  n_ar​​gs , ...);

请注意,由于变长参数的工作方式,我们必须在参数中以某种方式指定参数的可变长度部分中的元素数。在这里的平均函数中,它是通过一个名为n_ar​​gs的参数完成的。在printf函数中,它使用您在提供的参数中的第一个字符串中指定的格式代码完成。

既然函数已声明为使用可变长度参数,那么接下来我们必须编写执行函数实际工作的代码。要访问存储在我们的平均函数的可变长度参数列表中的数字,我们必须首先为列表本身声明一个变量:

  va_list  myList ;

该va_list的类型是在声明的类型STDARG.H头,基本上可以让你保持跟踪你的列表中。但是,要开始实际使用myList,我们必须首先为其赋值。毕竟,简单地宣布它本身就不会做任何事情。为此,我们必须调用va_start,它实际上是stdarg.h中定义的宏。在va_start的参数中,您必须提供您计划使用的va_list变量,以及函数声明中省略号之前出现的最后一个变量的名称:

#include  <stdarg.h>
float  average  (int  n_ar​​gs , ...)
{ 
    va_list  myList ; 
    va_start  (myList , n_ar​​gs ); 
    va_end  (myList ); 
}

既然myList已经准备好使用,我们终于可以开始访问存储在其中的变量了。为此,请使用va_arg宏,该宏弹出列表中的下一个参数。在va_arg的参数中,提供您正在使用的va_list变量,以及您正在访问的变量的原始数据类型(例如int,char):

#include  <stdarg.h>
float  average  (int  n_ar​​gs , ...)
{ 
    va_list  myList ; 
    va_start  (myList , n_ar​​gs ); 
    
    int  myNumber  =  va_arg  (myList , int ); 
    va_end  (myList ); 
}

通过从可变长度参数列表中弹出n_ar​​gs整数,我们可以设法找到数字的平均值:

#include  <stdarg.h>
float  average  (int  n_ar​​gs , ...)
{ 
    va_list  myList ; 
    va_start  (myList , n_ar​​gs ); 
    
    int  numbersAdded  =  0 ; 
    int  sum  =  0 ; 
     
    while  (numbersAdded  <  n_ar​​gs ) { 
        int  number  =  va_arg  (myList , int );  //从列表中获取下一个数字
        sum  + =  number ; 
        numbersAdded  + = 1 ; 
    } 
    va_end  (myList ); 
     
    float  avg  =  (float )(sum ) /  (float )(numbersAdded );  //查找平均
    收益 平均; 
}

通过调用平均值(2,10,20),我们得到10和20的平均值,即15。

猜你想读:《C编程.开始C》11.标准库

THE END
分享