【计算机408学习笔记】C语言(初级) 07函数

07函数
一、上节作业(重点)
1. 输入一个整型数,存入变量i,通过子函数change把主函数的变量i除2,然后打印i,例如如果输入的为10,打印出5,如果输入的为7,打印出3
#include 


void change(int *j)
{
    *j=*j/2;
}


int main() {
    int i;
    scanf("%d",&i);
    change(&i);
    printf("%d",i);
    return 0;
}

2. 输入一个整型数,然后申请对应大小空间内存,然后读取一个字符串(测试用例的字符串中含有空格),字符串的输入长度小于最初输入的整型数大小,最后输出输入的字符串即可(无需考虑输入的字符串过长,超过了内存大小);

#include 
#include 


int main() {
    //读取申请空间的大小
    int i;
    scanf("%d",&i);


    //读取字符串并清除标准输入缓冲区中的换行
    char c;
    scanf("%c",&c);


    //申请i个字节大小的空间,强制类型转换char*
    char *p;
    p=(char*)malloc(i);
    fgets(p,i,stdin);//等价于gets(p);gets被去掉是因为不安全,会访问越界


    puts(p);
    return 0;
}

二、函数的声明与定义

1.函数的声明与定义
  • 函数的调用:由主函数调用其他函数,其他函数可以相互调用。同一个函数可以被一个或多个函数调用任意次。
  • 函数的声明:指把 函数的名字、函数类型及形参的类型、个数和顺序通知编译系统,以便在调用该函数时编译系统能正确识别函数并检查调用是否合法。如果不在头文件中对使用的函数进行声明,那么在编译时会出现警告。
  • 函数的定义:指对函数功能的确立,包括指定 函数名、函数值类型、形参及其类型、函数体等,是一个完整的、独立的函数单位。
  • 隐式声明:C语言中有几种声明的类型名可以省略,如函数如果不显示地声明返回值的类型,那么它默认返回整型;使用旧风格声明函数的形式参数时,如果省略参数的类型,那么编译器默认其为整型。隐式声明不是好习惯,显示声明能够清楚地表达意图。
  • 例子
#include 

void print_message();//增加该声明即可解决

int main() {
    print_message();
    return 0;
}


void print_message()//函数定义
{
    printf("Hello, World!\n");
}
  • 结果
warning: conflicting types for 'print_message'//未声明print_message,则返回类型未知
Hello, World!//能正常出结果,但是在消息中有警告
  • CLion新建C源文件或头文件方法
【计算机408学习笔记】C语言(初级) 07函数_第1张图片
  • #include :引用的是标准库中的头文件
  • #include "func.h":引用的是当前目录下自定义的头文件
  • 例子
func.h   //存放公共头文件+声明函数
 
#ifndef INC_1_FUNCTION_FUNC_H
#define INC_1_FUNCTION_FUNC_H


#include 
void print_message();//声明函数
int print_star(int i);//声明函数


#endif //INC_1_FUNCTION_FUNC_H

func.c     //定义函数

#include "func.h"


void print_message()
{
    printf("Hello, World!\n");
    print_star(3);
}


int print_star(int i)
{
    printf("**********************\n");
    printf("printstar %d\n",i);
    return i+3;
}

main.c

#include "func.h"


int main() {
    int a=10;
    a=print_star(a);
    print_message();
    print_star(a);
    return 0;
}
  • 结果
**********************
printstar 10
Hello, World!
**********************
printstar 3
**********************
printstar 13
  • CLion快捷点:ctrl+鼠标左键,可以跳转到对应函数查看源码
2.C语言编译和执行的特点
  • 分别编写、分别编译,进而提高调试效率:一个C程序由一个或多个程序模块组成,每个程序模块作为一个源程序文件,对于较大的程序,通常将长须内容分别放在若干源文件中,再由若干源文件组成一个C程序。
  • 一个源程序文件是一个编译单位:在程序编译时是以源程序文件为单位而不是以函数为单位进行编译。一个源程序文件由一个或多个函数及其他有关内容组成(如命令行、数据定义等)。
  • C程序的执行是从main函数开始的:如果main函数中调用其他函数,那么在调用后会返回到main函数中,在main函数中结束整个程序的运行。
  • 所有函数都是平行的:在定义函数时是分别进行的,并且是相互独立的,一个函数并不从属于另一个函数,即函数不能嵌套定义。函数间可以相互调用(嵌套调用),但不能调用main函数,main函数是由系统调用的。
3.函数的分类与调用
  • 用户角度分类:标准函数与自定义函数。
  • 标准函数:即库函数,由系统提供,用户不必自己定义,可以直接使用。如printf、scanf等,不同的C系统提供的库函数的数量和功能会有一些不同。
  • 自定义函数:用户自己定义的函数,用以解决用户的专门需要。
  • 函数形式分类:无参函数与有参函数。
  • 无参函数:一般用来执行指定的一组操作,在调用无参函数时,主调函数不向被调用函数传递数据。
类型标识符  函数名()
{
    声明部分
    语句部分
}
  • 有参函数:主调函数在调用函数时,通过参数向被调用函数传递数据。
类型标识符  函数名(形式参数表列)
{
    声明部分
    语句部分
}
三、函数的递归调用
  • 把函数自身调用自身的操作,成为递归函数。递归函数一定要有结束条件,否则会产生死循环。
  • 递归的核心是找公式。
  • 例子(求阶乘)
#include 


int f(int i)
{
    if(i==1)
    {
        return 1;
    }
    return i*f(i-1);
}


int main() {
    int i,res;
    scanf("%d",&i);
    res=f(i);
    printf("f(%d)=%d\n",i,res);
    return 0;
}
  • 结果
4
f(4)=24
  • 使用递归在解决一些问题时,可以让问题变得简单,降低变成的难度。
  • 例子(上台阶,一次只能上1个台阶或2个台阶,上n个台阶有几种方法)
#include 


int step(int n)
{
    if(n==1 || n==2)
    {
        return n;
    }
    return step(n-1)+step(n-2);
}


int main() {
    int n,res;
    scanf("%d",&n);
    res=step(n);
    printf("step(%d)=%d\n",n,res);
    return 0;
}
  • 结果
4
step(4)=5
四、局部变量与全局变量
1.全局变量解析
  • 在不同函数之间传递数据时,可以使用的方法有:
(1)参数:通过形式参数和实际参数。
(2)返回值:用return语句返回计算结果。
(3)全局变量:外部变量。
  • 例子
#include 


int i=10;//全局变量
void print(int a)
{
    printf("print i=%d\n",i);
}


int main() {
    printf("main i=%d\n",i);


    i=5;//修改了全局变量
    print(i);
    
    return 0;
}
  • 结果
main i=10
print i=5
  • 例子
#include 


int i=10;
void print(int a)
{
    printf("print i=%d\n",i);
}


int main() {
    printf("main i=%d\n",i);


    int i=5;//局部变量和全局变量重名
    print(i);


    return 0;
}
  • 结果
main i=10
print i=10
  • 原因:全局变量i存储在 数据段,main函数和print函数都是可见的。全局变量不会因为某个函数执行结束而消失,在整个进程的执行过程中始终有效。在函数内定义的变量都称为局部变量,局部变量存储在自己的函数对应的 栈空间内,函数执行结束后,函数内的局部变量所分配的空间将会得到释放。如果局部变量与全局变量重名,那么将采取就近原则,即实际获取和修改的值是局部变量的值。
【计算机408学习笔记】C语言(初级) 07函数_第2张图片
  • 例子
#include 


int i=10;
void print(int i)
{
    printf("print i=%d\n",i);
}


int main() {
    printf("main i=%d\n",i);


    int i=5;
    print(i);


    return 0;
}
  • 结果
main i=10
print i=5
  • 原因:形参类似于局部变量,也遵循就近原则。定义函数中指定的形参,如果没有函数调用,那么它并不占用内存中的存储单元,只有在发生函数调用时,函数printt中的形参才被分配内存单元。在调用结束后,形参所占的内存单元也会被释放。
  • 实参可以是常量、变量或表达式,但要求它有确定的值。
  • 在被定义的函数中,必须指定形参的类型。如果实参列表中包含多个实参,那么各参数间用逗号隔开,实参与形参的个数应相等,类型应匹配,且实参与形参应按顺序对应,一一传递数据。
  • 实参与形参的类型应相同或赋值应兼容。
  • 实参向形参的数据传递是单向值传递,只能由实参传递给形参,而不能由形参传回给实参,在调用函数时,给形参分配存储单元,并将实参对应的值传递给形参,调用结束后,形参单元被释放,实参单元仍保留并维持原值。
  • 形参相当于局部变量,因此不能再定义局部变量与形参同名,否则会造成编译不不通。
2.局部变量与全局变量
  • 在一个函数内部定义的变量称为内部变量,只在本函数范围内有效,即只有在本函数内才能使用这些变量,也称局部变量。
  • 主函数中定义的变量只在主函数中有效,而不因为在主函数中定义而在整个文件或程序中有效,主函数也不能使用其他函数中定义的变量。
  • 不同函数中可以使用相同名字的变量,代表不同的对象,互不干扰。
  • 形式参数也是局部变量。
  • 在一个函数内部,可以在复合语句中定义变量,这些变量只在本负责语句中有效,这种复合语句也称“分程序”或“程序块”。
  • 例子
#include 


int i=10;
void print(int i)
{
    printf("print i=%d\n",i);
}


int main() {
    {
        int j=10;
    }
    printf("main i=%d\n",i);
    printf("main j=%d\n",j);


    int i=5;
    print(i);


    return 0;
}
  • 结果
error: 'j' undeclared (first use in this function)
  • 原因:局部变量在其最近的花括号内有效,若离开花括号,则在其下面使用该变量会造成编译不通。
  • for循环的小括号内定义的int i,在离开for循环后,是不可以再次使用的。
  • 例子
#include 


int i=10;
void print(int i)
{
    printf("print i=%d\n",i);
}


int main() {

    printf("main i=%d\n",i);
    for(int k=0;k<-1;)
    {


    }
    printf("main k=%d\n",k);
    int i=5;
    print(i);


    return 0;
}
  • 结果
error: 'k' undeclared (first use in this function)
  • 函数之外定义的变量称为外部变量,外部变量可以为本文件中的其他函数共用,它的 有效范围是从定义变量的位置开始到本源文件结束,所以也称为全程变量。
  • 全局变量在程序的全部执行过程中都占存储单元,而不是仅在需要时才开辟单元。
  • 使用全局变量过多会降低程序的清晰性,在各个函数执行时都可能改变外部变量的值,程序容易出错,因此要有限制地使用全局变量。
  • 因为函数在执行时依赖于其所在的外部变量,如果将一个函数移到另一个文件中,那么还要将有关的外部变量及其值一起移过去。然而,如果外部变量与其他文件的变量同名,那么就会出现问题,会降低程序的可靠性和通用性。
  • C语言一般要求把程序中的函数做成一个封闭体,除可以通过“实参→形参”的渠道与外界发生联系外,没有其他渠道。

你可能感兴趣的:(学习,笔记,c语言)