一、函数概念
函数三要素
函数名 (地址)输入参数输出参数
int fun(int,int,char){xxx}
1.传入参数
实参: 调用时传入的具体值
形参: 函数内部接受的变量
2.返回值
返回函数处理的结果数据(另一种方式是函数外部使用址传递参数,来获得函数处理的数据)
二、函数参数
C语言函数传参,实际上就是copy的过程,参数类型的区别在于copy的多少的问题,当然类型不同可能存在丢失部分数据情况(copy不完整)
1
2
3
4
5
6
7
8
9
10
11
12
13
|
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; } |
1
2
3
4
5
6
7
8
9
10
11
12
13
|
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; } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
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.指针指向其他函数的函数名(标签)效果
1
2
3
4
5
6
7
8
9
10
|
int main() { int (*printf2)( const char *,...); //HelloWOrld printf ( "HelloWOrld\n" ); printf2 = printf ; //FFFF printf2( "FFFF\n" ); return 0; } |
2.指针指向其他函数的地址效果
1
2
3
4
5
6
7
8
9
10
|
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.通过二级指针,将一组函数线性化
1
2
3
4
5
6
|
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.结构体(变量)
值传递示例
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
|
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; } |
址传递示例 (通过指针)
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
|
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]即可
数组址传递数据示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
//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; } |
数组址传递地址示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
//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地址
1
2
3
4
5
6
7
8
9
10
11
12
|
char func1() { char s = 'A' ; return s; } int main() { char s= func1(); //41 printf ( "%X" ,s); return 0; } |
注意:指针是C语言中返回空间的唯一类型 (无法返回函数内部创建的变量地址,会被回收:异常)
int * func();
1
2
3
4
5
6
7
8
9
10
11
|
char * func1() { return "HelloWorld" ; } int main() { char *p= func1(); //HelloWorld printf ( "%s" ,p); return 0; } |
工程中一般不使用上述方案(常量区存储),意义不大,通常使用静态区存储局部变量(程序结束才回收),即如下方法
1
2
3
4
5
6
7
8
9
10
11
12
|
char * func1() { static char abc[] = "HelloWorld" ; return abc; } int main() { char *p= func1(); //HelloWorld printf ( "%s" ,p); return 0; } |
也可以使用堆区存储malloc()函数中创建的变量,注意使用完毕后一定要释放内存(free())
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
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语言函数概念和规则
本篇文章就到这里了,希望能给你带来帮助,也希望您能够多多关注服务器之家的更多内容!
原文链接:https://blog.csdn.net/weixin_41832302/article/details/119902730