概要

  • 关键字: return
  • 运算符: *(一元), &(一元)
  • 函数及其定义方式
  • 如何使用参数和返回值
  • 如何把指针变量用作函数参数
  • 函数类型
  • ANSI C原型
  • 递归

复习函数

  1. /*FuXinFunc.c -- 复习函数*/
  2. #include "stdio.h"
  3. int sum(int a,int b);
  4. int main(){
  5. int a = 10,b = 50;
  6. printf("%d",sum(a,b));
  7. return 0;
  8. }
  9. int sum(int a,int b){
  10. return a + b;
  11. }

函数参数

  1. /*lethead2.c -- 函数参数*/
  2. #include "stdio.h"
  3. #include "string.h" //为strlen()提供原型
  4. #define NAME "chen"
  5. void hello(const char ch[]);
  6. void main(){
  7. hello(NAME);
  8. }
  9. void hello(const char ch[]){
  10. printf("%s\n",ch);
  11. }

带形式参数的函数

  1. //错误写法
  2. void hello(int a,b,c);
  1. //正确写法
  2. void hello(int a,int b,int c);

声明带形式参数函数的原型

  1. //一般写法
  2. int sum(int a,int b);
  1. int sum(int,int);

调用实际参数的函数

  1. #include "stdio.h"
  2. #include "lethead1.h"
  3. #define SUM 10
  4. // int sum(int a,int b); /*函数原型*/
  5. void main(){
  6. const char ch[] = NAME;
  7. printf("%s\n",ch);
  8. printf("%d\n",sum(2,3+SUM/2));
  9. }
  10. int sum(int a, int b){
  11. return a + b;
  12. }

黑盒视角

黑盒里面发生了什么对主调函数是不可见的。

return从函数中返回值

  1. int sum(int a, int b){
  2. return a + b;
  3. }

ANSI C原型对象

缺点
  1. #include "stdio.h"
  2. #include "string.h" //为strlen()提供原型
  3. #define NAME "chen"
  4. void hello1();//声明没有形参
  5. void main(){
  6. }
  7. void hello1(int a){ //定义函数有形参
  8. printf("%d",a);
  9. }

问题所在

我们看hello1()函数相关的一些示例,用过去声明函数的方式声明了hello1()函数,然后错误的使用该函数

ANSI 解决方案
  1. /*proto.c -- 使用函数原型*/
  2. #include "stdio.h"
  3. int sum(int,int); /*函数原型*/
  4. void main(){
  5. printf("%d\n",sum(10,20));
  6. }
  7. int sum(int a,int b){
  8. return a + b;
  9. }
  10. /* int sum(int f,int e){
  11. return f + e;
  12. }
  13. */
  1. #include "stdio.h"
  2. int sum(int a,int b){ //既是函数原型,又是函数定义
  3. return a + b;
  4. }
  5. void main(){
  6. printf("%d\n",sum(10,20));
  7. }

递归

  1. /*recur.c -- 递归*/
  2. #include "stdio.h"
  3. int hello(int); /*函数原型*/
  4. void main(){
  5. printf("%d",hello(100));
  6. }
  7. int hello(int i){
  8. if(i <= 0){
  9. return 0;
  10. }
  11. else{
  12. return hello(i - 1) + 1;
  13. }
  14. }

递归优点是省去了一部分重复代码的时间,缺点是不容易阅读与维护
递归常见错误: 栈溢出(stack overflow)

  • 栈区
    • 局部变量
    • 函数形参
  • 堆区
    • 动态开辟的内存
    • 内存
    • malloc
    • calloc
  • 方法区
    • 全局变量
    • static静态变量

程序员的知乎

Linux

  1. gcc file1.c file2.c

生成的是.out文件

使用头文件

  1. #ifndef __LETHEAD1_H
  2. #define __LETHEAD1_H
  3. #define NAME "chen"
  4. // int sum(int a,int b);
  5. int sum(int,int);
  6. #endif
  1. /*lethead1.c -- 创建并使用简单函数*/
  2. #include "stdio.h"
  3. #include "lethead1.h"
  4. #define SUM 10
  5. // int sum(int a,int b); /*函数原型*/
  6. void main(){
  7. const char ch[] = NAME;
  8. printf("%s\n",ch);
  9. printf("%d\n",sum(2,3+SUM/2));
  10. }
  11. int sum(int a, int b){
  12. return a + b;
  13. }

查询地址: &运算符

  1. /*loccheck.c -- 查看变量存储在何处*/
  2. #include "stdio.h"
  3. int sum(int,int); /*函数原型*/
  4. void main(){
  5. int i = 10;
  6. int* p = sum(2,3);
  7. printf("i=%d,地址=%p\n",p,&p);
  8. printf("i=%d,地址=%p\n",i,i = &p);
  9. }
  10. int sum(int a,int b){
  11. return a + b;
  12. }

&运算符目前得出的结论是只对变量有用

指针介绍

指针(pointer)值为内存地址的变量(或数据对象)
定义指针:

  1. 基本数据类型* 变量名;

指针赋值

  1. 指针变量名 = & + 变量/常量/函数

间接运算符: *

假设已知ptr指向barch:

  1. ptr = &barch

然后使用间接运算符(indirection operator)找出barch中的值,该运算符有时也称为引用运算符(dereferencing operator)。不要把间接运算符和乘法运算符()混淆

指针在函数间通讯

  1. /*swap3.c -- 指针*/
  2. #include "stdio.h"
  3. int sum(int*,int*);/*函数原型*/
  4. void main(){
  5. sum(5,9);
  6. }
  7. int sum(int* a,int* b){
  8. printf("a的地址=%p,b的地址=%p\n",&a,&b);
  9. return a;
  10. }

strcpy()

  1. #include "string.h"
  2. int main(){
  3. char arr1[] = "bit";
  4. char arr2[20] = "##########";
  5. //拷贝后 "bit\0#######"
  6. strcpy(arr2,arr1);
  7. printf("%s\n",arr2);//printf发现'\0'所以后面就不打印输出了
  8. return 0;
  9. }

memory(记忆,计算机指令叫内存)

memset(设置)

  1. #include "string.h"
  2. int main(){
  3. char arr[] = "hello world";
  4. memset(arr,'*',5);//'*'存储的是ASCI码值
  5. printf("%s\n",arr);
  6. return 0;
  7. }

C和C++官网手册

自定义函数

自定义函数与库函数一样,有函数名,返回类型和函数参数

函数的组成

  1. ret_type fun_name(paral, *){
  2. statement; //语句项
  3. }
  4. //ret_type : 返回值类型
  5. //fun_name : 函数名称
  6. //paral : 函数参数

传指针变量

我们可能变量在变,但是我们确定是不是同一个变量就可以传入指针

  1. /*prointerFun1.c -- 指针函数1*/
  2. #include "stdio.h"
  3. int* fun1(int* p);
  4. void swap1(int*,int*);
  5. void main(){
  6. int* p1;
  7. p1 = (int)100;
  8. printf("%p\n",fun1(p1));
  9. int* a = 10;
  10. int* b = 20;
  11. printf("a=%p,b=%p\n",&a,&b);
  12. printf("a=%d,b=%d\n",a,b);
  13. swap1(&a,&b);
  14. printf("a=%p,b=%p\n",&a,&b);
  15. printf("a=%d,b=%d\n",a,b);
  16. }
  17. int* fun1(int* p){
  18. return p + 1;
  19. }
  20. void swap1(int* a, int* b){
  21. int tmp = *a;
  22. *a = *b;
  23. *b = tmp;
  24. }

结果为:

  1. 0000000000000068
  2. //从下面开始
  3. a=000000000061FE10,b=000000000061FE08
  4. a=10,b=20
  5. a=000000000061FE10,b=000000000061FE08
  6. a=20,b=10

得知: 内存地址没有被更换里面的值就已经更换了

函数声明和定义

函数声明
  • 告诉编译器有一个函数叫什么,参数是什么,返回类型是什么,但是具体是不是存在,无关紧要。
  • 函数的声明一般出现在函数的使用之前,要先满足先声明后使用
  • 函数的声明一般要放在头文件中的。 ```c

    ifndef __HANSHUMING_H

    define __HANSHUMING_H

//函数声明 int sum(int a , int b);

endif

```

如果你在工程里面自己引入了一个头文件比方说stdio.h别人为了打印也引用了这份头文件;大家都引入stdio.h的时候,这样就重复了,这样同样的代码就重复了好多次,为了避免同一个头文件被很多人引用多次,我们的做法是**#ifndef(if:如果;n:not; def:定义) __根据每个头文件的名字_H__(如果成立则执行以下代码直到包含结束)**
#endif(包含结束)
所以这样就避免了同一个文件被定义多次

函数定义

函数的定义是指函数的具体实现,交待函数的功能实现