函数(一)
函数就是一个能完成特定功能的模块,其程序代码独立,可以给函数传递参数,也可以得到返回值。函数的实现人员把函数设计成一个黑盒子,隐藏了实现细节,并对外部提供尽可能简单的接口。对此对于函数调用方来说,无需知道函数的内部工作原理,仅需知道功能、参数、返回值即可。
一 函数定义和声明
1.1函数定义
函数是一个完成特定功能的代码模块,其程序代码独立,通常要求有返回值,也可以是空值。一般形式如下:
/* <数据类型> <函数名称>( <形式参数说明> )
{
语句序列;
return[(<表达式>)];
}
}
<数据类型>是整个函数的返回值类型。return[(<表达式>)]语句中表达式的值,
要和函数的<数据类型>保持一致。如无返回值应该写为void型。
*/
/*例如*/
int function(int a , int b)
{
int c;
c = a + b;
return c;
}
1.2函数声明
函数的说明就是指函数原型 其中,<形式参数说明>可以缺省说明的变量名称,但类型不能缺省.
例如, double Power(double x, int n) ; double Power(double, int);
二 函数的调用、参数传递
2.1 函数的调用
函数的使用也叫函数的调用,形式如下: 函数名称(〈实际参数〉)
实参就是在使用函数时,调用函数传递给被调用函数的数据。需要确切的数据
函数调用可以作为一个运算量出现在表达式中,也可以单独形成一个语句。对于无返回值的函数来讲,只能形成一个函数调用语句。
tips:实参和形参的区别?
形参指的是出现在函数定义中的参数列表。实参指的是函数调用时,里面的参数。如下例程所示,函数定义中,a,b为形参,函数在main中调用时,m,n为实参。整个程序运行过程中,是将实参m、n的数据传递到开辟的形参a、b中,实现整个程序的功能。
int function(int a , int b)
{
return a + b;
}
int main(int argc , char **aggv)
{
int m = 10 , n = 20;
int c = 0;
c = function(m , n);
}
2.2 函数的参数传递(重点)
函数之间的参数传递方式:
全局变量、复制传递方式、地址传递方式
1、全局变量传递
全局变量就是在函数体外说明的变量,它们在程序中的每个函数里都是可见的,全局变量一经定义后就会在程序的任何地方可见。函数调用的位置不同,程序的执行结果可能会受到影响。不建议使用。
使用代码示例如下:
#include <stdio.h>
int n;
double factorial();
int main(int argc, char const *argv[])
{
double s = 0;
printf("input:");
scanf("%d", &n);
s = factorial();
printf("%e\n", s);
return 0;
}
double factorial()
{
double ret = 1;
int i;
for (i = 1; i < n; i++)
ret *= i;
return ret;
}
2、复制传递方式(值传递)
调用函数将实参传递给被调用函数,被调用函数将创建同类型的形参并用实参初始化。形参是新开辟的存储空间,因此,在函数中改变形参的值,不会影响到实参。如下程序所示:
#include <stdio.h>
void rev(int a , int b)
{
int t = 0;
t = a;
a = b;
b = t;
printf("a= %d , b= %d\n", a, b);
printf("a addr:%p , b addr:%p\n",&a,&b);
}
int main(int argc , char **aggv)
{
int m = 10 , n = 20;
rev(m , n);
printf("m = %d, n = %d\n", m, n);
printf("m addr:%p , n addr:%p\n",&m,&n);
}
此代码中,目的是实现数据的交换功能,但是打印结果却不符合需求(俩数交换),就是我们提到的“在函数中改变形参的值,不会影响到实参”;
形参和实参的特点:
①、形参只有在函数被调用时才会分配内存空间(在栈上),在调用结束后,释放分配的内存空间。因此形参只在函数内部有效。
②、实参可以是常量、变量、表达式、函数等,无论实参是何种类型的量,进行函数调用时,必须具有特定的值,以便把这些值传给形参。
③、形参和实参在数量上、类型上、顺序上应该严格一致,否则发送“类型匹配错误”。
3、地址传递方式
按地址传递,实参为变量的地址,而形参为同类型的指针。
被调用函数中对形参的操作,将直接改变实参的值(被调用函数对指针的目标操作,相当于对实参本身的操作)。
#include <stdio.h>
void rev(int *a , int *b)
{
int t = 0;
t = *a;
*a = *b;
*b = t;
printf("*a= %d , *b= %d\n", *a, *b);
printf("a addr:%p , b addr:%p\n",&a,&b);
}
int main(int argc , char **aggv)
{
int m = 10 , n = 20;
rev(&m , &n);
printf("m = %d, n = %d\n", m, n);
printf("m addr:%p , n addr:%p\n",&m,&n);
}
如果只是实现功能,不改变目标,可以使用值传递。如果要改变目标(实参),比如实现交换,小写转大写等,让目标值(实参)发生变化,就要用地址传递。
三 函数和数组
3.1 传递数组
当形参是数组形式时,其本质也是一个指针。数组作为实参传递时,形参并没有复制形参所有的内容,而是复制了实参数组的首地址。
由于数组的特殊性,只要知道了数组的首地址,就可以依次访问数组中所有元素。
示例程序如下(计算数组所有元素和及数组中奇数个数):
#include <stdio.h>
int test_array(int a[] , int n , int *p)
{
int i = 0, sum = 0;
*p = 0;
for (i = 0; i < n; i++)
{
sum += a[i];
if(a[i]%2)
(*p)++;
}
return sum;
}
int main(int argc, char **argv)
{
int a[] = { 9 , 12 , 2 , 3 , 29 , 31 , 40 , 80}, n = 0;
int sum = 0 , odd = 0;
n = sizeof(a)/sizeof(int);
sum = test_array(a,n,&odd);
printf("sum = %d odd numbers count = %d\n",sum,odd);
return 0;
}
3.2 传递指针
#include <stdio.h>
int test_array(int *a , int n , int *p)
{
int i = 0, sum = 0;
*p = 0;
for (i = 0; i < n; i++)
{
sum += *(a+i);
if(*(a+i)%2)
(*p)++;
}
return sum;
}
int main(int argc, char **argv)
{
int a[] = { 9 , 12 , 2 , 3 , 29 , 31 , 40 , 80}, n = 0;
int sum = 0 , odd = 0;
n = sizeof(a)/sizeof(int);
sum = test_array(a,n,&odd);
printf("sum = %d odd numbers count = %d\n",sum,odd);
return 0;
}
3.3 main函数参数
形式:int main(int argc, char **argv)
参数说明:argc是给main传递的参数个数 , argv是具体的参数集合。
示例如下:
#include <stdio.h>
int main(int argc, char const *argv[])
{
int i = 0;
printf("argc = %d\n" , argc);
for (i = 0; i < argc ; i ++)
printf("argv[%d] = %s\n", i, argv[i]);
return 0;
}
四 综合练习
1、实现求x的n次方的函数
答:
#include <stdio.h>
float my_pow(float a,int n);
int main(int argc,char *argv[])
{
float a = 2;
float sum = 0;
int n = 10;
puts("please input a:");
scanf("%f",&a);
puts("please input n:");
scanf("%d",&n);
sum = my_pow(a,n);
printf("%f\r\n",sum);
return 0;
}
float my_pow(float a,int n)
{
int i = 0;
float sum = 1;
for(i = 0;i<n;i++)
{
sum = sum*a;
}
return sum;
}
2、编写一个函数,统计字符串中小写字母的个数,并把字符串中的小写字母转化成大写字母,并输出转换后的字符串及小写字母个数。
#include <stdio.h>
#include <ctype.h>
int my_str1tostr2(char *str);
int main(int argc,char *argv[])
{
char str[100] = {0};
char *p = str;
int num = 0;
puts("please input a str:");
gets(p);
num = my_str1tostr2(p);
printf("%s %d\r\n",p,num);
return 0;
}
int my_str1tostr2(char *str)
{
int num = 0;
while(*str != '\0')
{
if(islower(*str))
{
num++;
*str = toupper(*str);
}
str++;
}
return num;
}
3、编写函数,计算一个一维整型数组的所有元素之和。
答:
/*
* @Description:
* @Author: 余红祥
* @Date: 2022-03-26 19:32:56
* @LastEditTime: 2022-07-04 22:23:30
* @LastEditors:
*/
#include <stdio.h>
int sum_array(int *str,int n);
int main(int argc,char *argv[])
{
int a[10] = {0};
int sum = 0,i = 0;
int *p = a;
for(i = 0;i < 10;i++)
{
scanf("%d",p+i);
}
p = a;
sum = sum_array(p,10);
printf("%d\r\n",sum);
return 0;
}
int sum_array(int *str,int n)
{
int sum = 0;
int i = 0;
while(i<n)
{
sum += *(str+i);
//str++;
i++;
}
return sum;
}
4、 编程实例:编写函数,删除字符串中的空格。
答:
#include <stdio.h>
void del_sapce(char *s1);
int main(int argc,char *argv[])
{
char s[] = " a b s h";
puts(s);
del_space(s);
puts(s);
return 0;
}
void del_space(char *s1)
{
char *s2 = s1;
while(*s1 != '\0')
{
if(*s1 == ' ')
{
s1++;
}
else
{
*s2 = *s1;
s1++;
s2++;
}
}
*s2 = '\0';
}
5、利用函数,实现strlen函数的功能。
答:
#include <stdio.h>
int my_strlen(const char *str);
int main(int argc ,char *arrgv[])
{
char str[100] = {0};
char *p = str;
int length = 0;
puts("please input a str :");
gets(p);
length = my_strlen(p);
printf("the str length is: %d\n",length);
return 0;
}
//确定指针目标的值在这里不需要修改,尝试使用const修饰符
/*function:求字符串长度
*argument:str:需要获取长度的字符串
*return:n:返回值为长度*/
int my_strlen(const char *str)
{
int n = 0;
while(*str++ != '\0')
n++;
return n;
}
ps:欢迎大佬指正,共同学习进步。