c语言 函数

一.函数

1.1定义函数的格式:

返回值类型 函数名(函数的参数列表){

        函数体; //用来实现功能的代码块

}

1.2函数的参数:

形式参数:在定义函数时 ()里面的参数,简称“形参”。

实际参数:调用函数时 ()里面的参数,简称”实参“。

实参的个数、类型 都要和形参保持一致。

1.3函数的返回值

函数的返回值就是将函数执行的结果返回给函数调用使用,不需要返回值可以不写。

1.4函数传参方式

1.4.1复制传参(值传递)

把实参的值复制给形参,函数内对形参的任何操作都不会改变实参的的值

#include<stdio.h>
// 功能:将参数扩大100倍后求和
void user_add(int num1, int num2){
	num1 *= 100;
	num2 *= 100;
	printf("%d\n",num1+num2);
}

int main(int argc, const char *argv[])
{
	int a = 1;
	int b = 2;
	user_add(a, b);  //300
	return 0;
}
1.4.2地址传参(地址传递)

把指针变量所指向变量的地址复制给形参,如果函数里对指针变量所指向变量的值修改,实参也会被修改

#include <stdio.h>

int user_add(int x, int y, int *z){
	printf("my_add : z = %p\n", z); // 和实参c的地址是一样的
	*z = x+y;
}

int main(int argc, const char *argv[])
{
	int a = 100;
	int b = 200;
	int c = 0;
	printf("main : &c = %p\n", &c); // 和函数里的z是一样的
	user_add(a, b, &c);
	printf("c = %d\n", c); // 300

	return 0;
}

注意:形参是指针也不一定地址传参,有可能是指针的复制传参。如下代码:

#include <stdio.h>

int m = 100;
int n = 200;

void func1(int *q){
	q = &n;
}

void func2(int **q){
	*q = &n;
}

int main(int argc, const char *argv[])
{
	int *p = &m;
	// 一级指针的值传递
	func1(p);
	printf("*p = %d\n", *p); // 100
	
	// 一级指针的地址传递
	func2(&p);
	printf("*p = %d\n", *p); // 200

	return 0;
}

1.5数组的传参方式

1.5.1字符串数组

字符串传参只需要传首地址,因为每个字符串结尾都有‘\0’来标识字符串结束。

#include<stdio.h>
char *user_strcpy(char *dest, const char*src){
	char *temp = dest;
	while(*src != '\0'){
		*dest = *src;
		dest++;
		src++;
	}
	*dest = *src;  //'\0'fu赋给目标字符串
	return temp;
}


int main(int argc, const char *argv[])
{
	char s1[10] = "hello";
	char s2[10] = "1234";
	user_strcpy(s1,s2);
	printf("s1 = [%s]  s2 = [%s]\n", s1, s2); // 1234

	char s3[] = "world";
	//用user_strcpy函数的返回值作为实参
	user_strcpy(s1, user_strcpy(s2, s3));
	printf("s1 = [%s]  s2 = [%s]  s3 = [%s]\n", s1, s2, s3); // world
	return 0;
}
1.5.2整型数组

整型数组传参时,既要传首地址,还要传数组的长度。

include <stdio.h>

// 常用的写法----用指针做形参即可
void print_arr_1(int *p, int len){
	for(int i = 0; i < len; i++){
		printf("%d  ", p[i]);
	}
	printf("\n");
}

// 这种写法叫做代码的自注释
// p 的本质还是指针 并不是一个长度为10的数组
//void print_arr_2(int p[10], int len){

void print_arr_2(int p[], int len){
	printf("sizeof(p) = %ld\n", sizeof(p)); // 8
	for(int i = 0; i < len; i++){
		printf("%d  ", p[i]);
	}
	printf("\n");
}

int main(int argc, const char *argv[])
{
	int s[10] = {1,2,3,4,5,6,7,8,9,10};
	print_arr_1(s, 10);

	print_arr_1(s, 5);

	print_arr_2(s, 10);

	return 0;
}
1.5.3二维数组

二维数组作为实参时,形参需要用数组指针

#include <stdio.h>

void print_arr(int (*p)[4], int hang, int lie){
	for(int i = 0; i < hang; i++){
		for(int j = 0; j < lie; j++){
			printf("%d  ", p[i][j]);
		}
		printf("\n");
	}
}

int main(int argc, const char *argv[])
{
	int s[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
	print_arr(s, 3, 4);

	return 0;
}

1.6main函数的参数

int main(int argc, const char *argv[ ])

argc是执行程序时命令行的参数的个数(包括可执行文件名./a.out)

argv是一个指针,指向一个const cgar *类型的指针数组

数组排序,终端输入./a.out -升序,./a.out -降序

#include<stdio.h>
//封装排序的函数,flag=1升序,flag=0降序
int user_sort(int *p, int len, int flag){
	int i = 0;
	int j = 0;
	int temp = 0;
	if(flag){   //flag != 0
		for(i = 0;i < len-1;i++){
			for(j = 0;j < len-1-i;j++){
				if(p[j] > p[j+1]){
					temp = p[j];
					p[j] = p[j+1];
					p[j+1] = temp;
				}
			}
		}
	}else{
		for(i = 0;i < len-1;i++){
			for(j = 0;j < len-1-i;j++){
				if(p[j] < p[j+1]){
					temp = p[j];
					p[j] = p[j+1];
					p[j+1] = temp;
				}
			}
		}
	}
	return 0;
}
int main(int argc, const char *argv[])
{
	//入参合法性检查
	if(2 != argc){
		printf("Usage : %s -s/-j\n", argv[0]);
		return -1;
	}
	int s[10] = {11, 23, 32, 54, 5, 1, 18, 96, 7, 9};
	//if(!strcmp(argv[1]."-s"))  用strcmp比较字符串
	//比较字符 if('s' == *(argv[1]+1))  argv[1]是命令行第二个字符串的地址,+1在1取*操作的是  -后面的字符
	if('s' == *(*(argv+1)+1)){   //if('s' == argv[1][1])
		user_sort(s, 10, 1);
	}else{
		user_sort(s, 10, 0);
	}
	//输出
	for(int i = 0; i < 10; i++){
		printf("%d  ", s[i]);
	}
	printf("\n");
	return 0;
}

2.函数与指针、数组

2.1指针函数

本质是一个函数,返回值是一个指针类型。

注意事项:

1.不能返回局部变量的地址,局部变量占用的内存在函数结束后被操作系统收回。

2. 可以返回:全局变量的地址、static修饰的局部变量的地址、通过参数传递的地址、

                        堆区手动分配的内存空间。

#include<stdio.h>
#include<stdlib.h>
int *func1(int x, int y){
	int temp = x + y;
	return &temp; //局部变量占用的内存在函数结束时就被操作系统回收
}	
	int value = 0;//全局变量
int *func2(int x, int y){
	value = x + y;
	return &value;
}
int *func3(int x, int y){
	static int temp = 0;  //static修饰
	temp = x+y;
	return &temp;
}

int *func4(int x, int y, int *z){ //地址传参
	*z = x+y;
	return z;
}
int main(int argc, const char *argv[])
{
	int *p = NULL;
//  p = func1(10, 20);\
	printf("%d\n",*p);    //结果不可预知,错误用法,不能返回局部变量地址
	
//	p = func2(10, 20);\
	printf("%d\n",*p);    //30  可以返回全局变量地址

	//p = func3(10, 20);
	//printf("%d\n", *p); // 30 可以返回static修饰的局部变量地址
	
	int num = 0;
	p = func4(10, 20, &num);
	printf("%d\n", *p); // 30 可以返回地址传参的地址

	int *q = (int *)malloc(sizeof(int));  //指针指向堆区使用malloc函数分配4字节内存的地址
	*q = 10 + 20;
	printf("%d\n",*q); //30 可以返回堆区手动分配空间的地址
	free(q);//释放
	p = NULL; //防止误操作导致非法访问free,需将q置 NULL
	return 0;
}

2.2函数指针

本质是个指针,指向一个函数

格式:返回值类型  (*函数指针名)(形参列表)

#include<stdio.h>
void user_swap(int *x, int *y){
	*x = *x ^ *y;
	*y = *x ^ *y;
	*x = *x ^ *y;
}
int main(int argc, const char *argv[])
{
	//定义一个函数指针p,指向一个返回值类型是void、形参列表是(int *,int *)类型的函数
	void (*p)(int *x, int *y) = NULL;
	p = user_swap; //函数名就是函数的首地址
	
	//函数指针指向函数后,可以通过函数指针调用函数
	int a = 10;
	int b = 20;
	user_swap(&a, &b);
	printf("a=%d b=%d\n",a, b); //20 10

	a = 10;
	b = 20;
	p(&a, &b);
	printf("a=%d b=%d\n",a, b); //20 10
	return 0;
}

函数指针的典型使用场景----用作回调函数

将函数指针p作为函数user的形参。在函数user内部通过函数指针去调用函数时,具体用哪个函数由用户在调用函数user时传递的第三个参数。第三个参数传递到哪个函数,p就调用哪个函数。次用法称为回调函数。

#include<stdio.h>

int my_add(int x, int y){
	printf("%d\n", x+y);
}

int my_sub(int x, int y){
	printf("%d\n", x-y);
}
int user(int x, int y, int (*p)(int, int)){
	p(x, y);
}

int main(int argc, const char *argv[])
{
	int a = 10;
	int b = 20;
	user(a, b, my_add);//30
	user(a, b, my_sub);//-10
	return 0;
}

2.3函数指针数组

返回值类型  (*函数指针数组名][下标](函数的参数列表)

int  (*p[2])(int); 

2.4函数指针数组指针

返回值类型 (*(*a)[下标])(函数的参数列表)

//int  (*(*p)[2])(int);

3.字符串函数

3.1strlen

#include <string.h>

size_t strlen(const char *s);

参数:要计算长度的字符串的首地址

返回值:计算的结果

功能:计算字符串长度,第一个'\0'之前的字符的个数

sizeof 和 strlen 的区别:
strlen是函数调用 计算第一个'\0'之前的字符数
sizeof是一个C语言关键字 计算变量或者类型占用的内存空间的大小的 

#include <stdio.h>
#include <string.h>

int main(int argc, const char *argv[])
{
	char s1[20] = "hello world";

	// 可以定义变量保存结果
	int ret = strlen(s1);
	printf("ret = %d\n", ret); // 11
	
	// 也可以直接把结果打印到终端
	printf("strlen(s1) = %ld\n", strlen(s1)); // 11

	char s2[10] = "hqyj";
	printf("strlen(s2) = %ld\n", strlen(s2)); // 4
	// C语言对字符串处理时遇到\0就结束了
	char s3[10] = "ab\0cd";
	printf("strlen(s3) = %ld\n", strlen(s3)); // 2

	//char s1[20] = "hello world";
	printf("strlen(s1) = %ld\n", strlen(s1)); // 11
	printf("sizeof(s1) = %ld\n", sizeof(s1)); // 20 
	
	return 0;
}

自定义函数

#include<stdio.h>

//定义一个计算字符串长度
int user_strlen(char *p){  //p是存字符串的数组名
#if 0
	int count = 0;         //定义一个count来计数 
	while(p[count] != 0){  // 0='\0' 计算第一个'\0'之前的字符的个数 
		count++;
	}
#endif

#if 0
	int count = 0;        
	while(p[count]){  // p[count]这个表达式的结果非0即可执行while循环
		              // 等价于while(s[i] != 0)
		count++;
	}
#endif  

#if 1
	int count = 0;        
	while(p[count++]); // p[count++])这个表达式的结果非0即可执行while循环,等价于上方代码
	count--;	       // 当p[count++]等于'0'时,因为count++自增,count保存的数比实际数多加了一次1
	                   //所以count需要自减1   
#endif  

	return count;
}

int main(int argc, const char *argv[])
{
	char arr[20] = "hello\0word";
	int ret = user_strlen(arr);
	printf("%d",ret);

	return 0;
}

3.2strcpy

#include  <string.h>

char *strcpy(char *dest, const char *src);

功能:字符串的拷贝,将src指向的字符串包括'\0'都拷贝给dest指向的空间

参数:

        dest:目标字符串

        src:源字符串

返回值:dest的首地址

#include <stdio.h>
#include <string.h>

int main(int argc, const char *argv[])
{
	char s1[20] = "hello world";
	char s2[200] = "beijing";

	printf("拷贝前:s1 = [%s]\n", s1); // hello world
	printf("拷贝前:s2 = [%s]\n", s2); // beijing

	// 把s2拷贝给s1  要保证s1足够大!!!
	strcpy(s1, s2);

	printf("拷贝后:s1 = [%s]\n", s1); // beijing
	printf("拷贝后:s2 = [%s]\n", s2); // beijing

	// s1中剩下的字符 还在里面 只不过通过字符串的方式 访问不到了
	printf("s1[7] = [%c] [%d]\n", s1[7], s1[7]); // 0
	printf("s1[8] = [%c] [%d]\n", s1[8], s1[8]); // r
	printf("s1[9] = [%c] [%d]\n", s1[9], s1[9]); // l
	printf("s1[10] = [%c] [%d]\n", s1[10], s1[10]); // d

	// strcpy函数也可以用来给字符串重新赋值
	char s3[10] = "abcd";
	//s3 = "hqyj";//错误的写法
	strcpy(s3, "hqyj");
	printf("s3 = [%s]\n", s3); // hqyj

	return 0;
}

自定义函数

#include<stdio.h>

void user_strcpy(char *dest, char *src){
#if 0
	int i = 0;
	while(src[i]){    //等价于while(src[i] != '\0'){
		dest[i] = src[i];
		i++;
	}
	dest[i] = src[i]; // 等价于dest[i] = '\0',要把src中的'\0'复制给dest中
#endif
	
#if 0
	int i = 0;
	while(src[i]){               //while(dest[i++]=src[i++]);相当于i++进行了两次
		dest[i++]=str[i]);       //while(dest[i++]=src[i++]);相当于i++进行了两次
}                               //while(dest[i]=src[i++]);因为i++自增,相当于把src的第i位赋值给dest的i+1
	dest[i] = src[i];           // 等价于dest[i] = '\0',要把src中的'\0'复制给dest中
	
#endif

	int i = 0;
	while(dest[i++]=str[i]);    //代码先执行str给dest赋值,后执行while循环
}                               //已经把str中的'\0'赋值给了dest中,判断while循环结束

int main(int argc, const char *argv[])
{
	char s1[20] = "hello world";
	char s2[20] = "beijing";

	printf("拷贝前:s1 = [%s]\n", s1); // hello world
	printf("拷贝前:s2 = [%s]\n", s2); // beijing

	user_strcpy(s1, s2);              //函数调用

	printf("拷贝后:s1 = [%s]\n", s1); // beijing
	printf("拷贝后:s2 = [%s]\n", s2); // beijing
	return 0;
}
	int i = 0;
	while(dest[i++]=str[i]);     //while(dest[i++]=src[i++]);相当于i++进行了两次
}                                //while(dest[i]=src[i++]);因为i++自增,相当于把src的第i位赋值给dest的i+1

int main(int argc, const char *argv[])
{
	char s1[20] = "hello world";
	char s2[20] = "beijing";

	printf("拷贝前:s1 = [%s]\n", s1); // hello world
	printf("拷贝前:s2 = [%s]\n", s2); // beijing

	user_strcpy(s1, s2);              //函数调用

	printf("拷贝后:s1 = [%s]\n", s1); // beijing
	printf("拷贝后:s2 = [%s]\n", s2); // beijing
	return 0;

3.3strcat

#include  <string.h>

char *strcat(char *dest, const char *src);

功能:将src拼接到dest后面,会覆盖dest的'\0'   同样要注意保证dest足够大 否则会越界访问

参数:

        dest:目标字符串

        src:源字符串

返回值:dest的首地址

#include <stdio.h>
#include <string.h>

int main(int argc, const char *argv[])
{
	char s1[20] = "hello world";
	char s2[20] = "abcd";

	printf("拼接前:s1 = [%s]\n", s1); // hello world
	printf("拼接前:s2 = [%s]\n", s2); // abcd

	// 把s2拼接到s1后面  要保证s1足够大!!!
	strcat(s1, s2);

	printf("拼接后:s1 = [%s]\n", s1); // hello worldabcd
	printf("拼接后:s2 = [%s]\n", s2); // abcd

	return 0;
}

自定义函数:

#include <stdio.h>
#include <string.h>

void user_strcat(char *dest, char *src){
#if 0
	int i = 0;
	while(dest[i] != '\0'){  //先找到dest的'\0'的下标
		i++;
	}
   //拼接
   int j = 0;
   while(src[j] != '0'){
		dest[i] = src[j];
		i++;
		j++;
   }
	dest[i] = src[j];       //src的'\0'需要追加给dest
}
#else
	int i = 0;
	while(dest[i++]);      //因为i++自增 i的值是'\0'后一位的下标
	i--;                   //i需要减1
	int j = 0;
	while(dest[i++] = src[j++]);
	//先赋值,后循环,当src[i]='\0'时,while结束循环时,src的\0已经赋值给dest 
#endif
}

int main(int argc, const char *argv[])
{
	char s1[20] = "hello world";
	char s2[20] = "abcd";

	printf("拼接前:s1 = [%s]\n", s1); // hello world
	printf("拼接前:s2 = [%s]\n", s2); // abcd

	// 把s2拼接到s1后面  要保证s1足够大!!!
	user_strcat(s1, s2);

	printf("拼接后:s1 = [%s]\n", s1); // hello worldabcd
	printf("拼接后:s2 = [%s]\n", s2); // abcd

	return 0;
}

3.4strcmp

#include  <string.h>

int strcmp(const char *s1, const char *s2);

功能:比较两个字符串,并不是比较长度,而是逐个的比较两个字符串中对应字符的ascii码,直到出现大小关系时,立即返回。只有两个字符串中第一次出现'\0'之前的所有字符都相等 才认为两

字符串相等   

返回值:

                <0 s1<s2

               >0 s1>s2

                0 s1==s2

#include <stdio.h>
#include <string.h>

int main(int argc, const char *argv[])
{
	char s1[10] = "abcdefg";
	char s2[10] = "abcdxfg";

	//需要看返回值 来确定到底哪个大
	int ret = strcmp(s1, s2);
	if(0 == ret){
		printf("s1 == s2\n");
	}else if(ret > 0){
		printf("s1 > s2\n");
	}else if(ret < 0){
		pr
   因为 'x' > 'e'
	}
	return 0;
}

自定义函数:


#include<stdio.h>
//定义一个比较的函数
int user_strcmp(char *arr1, char *arr2){
	int i = 0;
	while(arr1[i] != '\0' && arr2[i] != '\0'){
		if(arr1[i] != arr2[i]){
			break;
		}
		i++;
	}
	return arr1[i] - arr2[i];
}

int main(int argc, const char *argv[])
{
	char arr1[10] = "abcd";
	char arr2[10] = "abcdefg";

	//需要看返回值 来确定到底哪个大
	int ret = user_strcmp(arr1, arr2);
	if(0 == ret){
		printf("arr1 == arr2\n");
	}else if(ret > 0){
		printf("arr1 > arr2\n");
	}else if(ret < 0){
		printf("arr1 < arr2\n"); // 上面例子中 arr2 更大 因为 'x' >
	}
	printf("ret = %d\n", ret);

	return 0;
}	

​

  strcmp常用于判断两个字符是否相等

#include<stdio.h>
#include<string.h>   //strcmp的头文件

int main(int argc, const char *argv[])
{
	char sport[10] = {0};
	printf("请输入是否喜欢运动:");
	scanf("%s",sport);
	//用strcmp返回值来判断两个字符串是否相等
	if(!strcmp(sport, "喜欢")){   //等价于if(0 == strcmp(sport, "喜欢" ))
		printf("用户输入的是喜欢\n");
	}else{
		printf("用户输入的是不喜欢\n");
	}
	return 0;
}

                                              

3.5字符串处理

3.5.1字符串转整型

#include  <stdlib.h>

int atoi(const char *nptr);

功能:将字符串转换成整型

参数:字符串

返回值:转换后的整型

#include <stdio.h>
#include <stdlib.h>

int main(int argc, const char *argv[])
{
	char s[10] = "1234";
	int num = atoi(s);
	printf("num = %d\n", num); // 1234

	return 0;
}

自定义函数:

#include<stdio.h>

int array_to_integer(char *p){
	int i = 0;
	int num = 0;
	while(p[i] != '\0'){
		num *= 10;
		num += p[i] - '0';
		i++;
	}
	return num;
}

int main(int argc, const char *argv[])
{
	int num = 0;
	char arr[10] = "1234";
	num = array_to_integer(arr);
	printf("%d\n",num);     //1234
	return 0;
}
3.5.2字符串翻转

例如输入:hello world beijing

则输出:gnijieb dlrow olleh

#include<stdio.h>

int main(int argc, const char *argv[])
{
	//终端获取字符串
	char arr[100] = {0};
	gets(arr);
	int i = 0;
	int temp = 0;
	//遍历数组有几个字符
	while(arr[i++]);
	i -= 2;//上面循环结束时i是\0后一位的下标
	//字符串翻转
	int j = 0;
	while(j < i){
		temp = arr[j];
		arr[j] = arr[i];
		arr[i] = temp;
		j++;
		i--;
	}
	printf("%s\n",arr);

	return 0;
}
中文字符串翻转

输入一个中文字符串翻转(在linux系统中一个中文占3个字节)

输入“我爱中国”  输出“国中爱我”

#include<stdio.h>

int main(int argc, const char *argv[])
{
	char arr[100] = {0};
	gets(arr);
	//遍历字符转数组,找到最后一个中文的起点
	int i = 0;
	while(arr[i++]);
	i -= 4; //定位到最后一个中文的起点

	char temp = 0;
	int j = 0;
	while(j < i){
		for(int k = 0;k < 3;k++){
			temp = arr[j + k];
			arr[j + k] = arr[i + k];
			arr[i + k] = temp;
		}
		j += 3;
		i -= 3;
	}
	printf("%s",arr);
	return 0;
}
英文字符串翻转

输入"i love china too"    输出"too china love i "

#include<stdio.h>

int main(int argc, const char *argv[])
{
	int i = 0;
	int j = 0;
	int k = 0;
	int temp = 0;
	char arr[100] = {0};
	gets(arr);  //终端获取一个字符串
	//遍历数组有多少个字符
	while(arr[i++]);
	i -= 2;
	//所有的字符翻转
	while(j < i){
		temp = arr[i];
		arr[i] = arr[j];
		arr[j]= temp;
		j++;
		i--;
	}
	//清零
	i = 0;
	j = 0;

	while(1){
		//遍历单词
		while(arr[i] != 32 && arr[i] != 0){ //32是空格的assic码 0是'\0'的assic码
			i++;
		}
        //定义变量k来保存下一个单词第一位的下标
		k = i + 1; //此时i是第一个单词后的空格下标,需+1
		i--; //i-1后 i才是第一个单词的最后一位下标
		while(j < i){
			temp = arr[i];
			arr[i] = arr[j];
			arr[j]= temp;
			j++;
			i--;
		}
		//i和j此时都是下一个单词的第一位字母下标 
		i = k;
		j = k;

		if(!arr[i]){   //等价于 if(arr[i] == '\0')
			break;    
		}
	}
	printf("%s",arr);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值