函数

应用程序的运行

yinyongcxu的yunxing

用户双击 App 这一操作会被 os 所感知到;os 会根据文件名,将对应的 App 加载到内存中;加载到内存之后便会寻找去 main() 函数并将其调用;当 main() 函数运行完成之后 return 会向操作系统返回一个值;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include<stdio.h>
#include<stdlib.h>

int main() {
printf("Hello\n");
system("pause");
printf("Word\n");
system("pause");
return 122;
}

// Hello
// Press any key to continue . . .
// Word
// Press any key to continue . . .

在 C 语言中,system 函数是用来执行外部命令的,如 Linux 下的 ls 等等

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include<stdio.h>
#include<stdlib.h>

int main() {
printf("Hello\n");
printf("Word\n");
return 122;
}

/*
C:\Users\kay\Desktop\workspace>a.exe
Hello
Word

C:\Users\kay\Desktop\workspace>echo %ERRORLEVEL%
122

*/

powershell 下可以通过 %ERRORLEVEL% 环境变量来获取上一个执行命令的退出状态。这是我们看上当前返回的数值确实是 return 中返回的数值,这则可证明:在 C 语言程序中,main 函数是程序的入口点,当程序执行完毕时,它会通过 return 语句返回一个值给操作系统。这个返回值通常用于表示程序的执行状态或结果。

函数定义与函数调用

  • 函数在被调用前必须完整定义
  • 函数可以先被声明,再被定义
    • 声明时,必须给出函数三要素(函数名,参数列表,返回值类型)
    • 定义时,必须完整给出函数体定义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include<stdio.h>

void sum(int n) {
int sum = 0;
for (int i = 1; i <= n; i++) {
if(i % 7 == 0 || i % 9 == 0){
printf("%d\n", sum);
sum += i;
}
}
printf("1到n之间所有能被7或者被9整除的数字的和: %d", sum);
}

int main() {

int n;
printf("第一题:键入长度 n");
scanf("%d", &n);
sum(n);
}

此时在 main 函数中使用 sum 函数并不需要显示的进行函数声明;

因为在同一个源文件中,如果函数的定义(即函数体)出现在对该函数的调用之前,那么编译器在编译到调用点时已经知道函数的存在和签名,因此不需要额外的函数声明。

当 C 文件被加载到内存中时候,他会从上往下去找 main 函数,再找到 main 函数之前他会先加载到 sum 函数,所以在 main 函数中进行使用时无需再次进行声明;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include<stdio.h>

int main() {
int n;
printf("第一题:键入长度 n");
scanf("%d", &n);
void sum(int n); // void sum(int);
sum(n);
}

void sum(int n) {
int sum = 0;
for (int i = 1; i <= n; i++) {
if(i % 7 == 0 || i % 9 == 0){
printf("%d\n", sum);
sum += i;
}
}
printf("1到n之间所有能被7或者被9整除的数字的和: %d", sum);

}

在同一个源文件中,如果你的函数调用出现在函数定义之前,你也需要在使用函数之前提供函数的声明,以便编译器知道函数的存在、返回类型以及它接受的参数。

声明时 void sum(int); 与 void sum(int a); 二者的区别

在C语言中,函数声明的void sum(int);void sum(int a);在大多数情况下被视为等价的,二者并无区别;但它们在上下文和用途上略有不同,主要区别在于它们是如何被解释的。

void sum(int);

这是C语言中标准的函数声明方式。它声明了一个名为sum的函数,该函数接受一个int类型的参数,但在这个声明中并没有为这个参数命名。这种声明方式通常用于头文件中,或者在你只想声明函数的存在而不关心参数具体名称时。由于参数没有具体名称,这个声明不会为参数分配存储空间或进行任何操作,它仅仅是告诉编译器sum是一个函数,接受一个int类型的参数,并返回一个void(即不返回任何值)。

void sum(int a);

它同样声明了一个名为sum的函数,该函数接受一个int类型的参数,并将其命名为a。然而,这种命名方式主要出现在函数定义中,即你实际编写函数体时。在函数声明中(特别是头文件中),虽然可以这样做,但它实际上并不为外部代码提供关于参数名称的任何有用信息,因为外部代码调用这个函数时不会使用到参数名a。此外,如果这个函数声明出现在多个地方(如头文件中),而每个声明中参数的名称不同,这可能会引起混淆,尽管从技术上讲这是合法的;a仅仅是一个占位符,用于说明函数接收一个int类型的参数。它本身并不在栈上分配空间或执行任何操作。

void

在 C 语言如果想要定义无参函数则需要以下定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void sum(void) {

}
==========================================================
void sum() { // 这种定义表示可接受任意类型、任意多个的参数

}

int main() {

// 下面这三种这种调用在编译时不会进行报错
sum(1);
sum();
sum(1, 2, 3);
}

数组作为参数

  • 可以在定义函数时使用数组形参(如:int f(int a [5]))
    • 数组形参需要使用同类型数组作为参数
    • 在 C 语言中,数组作为函数参数传递时大小信息丢失
    • 在函数内部修改数组形参,将影响数组实参
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include<stdio.h>

void demo(int a[3]) { // 也可以写成 int a[]
int len = strlen(a); // 'strlen' makes pointer from integer without a cast [-Wint-conversion]
a[0] = 50;
}

int main() {
int arry1[5] = {1, 2, 3, 4, 5};
int arry2[10] = {1, 20, 30};
// 此处并不会报错,因为数组作为函数参数传递时大小信息丢失,他不关心数组参数的个数;
// 对于数组参数来说,是将数组本身传递到了函数内部;
demo(arry1);
demo(arry2);

printf("arry1[0] = %d\n", arry1[0]); // arry1[0] = 50
printf("arry2[0] = %d", arry2[0]); // arry2[0] = 50

}

使用数组参数时如何获取数组的大小信息? 将数组的大小也作为参数,传递给函数;

递归

递归函数

  • 函数体中存在自我调用的函数
  • 递归函数必须有递归出口(边界条件)
  • 函数的无限递归将导致程序崩溃