万变不离其宗:C语言函数的灵魂思想

封面

学会思想就够了!
理解获得意想不到的结果——新技术的诞生!

函数定义公式:


image.png

函数可以作为定义返回值,也可作为函数内部的应用函数。
注意:每个C程序的入口和出口都位于main函数之中。编写程序时,并不是将所有的内容都放在主函数main中。

函数调用的一般形式为:
functionName(param1, param2, param3 ...);

functionName 是函数名称,param1, param2, param3 ...是实参列表。实参可以是常数、变量、表达式等,多个实参用逗号,分隔。

先来看一个例子:

1、函数的声明

注意:这里的函数为新定义的函数,以下是声明函数之后,主函数调用其他函数。

#include<stdio.h>

int Function();         /*声明函数*/

int main()
{
    printf("我在函数之前\n"); /*输出提示信息*/
    Function();     /*调用函数*/
    printf("函数的结尾\n");  /*输出提示信息*/
    return 0;
}

int Function()      /*定义函数*/
{
    printf("我是函数\n");       /*输出提示信息*/
    /*函数结束*/
}

执行结果:

image.png

当然我们可以把定义的函数和主函数程序块交换一个位置,结果仍然相同,因此主函数总是去调用定义的新函数内容。这主要看个人的书写习惯吧!我比较喜欢把定义的函数放在前面,主函数放在后面。即:

#include<stdio.h>

int Function();         /*声明函数*/


int Function()      /*定义函数*/
{
    printf("我是函数\n");       /*输出提示信息*/
    /*函数结束*/
}

int main()
{
    printf("我在函数之前\n"); /*输出提示信息*/
    Function();     /*调用函数*/
    printf("函数的结尾\n");  /*输出提示信息*/
    return 0;
}

执行结果:


image.png

类似地,我们这里举出一个简单例子:

#include<stdio.h>
/*自定义一个函数*/
int fun2020(int x,int y)
{
    if((x*x+y*y)%2==0)
    {
        return 0;
    }
    else
    {
        return 1;
    }
 } 
 
 /*定义一个主函数去调用上诉函数*/
 int main()
 {
    int num1,num2,i,j;
    printf("请输入两位数:\n");
    scanf("%d%d",&num1,&num2);
    /*声明调用的函数*/ 
    i=fun2020(num1,num2);
    j=num1*num1+num2*num2;
    if(i==0)
    {
        printf("%d和%d的平方和%d是偶数\n",num1,num2,j); 
    }
    else
    {
        printf("%d和%d的平方和%d是奇数\n",num1,num2,j);
    }
     
  } 
 

执行结果:

请输入两位数:
66
88
66和88的平方和12100是偶数

2、函数的参数调用

递归:

#include<stdio.h>
/*自定义一个函数*/
int fun2020(int n)
{
    int m;
    if(n==0||n==1)
    {
        return m=1;
    }
    else
    {
        m=n*fun2020(n-1);
    }
    return m;
 } 
 
 /*定义一个主函数去调用上诉函数*/
 int main()
 {
    int i,j;
    printf("请输入一个数:\n");
    scanf("%d",&i);
    if(i<0)
    {
        printf("输入错误 \n"); 
    }
    else
    {
        j=fun2020(i);
        printf("%d的阶乘为%d",i,j);
    }
     
  } 
 

受到以上方法的启发,这里来判断三角形

/*三角形*/
#include<stdio.h>
#include<stdlib.h>
/*自定义一个函数*/
int feimagougu(int a, int b,int c)
{ 
    if(((a+b)>c&&abs(a-b)<c))
    {
        return 0;
    }
    else
    {
        return 1;
    }
 } 
 
 /*定义一个主函数去调用上诉函数*/
 int main()
 {
    int x,y,z,i;
    printf("请输入三个数:\n");
    scanf("%d%d%d",&x,&y,&z);
    /*一定别忘了要声明函数,不然不能调用*/
    i=feimagougu(x,y,z);
    if(i==0)
    {
        printf("是三角形 \n"); 
        printf("这三边长分别为%d,%d和%d",x,y,z);
    }
    else
    {
        printf("不是三角形 \n"); 
    }
    
  } 
 

执行结果:

image.png

同样的方法,可以获得如下:

3、输入任意三个数判断是否为勾股数

/*任意输入三个数,判断是否为勾股数*/
#include<stdio.h>
/*自定义一个函数*/
int feimagougu(int a, int b, int c)
{ 
    if((a*a+b*b)==(c*c))
    {
        return 0;
    }
    else
    {
        return 1;
    }
 } 
 
 /*定义一个主函数去调用上诉函数*/
 int main()
 {
    int x,y,z,i;
    printf("请输入三个数:\n");
    scanf("%d%d%d",&x,&y,&z);
    /*一定别忘了要声明函数,不然不能调用*/
    i=feimagougu(x,y,z);
    if(i==0)
    {
        printf("这三个数为勾股数 \n"); 
        printf("这三个数分别为%d,%d和%d",x,y,z);
    }
    else
    {
        printf("这三个数为非勾股数 \n"); 
    }
    
  } 
 

执行结果:

image.png

4、调用其他函数

其实从上面的函数之间的调用分析,我们已经知道函数调用的初步过程了,为了更细说明其中的思想,那么如何在函数之间互相调用取值?这里再举例说明:


image.png
/*在主函数中调用其他函数*/
#include<stdio.h>
void Move();    /*声明搬运函数*/
void Build();   /*声明建造函数*/
void Paint();   /*声明粉刷函数*/

//使用主函数main调用其他函数 
int main()
{
    Move(); /*执行搬运函数*/
    Build();        /*执行建造函数*/
    Paint();    /*执行粉刷函数*/
    return 0;   /*程序结束*/
}

//构造各个函数的功能和方法 
/*执行搬运功能  */
void Move()
{
    printf("这个函数可以搬运货物\n");
}
/*执行建造功能  */
void Build()
{
    printf("这个函数可以建造一栋高楼\n");
}
/*  执行粉刷功能  */
void Paint()
{
    printf("这个函数可以粉刷墙面\n");
}

执行结果:

image.png

备注:

返回值类型为void的方法.
定义格式:
修饰符  void 方法名(参数列表){
    方法体;
    return; // 可以省略
}

void: 空的意思, 不能用来定义变量,只能用来作为返回值类型使用

注意点:
1. 不能定义变量来接收
2. 不能直接放在输出语句中


5、返回值的调用

以下是一输入任意两数求差的例子:

#include<stdio.h>
//先声明定义的函数 
int Fun(int iNumber1,int iNumber2);

//定义一个函数Fun
int Fun(int iNumber1,int iNumber2)
{
    int iResult;    /*定义一个整型变量用来存储返回的结果*/
    //以上定义的iResult也可定义多个 
    iResult=iNumber1-iNumber2;  /*进行减法计算,得到计算结果*/
    return iResult; 
    /*return语句返回计算结果,这里要注意的是,返回值给了函数,
    一般只能返回一个值,因此其他函数调用它时调用的return返回的结果*/
}

//定义一个主函数main调用以上函数 
int main()
{
    
    int x,y,Result; /*定义3个整型变量*/
    printf("请任意输入两个数:\n");
    scanf("%d%d",&x,&y);
    Result=Fun(x,y);/*进行x-y的减法计算,并将结果赋值给变量iResult*/
    printf("Fun(%d,%d)=%d\n",x,y,Result); 
    return 0;   /*程序结束*/
}
//切记,定义函数中的iResult和主函数中Result不能重名,否则报错或不能运行 
//scanf()函数里的参数一定别忘了取地址符:& 
 

执行结果:

image.png

6、使用数组元素作为函数参数

由于实参可以是表达式形式,数组元素可以是表达式的组成部分,因此数组元素可以作为函数的实参,与用变量作为函数实参一样,是单向传递。

#include<stdio.h>
void ShowMember(int iMember);   /*声明函数*/


void ShowMember(int iMember)    /*函数定义*/
{
    printf("这名员工的编号为00%d\n",iMember);/*输出数据*/
}


int main()
{
    int iCount[10]; /*定义一个整型的一维数组*/
    int i;      /*定义整型变量,用于循环*/
    for(i=0;i<10;i++)   /*进行赋值循环*/
    {
        iCount[i]=i;    /*为数组中的元素进行赋值操作*/
    }
    for(i=0;i<10;i++)       /*循环操作*/
    {
        ShowMember(iCount[i]);/*执行输出函数操作*/
    }
    return 0;
}


执行结果:

image.png

类似地,我们再举一个有难度的例子,计算一个函数列二维数组,生成10*10矩阵值:

\begin{array}{l} a(i,j) = \cos i + \sin j + {e^{i - j}} + {( - 1)^{i + j}}, {\rm{ where }} i,j{\rm{ = 0}},{\rm{1}}, \cdots ,{\rm{9}}{\rm{.}} \end{array}

#include<stdio.h>
#include <math.h>
#include <complex.h>
void ShowMember(int k,int l,int iMember);   /*声明函数*/


void ShowMember(int k,int l,int iMember)    /*函数定义*/
{
    
    printf("a[%d][%d]=%4d  ",k,l,iMember);/*输出数据*/
}


int main()
{
    int iCount[10][10]; /*定义一个整型的数组*/
    int i,j;        /*定义整型变量,用于循环*/
    for(i=0;i<10;i++)   /*进行赋值循环*/
    {
        for(j=0;j<10;j++)
        {
            iCount[i][j]=cos(2*i)+sin(2*j)+exp(i-j)+pow(-1,i+j);    /*为数组中的元素进行赋值操作*/
        }   
    }
    
    for(i=0;i<10;i++)       /*循环操作*/
    {
        for(j=0;j<10;j++)
        {
            ShowMember(i,j,iCount[i][j]);/*执行输出函数操作*/
        }
    printf("\n");//生成10*10矩阵形式 
    }
    return 0;
}


执行结果:

image.png

备注:还可以使用指针作为函数参数,也就是将函数参数声明为一个指针,例如:

#include<stdio.h>
void  Evaluate(int* pPoint);/*声明函数,参数为可变长度数组*/
void  Display(int* pPoint);/*声明函数,参数为可变长度数组*/
int main()
{
      int iArray[10];   /*定义一个具有10个元素的整型数组*/
    ......
}

void  Display(int* pPoint)/*定义函数,参数为可变长度数组*/
{
    ......
}

void  Evaluate(int* pPoint) /*定义函数,参数为可变长度数组*/
{
    ......
}


7、主函数参数main()

运行程序时,有时需要将必要的参数传递给主函数。
主函数main的形式参数如下:
main (int argc, char * argv[] )
两个特殊的内部形参argc和argv是用来接收命令行实参的,这是只有主函数main才能具有的参数。

  • argc参数: argc参数保存命令行的参数个数,是整型变量。这个参数的值至少是1,因为至少程序名就是第一个实参。
  • argv参数: argv参数是一个指向字符指针数组的指针,这个数组里的每一个元素都指向命令行实参。所有命令行实参都是字符串,任何数字都必须由程序转变成为适当的格式。

argc、argv的具体含义

argc和argv参数在用命令行编译程序时有用:
main( int argc, char* argv[], char **env )

第一个参数,int型的argc,为整型,用来统计程序运行时发送给main函数的命令行参数的个数,在VS中默认值为1。
第二个参数,char*型的argv[],为字符串数组,用来存放指向的字符串参数的指针数组,每一个元素指向一个参数。各成员含义如下:

  • argv[0]指向程序运行的全路径名
  • argv[1]指向在DOS命令行中执行程序名后的第一个字符串
  • argv[2]指向执行程序名后的第二个字符串
  • argv[3]指向执行程序名后的第三个字符串
  • argv[argc]为NULL

第三个参数,char**型的env,为字符串数组。env[]的每一个元素都包含ENVVAR=value形式的字符串,其中ENVVAR为环境变量,value为其对应的值。平时使用到的比较少。

下面给出例子说明:


image.png

8、指针作为函数参数

指针变量作参数后就很容易解决上面的问题:

#include <stdio.h>
void swap(int *p1, int *p2){
    int temp;  //临时变量
    temp = *p1;
    *p1 = *p2;
    *p2 = temp;
}
int main(){
    int a = 2019, b = 2020;
    swap(&a, &b);
    printf("a = %d, b = %d\n", a, b);
    return 0;
}

运行结果:

a = 2020, b = 2019

调用 swap() 函数时,将变量 a、b 的地址分别赋值给 p1、p2,这样 p1、p2 代表的就是变量 a、b 本身,交换 p1、p2 的值也就是交换 a、b 的值。函数运行结束后虽然会将 p1、p2 销毁,但它对外部 a、b 造成的影响是“持久化”的,不会随着函数的结束而“恢复原样”。

需要注意的是临时变量 temp,它的作用特别重要,因为执行*p1 = *p2;语句后 a 的值会被 b 的值覆盖,如果不先将 a 的值保存起来以后就找不到了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,723评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,080评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,604评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,440评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,431评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,499评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,893评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,541评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,751评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,547评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,619评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,320评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,890评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,896评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,137评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,796评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,335评论 2 342