7.函数那点事.md 10.6 KB
Newer Older
极客江南's avatar
极客江南 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396
## 函数基本概念

- C源程序是由函数组成的

+ 例如: 我们前面学习的课程当中,通过main函数+scanf函数+printf函数+逻辑代码就可以组成一个C语言程序

- C语言不仅提供了极为丰富的库函数, 还允许用户建立自己定义的函数。用户可把自己的算法编写成一个个相对独立的函数,然后再需要的时候调用它

+ 例如:你用C语言编写了一个MP3播放器程序,那么它的程序结构如下图所示
+ ![](https://img-blog.csdnimg.cn/img_convert/5ae3272e9f2f49988524b0e511a40287.png)

- 可以说C程序的全部工作都是由各式各样的函数完成的,所以也把C语言称为函数式语言

---

## 函数的分类

- 在C语言中可从不同的角度对函数分类
- 从函数定义的角度看,函数可分为库函数和用户定义函数两种

+ ***库函数:*** 由C语言系统提供,用户无须定义,也不必在程序中作类型说明,只需在程序前包含有该函数原型的头文件即可在程序中直接调用。在前面各章的例题中反复用到printf、scanf、getchar、putchar等函数均属此类
+ ***用户定义函数:***由用户按需编写的函数。对于用户自定义函数,不仅要在程序中定义函数本身,而且在主调函数模块中还必须对该被调函数进行类型说明,然后才能使用

- 从函数执行结果的角度来看, 函数可分为有返回值函数和无返回值函数两种

+ ***有返回值函数:***  此类函数被调用执行完后将向调用者返回一个执行结果,称为函数返回值。(必须指定返回值类型和使用return关键字返回对应数据)
+ ***无返回值函数:*** 此类函数用于完成某项特定的处理任务,执行完成后不向调用者返回函数值。(返回值类型为void, 不用使用return关键字返回对应数据)

- 从主调函数和被调函数之间数据传送的角度看,又可分为无参函数和有参函数两种

+ ***无参函数:*** 在函数定义及函数说明及函数调用中均不带参数。主调函数和被调函数之间不进行参数传送。
+ ***有参函数:*** 在函数定义及函数说明时都有参数,称为形式参数(简称为形参)。在函数调用时也必须给出参数,称为实际参数(简称为实参)

---

## 函数的定义

- 定义函数的目的

+ 将一个常用的功能封装起来,方便以后调用

- 自定义函数的书写格式

```c
返回值类型 函数名(参数类型 形式参数1,参数类型 形式参数2,…) {
    函数体;
    返回值;
}
```

- 示例

```c
int main(){
    printf("hello world\n");
    retrun 0;
}
```

- 定义函数的步骤

+ 函数名:函数叫什么名字
+ 函数体:函数是干啥的,里面包含了什么代码
+ 返回值类型: 函数执行完毕返回什么和调用者

---

- 无参无返回值函数定义

+ 没有返回值时return可以省略

+ 格式:

  ```c
  void 函数名() {
      函数体;
  }
  ```

+ 示例:

  ```c
  // 1.没有返回值/没有形参
  // 如果一个函数不需要返回任何数据给调用者, 那么返回值类型就是void
  void printRose() {
      printf(" {@}\n");
      printf("  |\n");
      printf(" \\|/\n"); // 注意: \是一个特殊的符号(转意字符), 想输出\必须写两个斜线
      printf("  |\n");
    // 如果函数不需要返回数据给调用者, 那么函数中的return可以不写
  }
  ```

---

- 无参有返回值函数定义

+ 格式:

  ```c
  返回值类型 函数名() {
      函数体;
      return ;
  }
  ```

+ 示例:

  ```c
  int getMax() {
      printf("请输入两个整数, 以逗号隔开, 以回车结束\n");
      int number1, number2;
      scanf("%i,%i", &number1, &number2);
      int max = number1 > number2 ? number1 : number2;
      return max;
  }
  ```

---

- 有参无返回值函数定义

+ 形式参数表列表的格式: ```类型 变量名,类型 变量2,......```

+ 格式:

  ```c
  void 函数名(参数类型 形式参数1,参数类型 形式参数2,…) {
      函数体;
  }
  ```

+ 示例:

  ```c
  void printMax(int value1, int value2) {
      int max = value1 > value2 ? value1 : value2;
      printf("max = %i\n", max);
  }
  ```

---

- 有参有返回值函数定义

+ 格式:

  ```c
  返回值类型 函数名(参数类型 形式参数1,参数类型 形式参数2,…) {
      函数体;
      return 0;
  }
  ```

+ 示例:

  ```c
   int printMax(int value1, int value2) {
      int max = value1 > value2 ? value1 : value2;
      return max;
  }
  ```

---

- 函数定义注意

- 函数名称不能相同

  ```c
  void test() {
  }
  void test() { // 报错
  }
  ```

---

## 函数的参数和返回值

- 形式参数
  -***定义函数\***时,函数名后面小括号()中定义的变量称为形式参数,简称形参
  - 形参变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。
  - 因此,形参只有在函数内部有效,函数调用结束返回主调函数后则不能再使用该形参变量

```c
int max(int number1, int number2) //  形式参数
{
    return number1 > number2 ? number1 : number2;
}
```

------

- 实际参数
  -***调用函数\***时, 传入的值称为实际参数,简称实参
  - 实参可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,它们都必须具有确定的值,以便把这些值传送给形参
  - 因此应预先用赋值,输入等办法使实参获得确定值

```c
int main() {
    int num = 99;
    // 88, num, 22+44均能得到一个确定的值, 所以都可以作为实参
    max(88, num, 22+44); // 实际参数
    return 0;
}
```

------

- 形参、实参注意点

  - 调用函数时传递的实参个数必须和函数的形参个数必须保持一致

  ```c
  int max(int number1, int number2) { //  形式参数
      return number1 > number2 ? number1 : number2;
  }
  int main() {
      // 函数需要2个形参, 但是我们只传递了一个实参, 所以报错
      max(88); // 实际参数
      return 0;
  }
  ```

- - 形参实参类型不一致, 会自动转换为形参类型

  ```c
  void change(double number1, double number2) {//  形式参数
     // 输出结果: 10.000000, 20.000000
     // 自动将实参转换为double类型后保存
     printf("number1 = %f, number2 = %f", number1, number2);
  }
  int main() {
      change(10, 20);
      return 0;
  }
  ```

- - 当使用基本数据类型(char、int、float等)作为实参时,实参和形参之间只是值传递,修改形参的值并不影响到实参函数可以没有形参

  ```c
  void change(int number1, int number2) { //  形式参数
      number1 = 250; // 不会影响实参
      number2 = 222;
  }
  int main() {
      int a = 88;
      int b = 99;
      change(a, b);
      printf("a  = %d, b = %d", a, b); // 输出结果: 88, 99
      return 0;
  }
  ```

------

- 返回值类型注意点

  - 如果没有写返回值类型,默认是int

  ```c
  max(int number1, int number2) {//  形式参数
      return number1 > number2 ? number1 : number2;
  }
  ```

- - 函数返回值的类型和return实际返回的值类型应保持一致。如果两者不一致,则以返回值类型为准,自动进行类型转换

  ```c
  int height() {
      return 3.14; 
  }
  int main() {
    double temp = height();
    printf("%lf", temp);// 输出结果: 3.000000
  }
  ```

- - 一个函数内部可以多次使用return语句,但是return语句后面的代码就不再被执行

  ```c
  int max(int number1, int number2) {//  形式参数
      return number1 > number2 ? number1 : number2;
      printf("执行不到"); // 执行不到
      return 250; // 执行不到
  }
  ```

------

## 函数的声明

- 在C语言中,函数的定义顺序是有讲究的:

+ 默认情况下,只有后面定义的函数才可以调用前面定义过的函数

- 如果想把函数的定义写在main函数后面,而且main函数能正常调用这些函数,那就必须在main函数的前面进行函数的声明, 否则

+ 系统搞不清楚有没有这个函数
+ 系统搞不清楚这个函数接收几个参数
+ 系统搞不清楚这个函数的返回值类型是什么

- 所以函数声明,就是在函数调用之前告诉系统, 该函数叫什么名称, 该函数接收几个参数, 该函数的返回值类型是什么
- 函数的声明格式:

+ 将自定义函数时{}之前的内容拷贝到调用之间即可
+ 例如: ```int max( int a, int b );```
+ 或者: ```int max( int, int );```

```c
// 函数声明
void getMax(int v1, int v2);
int main(int argc, const char * argv[]) {
    getMax(10, 20); // 调用函数
    return 0;
}
// 函数实现
void getMax(int v1, int v2) {
    int max = v1 > v2 ? v1 : v2;
    printf("max = %i\n", max);
}
```

- 函数的声明与实现的关系

+ 声明仅仅代表着告诉系统一定有这个函数, 和这个函数的参数、返回值是什么
+ 实现代表着告诉系统, 这个函数具体的业务逻辑是怎么运作的

- 函数声明注意点:

+ 函数的实现不能重复, 而函数的声明可以重复

  ```c
  // 函数声明
  void getMax(int v1, int v2);
  void getMax(int v1, int v2);
  void getMax(int v1, int v2); // 不会报错
  int main(int argc, const char * argv[]) {
      getMax(10, 20); // 调用函数
      return 0;
  }
  // 函数实现
  void getMax(int v1, int v2) {
      int max = v1 > v2 ? v1 : v2;
      printf("max = %i\n", max);
  }
  ```

- 函数声明可以写在函数外面,也可以写在函数里面, 只要在调用之前被声明即可

  ```c
  int main(int argc, const char * argv[]) {
      void getMax(int v1, int v2); // 函数声明, 不会报错
      getMax(10, 20); // 调用函数
      return 0;
  }
  // 函数实现
  void getMax(int v1, int v2) {
      int max = v1 > v2 ? v1 : v2;
      printf("max = %i\n", max);
  }
  ```

- 当被调函数的函数定义出现在主调函数之前时,在主调函数中也可以不对被调函数再作声明

  ```c
  // 函数实现
  void getMax(int v1, int v2) {
      int max = v1 > v2 ? v1 : v2;
      printf("max = %i\n", max);
  }
  int main(int argc, const char * argv[]) {
      getMax(10, 20); // 调用函数
      return 0;
  }
  ```

- 如果被调函数的返回值是整型时,可以不对被调函数作说明,而直接调用

  ```c
  int main(int argc, const char * argv[]) {
      int res = getMin(5, 3); // 不会报错
      printf("result = %d\n", res );
      return 0;
  }
  int getMin(int num1, int num2) {// 返回int, 不用声明
      return num1 < num2 ? num1 : num2;
  }
  ```

---



最后,如果你有任何疑问 加微信 leader_fengy 拉你进学习交流群,另外开源项目有配套视频教程哦。