C语言学习笔记---函数

C语言学习笔记

函数


子函数在主函数之后,须在主函数开头部分声明子函数。

定义

无参函数
类型标识符 函数名() {
   
    函数体
}
类型标识符 函数名(void) {
   
    函数体
}
- void表示空类型,无函数值(返回值)
- 函数体包括'声明部分''语句部分'
有参函数
类型标识符 函数名(形式参数表列) {
   
    函数体
}

类型标识符 函数名(类型标识符 形式参数, 类型标识符 形式参数, .....) {
   
    函数体
}
例如:
int max(int x,int y) {
   
    int z;                      // 声明部分
    z = x>y ? x : y;            // 执行部分
    return z;
}
空函数
类型标识符 函数名() {
   

}

C语言中,若未说明函数的类型,则系统默认该函数的类型是int型。

调用

格式
格式: 函数名(实参表列)
- 函数调用语句,针对无返回值的函数
    printf("hello");
- 函数表达式,针对有返回值的函数
    c = 2 * max(1,2);
- 函数参数,作为另一个函数的实参
    m = max(1,max(2,3));
    printf("max:%d",max(5,11)); 
形参与实参

在有参函数中,定义函数时函数名后面括号里的是形参,在主函数中调用时,被调用函数名后面括号里的是实参。实参可以是常量,变量,表达式。实参要求必须是确定的值。

数组作为函数参数

数组元素作为函数实参,其用法与变量相同,向形参传递数组元素的

数组名也可以作为实参和形参,传递的是数组第一个元素的地址

数组元素作为函数实参

可做实参,不可做形参,因为形参是在函数被调用处临时分配存储单元的,不可能为一个数组元素单独分配存储空间。是 ”值传递“ 方式,数据传输方向是从实参传递到形参,单向传递。

一维数组名作为函数参数

用数组元素作为实参时,向形参彻底的是数组元素的值。数组名作为函数实参时,向形参(数组名或指针变量)传递的是数组首元素的地址。

当用数组名作为函数的参数时,如果形参数组中的元素值发生改变时 实参元素的值也发生改变。

  • 用数组名作为函数的参数时,应在主调用函数和被调用函数分别定义数组

  • 实参数组与形参数组应类型一致

  • 在定义函数时,指定数组大小时不起任何作用,因为C语言编译器不检查形参数组大小,只是将实参数组的首元素的地址传递给形参数组名

  • 形参数组可以不知道大小

    void foo(int a[]){
         ......}
    

编译系统会把形参数组处理为指针变量,例如把float a[]转化为float *a,二者是等价的

多维数组名作为函数参数

可省略第一维大小

void foo(int a[][10]){
   ......}
嵌套调用

C语言的函数定义是互相平行的、独立。在定义函数时,一个函数内不能在定义另一个函数,即不能嵌套定义

可以嵌套调用函数

递归调用

直接或间接地调用该函数本身,称为函数的递归调用。

int foo(int x) {
   
    int y,z;
    z=foo(y);
    return 2*z;
}

声明

来源

函数的来源有两种:1. 用户自定义的函数,2. 库函数。

  • 被调用的函数必须是已定义的函数。

  • 使用库函数,需要在本文件开头用#include指令将所需要的信息添加到本文件中。

    #include <math.h>
    .h是头文件所用的后缀,表示头文件
    
  • 如果用户自定义的函数在主函数后面定义,需要在主函数开头声明该函数。以便编译器能够正确识别到。因为编译器是自上向下进行的。

  • 如果已在文件的开头(在所有函数之前),对本文件中所调用的函数进行声明,则在个函数中不必对其所调用的函数再做声明。

    int max(int,int);
    int add(int,int);
    int main() {
         ......}        // 在main函数中就无需对max和add函数进行声明
    int max(int x,int y){
         ......}
    int add(int x,int y){
         ......}
    

格式

一般形式:
    - 函数类型 函数名(参数类型1 参数名1, 参数类型2 参数名2, ...)
    - 函数类型 函数名(参数类型1, 参数类型2, ...)
例如:
float add(float,float);
float add(float x,float y);

返回值

  • 函数的返回值通过函数中的return语句获得

  • 在定义函数时必须指定函数的类型

  • 在定义函数指定的函数类型一般应该和return语句中的表达式一致,即函数类型觉得返回值类型

  • 对于不带回值的函数,应当定义函数为 “void类型”

变量的作用域

在函数开头定义的变量,只在该函数中使用。引申到变量的作用域问题,每个变量都有一个作用域。

在函数内定义的变量是局部变量;在函数外定义的变量是全局变量。

定义变量的三种情况:

  • 在函数开头的位置定义

  • 在函数内的复合语句内定义

  • 在函数外部定义

局部变量
  • 主函数定义的变量只在主函数中有效

  • 不同函数可以使用相同名的变量,他们代表不同的对象,互不干扰

  • 形式参数也是局部变量

  • 复合语句中定义的变量只在复合语句中有效

全局变量

为了便于区分全局变量和局部变量,有一个习惯将全局变量第一个字母大写

建议不在必要时不要使用全局变量,理由如下:

  • 全局变量在程序的全部执行中占用内存

  • 使函数的通用性降低

  • 使用全局变量过多,会降低程序的清晰性

举例
// 变量的作用域
#include<stdio.h>

int G = 123;

int add(int x, int y) {
   
    printf("add中打印g:%d\n", G);
    return x + y;
}
char M = 'M';            // 编译器自上向下执行语句,虽然M和G都是全局作用域,但m的作用域比g小,不包含add函数


int main() {
   
    int a = 0;
    int sum=0;
    for (int i = 0; i < 10 ;i++) {
   
        int c=10;
        sum = sum + add(a, i);
    }
    G = 321;
    //G = G + i + c;            // 未定义标识符 "i" 和 "c"
    //printf("G:%d; a:%d; sum:%d; c:%d; i:%d\n",G,a,sum,c,i);    // c和i无效
    printf("g:%d; a:%d; sum:%d;\n", G, a, sum);
}

若全局变量与局部变量同盟,分析结果

#define _CRT_SECURE_NO_WARNINGS 1
#pragma warning(disable:4996) // 禁止显示strcpy等不安全函数的警告
#pragma warning(disable:6031) // 禁止显示未经检查的返回值警告

#include<stdio.h>

int a = 3, b = 5;

int main() {
   
    int max(int a, int b);
    int a = 8;
    printf("main里的a:%d; b:%d\n", a, b);    // a:8(局部变量) b:5(全局变量)
    printf("max=%d\n", max(a, b));
    return 0;
}

int max(int a, int b) {
   
    int c;
    printf("max里的a:%d; b:%d\n", a, b);        // a,b为局部变量
    c = a > b ? a : b;
    return c;
}

// max=8

变量的存储方式和生存期

存储方法

变量的存储方式有两种:

  1. 静态存储方式

  2. 动态存储方式

静态存储方式:指程序运行期间由系统分配固定的存储空间的方式。

动态存储方式:指在程序运行期间根据需要进行的动态分配存储空间的方式。

常量的存储方式取决于常量的类型和存储类别。一般来说,C语言中的常量存储方式有以下特点:

  1. 字面常量(Literal Constants):字符串常量和数值常量(包括整型、浮点型和字符型)通常存储在程序的静态存储区(Static Memory),也被称为常量存储区(Constant Pool)。这个区域在程序运行期间始终存在,不会随函数调用结束而消失。字符串常量尤为特殊,它们在内存中以只读形式存储,不可修改。

  2. const修饰的变量(Const Variables):当使用const关键字定义一个常量变量时,虽然它在概念上被视为常量,但在内存中仍可能分配存储空间,且存储在静态存储区。一旦初始化,其值就不能再改变。

  3. 编译时常量(Compile-Time Constants):对于整型和指针类型的编译时常量(如用#define预处理器定义的宏或者枚举常量),它们在编译阶段就被替换成了具体的值,不会在运行时占用内存空间。

简而言之,在C语言中,大多数常量在内存中的存储是静态的,并且在程序的整个生命周期内保持不变。字符串常量具有额外的只读属性,防止它们在运行时被修改。而用const修饰的变量虽然名义上是常量,但在内存中仍有其专属的存储空间。

内存中供用户使用的存储空间,可分为三部分:

  1. 程序区

  2. 静态存储区

  3. 动态存储器

数据分别存放在静态存储区和动态存储区中,全局变量全部存放在静态存储区中,在整个程序执行期间一直存在,程序开始时分配存储空间,程序结束时释放。

一般静态存储区存放全局变量,常量等。

在动态存储区中存放下列数据:

  1. 函数形式参数。在调用函数时给形参分配存储空间。

  2. 函数中定义没有用关键字static声明的变量,即自动变量。

  3. 函数调用时的现场包含和返回地址等。

存储类别

每一个变量和函数有两个属性:数据类型数据的存储类别

C语言的存储类别包括4种:autostaticregisterextern;前三种用于局部变量,extern用于全局变量

局部变量的存储类别

  1. auto—自动局部变量
    使用关键字auto作为自动变量的存储列表声明。
    函数中的局部变量,如果没有专门用static(静态)声明存储类别,都是动态分配存储空间的。
    关键字auto可省略,不写auto则隐含指定为 ”自动存储类别“

    int foo(int a){
         
        auto int b=2;
        return a+b;
    }
    
  2. static—静态局部变量
    使用关键字static声明。
    函数调用结束后不会消失,而继续保留原值,即其占用的存储空间不释放,在下一次调用该函数时,该变量的值是上一次函数调用结束的值。
    非必要情况下不要使用静态局部变量,会多占用内存空间。
    建议定义静态局部变量时,同时初始化值,以免每次调用时重新赋值。

    // 考虑静态变量的
    #include<stdio.h>
    
    int main() {
         
        int foo(int);
        int a = 2, i;
        for ( i = 0; i < 3; i++<
这是一个使用Windows API创建线程的C语言程序,实现了多线程计算1到指定上界的整数和,并输出结果。 代码注释如下: ```c #include<stdio.h> // 引入标准输入输出库 #include<windows.h> // 引入Windows API库 DWORD Sum; // 定义一个全局变量Sum,用于保存计算结果 // 线程函数,计算1到指定上界的整数和 DWORD WINAPI Summation(LPVOID Param) { DWORD Upper = *(DWORD*)Param; // 读取传入的上界参数 for (DWORD i = 0; i<= Upper; i++) // 计算1到上界的整数和 Sum += i; // 将每个整数累加到结果中 return 0; // 返回0表示线程执行完毕 } // 主函数,程序入口 int main(int argc, char *argv[]) { DWORD ThreadId; // 定义线程ID HANDLE ThreadHandle; // 定义线程句柄 int Param; // 定义传入的上界参数 Param = atoi(argv[1]); // 读取传入的参数并转换为整数类型 if (Param <0) // 判断参数是否小于0 { printf("An interger parameter is required.\n"); // 输出错误提示信息 return -1; // 返回错误码-1 } ThreadHandle = CreateThread( // 创建线程 NULL, // 默认安全属性 0, // 默认栈大小 Summation, // 调用Summation函数 &Param, // 传入上界参数 0, // 默认线程启动标志 &ThreadId // 保存线程ID ); if(ThreadHandle != NULL) // 如果线程创建成功 { printf("ThreadId=%d\n", ThreadId); // 输出线程ID WaitForSingleObject(ThreadHandle, INFINITE); // 等待线程结束 CloseHandle(ThreadHandle); // 关闭线程句柄 printf("sum=%d\n", Sum); // 输出计算结果 } return 0; // 返回0表示程序正常结束 } ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

childish_tree

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值