C语言的函数概念与规则你了解吗

编辑: admin 分类: c#语言 发布时间: 2021-12-12 来源:互联网
目录
  • 一、函数概念
    • 1.传入参数
    • 2.返回值
  • 二、函数参数
    • 三、指针函数-函数名指针化
      • 1.指针指向其他函数的函数名(标签)效果
      • 2.指针指向其他函数的地址效果
      • 3.通过二级指针,将一组函数线性化
    • 四、函数值传递和址传递
      • 五、函数连续空间的传递
        • 1.结构体(变量)
        • 2.数组(标签)
        • 3.连续空间的只读
      • 六、函数返回值
        • 总结

          一、函数概念

          函数三要素
          int fun(int,int,char){xxx}

          函数名 (地址)输入参数输出参数

          1.传入参数

          实参: 调用时传入的具体值
          形参: 函数内部接受的变量

          2.返回值

          返回函数处理的结果数据(另一种方式是函数外部使用址传递参数,来获得函数处理的数据)

          二、函数参数

          C语言函数传参,实际上就是copy的过程,参数类型的区别在于copy的多少的问题,当然类型不同可能存在丢失部分数据情况(copy不完整)

          void printAddress(int a)
          {
          	//printAddress a地址:0xc
              printf("printAddress a地址:%p\n",a);
          }
          int main()
          {
              int a = 12;
              //main a地址:0xc
              printf("main a地址:%p\n",a);
              printAddress(a);
              return 0;
          }
          
          void printAddress(int a)
          {
          	//printAddress a地址:0xffffff9b
              printf("printAddress a地址:%p\n",a);
          }
          int main()
          {
              char a = "ABCDE";
              //main a地址:0xffffff9b
              printf("main a地址:%p\n",a);
              printAddress(a);
              return 0;
          }
          
          void printAddress(int a)
          {
              //printAddress a地址:0xeccc1e18
              printf("printAddress a地址:%p\n", a);
          }
          int main()
          {
              int a = 12;
              int *p_a;
              p_a = &a;
              //main a地址:0x7ffeeccc1e18
              printf("main a地址:%p\n", p_a);
              printAddress(p_a);
              return 0;
          }
          

          三、指针函数-函数名指针化

          通过函数三要素可以得出,指针也可以表示函数
          int (*p)(int,int,char){xxx} 与指针指向二维数组类似,指针必须要加(),来提高程序对他解读的优先级,而不是默认从右往左读

          1.指针指向其他函数的函数名(标签)效果

          int main()
          {
              int (*printf2)(const char *,...);
              //HelloWOrld
              printf("HelloWOrld\n");
              printf2 = printf;
              //FFFF
              printf2("FFFF\n");
              return 0;
          }
          

          2.指针指向其他函数的地址效果

          int main()
          {
              int (*printf2)(const char *, ...);
              //HelloWOrld 0x7fff204b50b8
              printf("HelloWOrld %p \n", printf);
              printf2 = (int (*)(const char *, ...))0x7fff204b50b8;
              //FFFF
              printf2("FFFF\n");
              return 0;
          }
          

          3.通过二级指针,将一组函数线性化

          int (*p[5])(int,int);
          p[0]=func1;
          p[1]=func2;
          p[2]=func3;
          //使用效果,可以替代switch
          p[day](8,9);
          

          四、函数值传递和址传递

          值传递:void fun1(int a){...}传入数据a copy到函数中,函数中数据的修改不影响函数外被传入的数据
          址传递:void func2(*a){...}传入数据地址&a copy到函数中,函数中数据的修改是在该地址上修改的,函数外被传入的数据也处在该内存地址上,所以会数据被修改

          仅查看时用值传递
          读写资源或节省资源时 使用址传递

          五、函数连续空间的传递

          连续空间传递,一般都会选择使用址传递,使用值传递浪费资源过多

          1.结构体(变量)

          值传递示例

          struct product_t
          {
              char name[20];
              int price;
          };
          
          void println(struct product_t product)
          {
              strncpy(product.name, "牙刷", 20);
              product.price = 3;
              //println:牙刷
              printf("println:%s \n", product.name);
              //println:3
              printf("println:%d \n", product.price);
          }
          int main()
          {
              struct product_t product;
              strncpy(product.name, "手机", 20);
              product.price = 3666;
              println(product);
          
              //main:手机
              printf("main:%s \n", product.name);
              //main:3666
              printf("main:%d \n", product.price);
              return 0;
          }
          

          址传递示例 (通过指针)

          struct product_t
          {
              char name[20];
              int price;
          };
          
          void println(struct product_t *product)
          {
              //product[0]等价于*product此处可以替换,但是需要加()让程序优先识别,即:(*product)
              strncpy(product[0].name, "牙刷", 20);
              product[0].price = 3;
              //println:牙刷
              printf("println:%s \n", product[0].name);
              //println:3
              printf("println:%d \n", product[0].price);
          }
          int main()
          {
              struct product_t product;
              strncpy(product.name, "手机", 20);
              product.price = 3666;
              println(&product);
          
              //main:牙刷
              printf("main:%s \n", product.name);
              //main:3
              printf("main:%d \n", product.price);
              return 0;
          }
          

          2.数组(标签)

          数组int names[10];在定义时,编译器已经知道其首地址被names标签描述,因此数组作为形参时只需要传递数组的标签名和偏移量即可names[10]即可

          数组址传递数据示例

          //C语言现在优化允许使用int p[10],便于阅读 实际上效果与int *p一致,都是址传递
          void println(int *p)
          {
              p[0] = 77;
              //println:77
              printf("println:%d \n", p[0]);
              //println:11
              printf("println:%d \n", p[1]);
          }
          int main()
          {
              int prices[10] = {22, 11, 55};
              println(prices);
          
              //main:77
              printf("main:%d \n", prices[0]);
              //main:11
              printf("main:%d \n", prices[1]);
              return 0;
          }
          

          数组址传递地址示例

          //C语言现在优化允许使用int p[10],便于阅读 实际上效果与int *p一致,都是址传递
          void println(int *p)
          {
              p[0] = 77;
              //println:0x7ffee8964df0
              printf("println:%p \n", &p[0]);
              //println:0x7ffee8964df4
              printf("println:%p \n", &p[1]);
          }
          int main()
          {
              int prices[10] = {22, 11, 55};
              println(prices);
          
              //main:0x7ffee8964df0
              printf("main:%p \n", &prices[0]);
              //main:0x7ffee8964df4
              printf("main:%p \n", &prices[1]);
              return 0;
          }
          

          如上两段代码,数组传递为址传递,数组标签名本身就是地址的标签,与指针非常相似

          3.连续空间的只读

          定义参数时使用const修饰指针所指的内存地址即可,如const char *p,这也是开发时默认的规范
          读写函数形参:char *p;
          只读函数形参:const char *p
          如strncpy函数的源码:char *stpncpy(char *dst, const char *src, size_t n)

          六、函数返回值

          返回值与参数一样,也是copy的原理传递,可以copy值,也可以copy地址

          char func1()
          {
              char s = 'A';
              return s;
          }
          int main()
          {
              char s= func1();
              //41
              printf("%X",s);
              return 0;
          }
          

          注意:指针是C语言中返回空间的唯一类型 (无法返回函数内部创建的变量地址,会被回收:异常)
          int * func();

          char * func1()
          {
              return "HelloWorld";
          }
          int main()
          {
              char *p= func1();
              //HelloWorld
              printf("%s",p);
              return 0;
          }
          

          工程中一般不使用上述方案(常量区存储),意义不大,通常使用静态区存储局部变量(程序结束才回收),即如下方法

          char * func1()
          {
              static char abc[] = "HelloWorld";
              return abc;
          }
          int main()
          {
              char *p= func1();
              //HelloWorld
              printf("%s",p);
              return 0;
          }
          

          也可以使用堆区存储malloc()函数中创建的变量,注意使用完毕后一定要释放内存(free())

          char * func1()
          {
              char *s = (char *)malloc(50);
              strncpy(s,"HelloWorld",50);
              return s;
          }
          int main()
          {
              char *p= func1();
              //HelloWorld
              printf("%s",p);
              free(p);
              return 0;
          }
          

          总结

          本章主要为C语言函数概念和规则

          本篇文章就到这里了,希望能给你带来帮助,也希望您能够多多关注海外IDC网的更多内容!

          【文章出处:cc防御 转载请说明出处】