C基础(三)函数的使用

一、库函数的使用

1.1 随机数rand与srand

对应的头文件是stdlib.h,Rand是伪随机数产生器,每次调用rand产生的随机数是一样的,如果调用rand之前先调用srand就出现任意的随机数。

只要能保证每次调用srand函数的时候,参数的值是不同的,那么rand函数就一定会产生不同的随机数, 通常会传一个时间作为srand的参数. 这样rand的值就是随机不重复的。

由于生成的随机数的范围是不定的, 那么通常会采用取摸的方式来规定范围,例如 生产0-100之间的随机数,那么可以和101取余。

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

int main()
{

	int t = (int)time(NULL); //获取距1970的系统时间,单位s ,需要导入time.h头文件
	printf("t=%d\n",t);

	srand(t);//先调用srand

	int i;
	for(i=0;i<10;i++)
	{
		int r=rand(); //再调用这个生产随机数
		printf("r=%d,sr=%d\n",r,r%11);
	}

	return 0;
}

结果:
在这里插入图片描述

1.2 scanf函数

通过scanf("%s",a); 可以把用户输入的字符串写到char数组中, 由于a是数组名, 已经表示的是地址了,不用在&a多次一举。

scanf并不会直接读入用户最后输入完成的那个回车键,而是会在最后自动补一个0,这样才是完整的字符串. 用户按空格或回车键都表示输入完成.

#include<stdio.h>
int main()
{
	char a[100] = {1,2,3,4,5,6,7,8,9,10};
	//接收用户输入
	scanf("%s",a);

	int i;
	for(i=0;i<10;i++)
	{
		printf("%d\n",a[i]);
	}
	return 0;
}

(1)当输入abc回车结果:
在这里插入图片描述
说明abc字符串的ASCII= 97 98 99 0 , 末尾的0就是’\0’

(2)当输入ab c回车结果:
在这里插入图片描述
可以看到空格和c被忽略了, 因为scanf遇到空格和遇到回车是一样的效果,表示输入完成。

scanf缓冲区溢出的危险
scanf并不检查参数的char数组的下标,用户输入多少,scanf就往数组里面放入多少,一旦用户输入的过多,会导致内存错误。

1.3 gets函数

用来接收用户输入的字符串的, 参数是字符数组, 仅认回车符表示输入完成,同样和scanf一样存在安全问题(用户输入过多内容会报缓冲区溢出的问题).

#include<stdio.h>

int main(){

    char a[10] ={0};
    gets(a);
    for(int i=0;i<sizeof(a);i++){
        printf("%d\n",a[i]);
    }
    return 0;
}

结果如下:
在这里插入图片描述

1.4 fgets函数

fgets函数就是解决gets函数的安全缺陷问题的. 但是由于fgets是为读取文件而设计的,所以读取键盘输入时没有gets函数方便 , fgets函数会把用户的回车键也当做字符接收了。
它接收3个参数:
参数一:char数组
参数二: char数组的大小
参数三: 如果是通过键盘输入, 那么可以固定写为 stdin

#include<stdio.h>
int main()
{
	char a[10] ={0};
     // 注意:参数2要填写正确的数组大小, 如果故意填写过大, 也会造成缓冲区溢出的问题
	fgets(a,sizeof(a),stdin);
	printf("%s",a);
}

结果如下:
在这里插入图片描述
虽然用户输入了超过10个字符的长度, 但是实际接收的是9个字符,因为第10元素用来放’\0’了.

1.5 puts函数

用于打印字符串,与printf不同,puts会在输出结果最后自动加一个’\n’,无论要输出的字符串末尾是否已有’\n’了, 而且不支持各种转义字符,比如%d,%s都是不支持的,puts只能简单的直接输出一个字符串,而不能输出char,int,double等其他类型。

#include<stdio.h>
int main()
{
	char a[] ="hello";
	puts(a); //字符串才可以输出

	char b[] = {0,1,2,3};
	puts(b); //char数组输出不了效果

	return 0;
}

结果:
在这里插入图片描述

1.6 strlen函数

用于求字符串所占的字节长度,使用时需要导入头文件<string.h> , 得到的长度是不包含字符串末尾’\0’所占的长度。
注意:英文和数字是占1BYTE的长度, 而中文在utf-8是占3BYTE的长度。

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

int main()
{
	char a[] ="hello世界";
	/*
	unsigned int len =0;
	while(a[len])
	{
		len++;
	}
	*/

	int len = strlen(a); //效果和上面的一样,都是11,5个英文占5Byte,2个中文占6Byte

	printf("%u\n",len);
}

练习-给定一个字符串,求中文出现的次数
思路: 汉字的首个字节的ASCII值是负数, 只需要统计下有多少个首字母是负数的即可。

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

int main()
{
	char a[] = "abc我ef哈哈12呵呵";

	int len = strlen(a);

	int i;
	int count=0;//统计汉字个数

 	//方式一:
	for(i=0;i<len;i++)
	{
		char c = a[i];
		if(c < 0)
		{
			count++;
			i+=2;//下标+2,因为utf-8下汉字占3个字节,因此后面2个还是汉字的字节,可以跳过
		}
	}
	printf("count1=%d\n",count); // 5
	
	//方式二:
	count=0;
	for(i=0;i<len;i++)
	{
		char c = a[i];
		if(c<0)
		{
			count++;
		}
	}
	count/=3;//由于汉字占3个字节,所以每3个字节位一个汉字

	printf("count2=%d\n",count); // 5
	
	return 0;

	
}

1.7 strcat函数和strncat函数

字符串追加,接收2个字符串作为参数
例如传入char1 和char2,则char2会追加到char1的末尾. 使用这个函数需要注意第一个字符串需要有足够的空间存放第二个字符串,使用时也需要导入头文件<string.h>。
而strncat在strcat函数的基础上增加了一个参数,用来表示最多可以追加多少个char.

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

int main()
{
	char a[20] = "abc";
	char b[] = "hello world";

	strcat(a,b); //把字符串b追加到字符串a中
	printf("a1=%s\n",a); // a1=abchello world


	strncat(a,b,5);//把字符串b追加到字符串a中,同时只追加5个字符

	printf("a2=%s\n",a);// a2=abchello worldhello

	return 0;
}

1.8 strcmp和strncmp

strcmp:比较2个字符串是否相等,相等返回0,不等返回非0
strncmp:比较2个字符串的前几个字符是否相同,相等返回0,不等返回非0

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

int main()
{
	char a[] = "hello";
	char b[] = "hello";

	if(strcmp(a,b)==0)  //0表示相同,非0表示不同
	{
		puts("相同");
	}else{
		puts("不相同");
	}



	if(strncmp(a,b,3)==0)
	{
		puts("相同");
	}else{
		puts("不相同");
	}

	return 0;
}

1.9 strcpy和strncpy

strcpy:拷贝字符串,将字符串2拷贝到字符串1中, 会存在内存溢出的问题.
strncpy:拷贝有限个数的字符串, 通常这样使用strncpy(a,b,sizeof(a)-1);表示将字符串b拷贝到字符串a中, 最大可以拷贝的字节长度是a字符串的字节长度-1 , -1是因为保留字符串末尾的’\0’标识。

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

int main()
{
	char a[]="hello";
	char b[100]="abcd1234";
	
//	strcpy(a,b);// 这样会有安全问题,超出了a字符串的字节长度
//	printf("a1=%s\n",a);


	strncpy(a,b,sizeof(a)-1); //a字符串的长度其实是6个BYTE(包含了'\0'标识)
	printf("a2=%s\n",a); //a2=abcd1
	return 0;
}

1.10 sprintf函数

int sprintf(char *str,const char *format,...);
  • 参数:
    str: 字符串首地址
    format: 字符串格式,用法和printf()一样
    参数3: format对应的数据
  • 返回值:
    成功:实际格式化的字符个数
    失败:-1

用于向一个char的数组输出一个字符串。和printf的使用基本一致,区别是多了第一个参数,第一个参数是一个char数组,所有printf的转义符对于sprintf来说是一样的。

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

int main()
{
	char a[100];
	int i=100;
	//将结果输出到a数组中
	sprintf(a,"%s%d","hello",i);

	printf("%s\n",a); // hello100
	return 0;
}

[使用技巧1] 可以利用sprintf拼接多个类型的数据

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

int main()
{
    char buf[1024] = {0};
    char *str1 = "hello";
    char *str2 = "world";
    int a = 100;
    sprintf(buf, "%s%s%d", str1, str2, a); // helloworld100
    printf("%s", buf);
    return 0;
}

[使用技巧2] 将数字转字符串

char buf[1024] = {0};
int a = 10;
sprintf(buf,"%d",a);

1.11 sscanf函数

int sscanf(const char *str,const char *fromat,...);
  • 参数:
    str:指定的字符串首地址
    format:字符串格式,用法和scanf()一样
    参数3:输出的目标,是一个可变参数
  • 返回值:
    成功:实际读取的字符个数
    失败:-1

类似与scanf,scanf是从键盘读取用户输入,而sscanf是从指定字符串读取输入数据。它多了第一个参数,char的数组,sscanf会从这个char数据中读取相关内容。

#include<stdio.h>

int main()
{
	char a[100] = "56+57";
	int i,j;
	sscanf(a,"%d+%d",&i,&j); //用法和scanf差不多,只是多传了一个char数组

	printf("%d+%d=%d\n",i,j,i+j); // 56+57=113
	return 0;
}

常用的格式化符号
在这里插入图片描述
其中%*表示忽略的意思,例如%*d表示忽略数字, %*s表示忽略字符串
1.%*s忽略字符串的时候需要有分割符号,例如空格或者\t, 否则会将整个字符串都忽略了
2.%[a-z]是从字符串开头位置开始匹配,如果开都是数字,那么直接匹配失败,通常结合%*d使用,也就是%*d%[a-z],这样可以忽略开头的数字
3.%[aBc]也是从字符串开头位置开始匹配,如果第一个都不符合,那么直接匹配失败.
4.%[^a]也是从字符串开头位置开始匹配,直到出现了a字符为止,a字符之后的不会参与匹配.
5.%[^a-z]也是从字符串开头位置开始匹配,直到出现了a-z中的任意字符位为止.
上面说说的a-z字符是任意字符,可以是数字,例如0-9;

练习-求获取"abcde#12uiop@plju"字符串中的"12uiop"子串.

int main()
{
    char *str = "abcde#12uiop@plju";
    char buf[1024] = {0};
    //先忽略#号前的字符,然后#照写,然后匹配非@的字符
    sscanf(str, "%*[^#]#%[^@]", buf); 
    printf("buf=%s\n", buf);
    return 0;
}

练习-求给定一个字符串表达式,求其结果并追加到原字符串末尾
例如"56*57="的字符串。

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

int main()
{
	char a[100] = "56*57=";
	int i,j; // 存储2个操作数
	char c; // 存储操作符号
 
     // 从字符串中取数据
	sscanf(a,"%d%c%d",&i,&c,&j);
	
	int res =0;
	if(c=='+')
	{
		res = i+j;
	}else if(c=='-')
	{
		res = i-j;
	}else if(c=='*')
	{
		res = i*j;
	}else if(c=='/')
	{
		res = i/j;
	}

	printf("%d%c%d=%d\n",i,c,j,res);
	return 0;
}

1.12 strchr和strstr函数

这2个函数都是用于在特定字符串中查找子串, 区别是strchr只能按字符查找, 而strstr可以按字符串查找, 注意如果找到特定的字符或者字符串,那么它会返回这个字符或者字符串作为起点到被查找字符串的末尾的字符串。 相当于java中的substring的功能, 截取子串。

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

int main()
{
	char a[] = "hello world";

	char *s; //定义一个char类型的指针变量
	s = strchr(a,'l');
	if(s !=NULL)
	{
		printf("s1=%s\n",s); // s1=llo world
	}

	s = strstr(a,"wo");
	if(s !=NULL)
	{
		printf("s2=%s\n",s); // s2=world
	}

	return 0;
}

1.13 strtok函数

字符在第一次调用时strtok必须给予参数s字符串,往后的调用则将参数s设置成NULL,每次调用成功则返回指向被分割出片段的指针, 类似于java中的String的split方法

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

int main1()
{
	char a[]="abc_123_hello_world";
	char *s;//定义一个char的指针变量,指针的概念将会在后续介绍
    s = strtok(a,"_");
	printf("%s\n",s); // abc

	
    s = strtok(NULL,"_"); //后面的调用必须传NULL
	printf("%s\n",s); // 213

    s = strtok(NULL,"_");
	printf("%s\n",s); // hello


	return 0;

}

//上面的截取方式有点麻烦,通常会采用下面这种方式
int main()
{
	char a[] = "abc_213_hello_world";
	char *s;
	s = strtok(a,"_");
	while(s) //如果s为NULL,就是0 , 0就是false,退出循环
	{
		printf("%s\n",s);// 依此输出:abc、213、hello、world
		s = strtok(NULL,"_");
	}
	return 0;
}

1.14 atoi和atof,atoll函数

可以将字符串转成int,long ,long long类型

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

int main()
{
	char a[] = "36";
	char b[] = "37";
	//字符串转int
	int sum = atoi(a)+atoi(b);
	printf("sum=%d\n",sum); //sum=73

	//字符串转float或double
	char c[] = "3.5";
	double f = atof(c);
	printf("f=%f\n",f);// f=3.500000
	return 0;
}

int类型转字符串类型

c语言中没有提供int转string的,那如何要实现将int添加到字符串的功能?
只能使用sprintf+strcat来完成

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

int main()
{
	char str1[100] = "abc";
	char str2[10]="helloworld";
	int num = 45;
	sprintf(str2,"%d",num); //将45添加到str2字符串中,会覆盖str2的原内容,所以str2=45
	strcat(str1,str2);//将str2追加到str1的末尾,所以str1=abc45

	printf("str1=%s,str2=%s\n",str1,str2);// str1=abc45,str2=45
	return 0;
}

练习-求字符串中的表达式的值,同时将结果写到等号后面

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

int main()
{
     //表达式
	char a[100]= "12+5=;45-2=;34*2=;56/3=";
	char b[100];
	//先截取
	char *str;
	str = strtok(a,";");

	while(str)
	{
		int i,j;
		char c;
		//从字符串中读取数据
		sscanf(str,"%d%c%d=",&i,&c,&j);
		
		//计算结果
		int res =0;
		switch(c)
		{
			case '+':
				res = i+j;
				break;

			case '-':
				res = i-j;
				break;
			case '*':
				res = i*j;
				break;
			case '/':
				res = i/j;
				break;

		}
		char temp[10]; //结果放到一个char数组中
		sprintf(temp,"%s%d;",str,res);
		
		//追加到b数组
		strcat(b,temp);
		// 继续取出下一节内容
		str = strtok(NULL,";");
	}
	//将b copy 到a
	strncpy(a,b,sizeof(a)-1);
	printf("%s\n",a);
	return 0;
}

1.15 strchr和strrchr函数

如果需要对字符串中的单个字符进行查找,那么应该使用 strchr 或 strrchr 函数。
其中,strchr 函数原型的一般格式如下:

char *strchr(const char *s, int c);

它表示在字符串 s 中查找字符 c,返回字符 c 第一次在字符串 s 中出现的位置,如果未找到字符 c,则返回 NULL。也就是说,strchr 函数在字符串 s 中从前到后(或者称为从左到右)查找字符 c,找到字符 c 第一次出现的位置就返回,返回值指向这个位置,如果找不到字符 c 就返回 NULL。

相对于 strchr 函数,strrchr 函数原型的一般格式如下:

char *strrchr(const char *s, int c);

与 strchr 函数一样,它同样表示在字符串 s 中查找字符 c,返回字符 c 第一次在字符串 s 中出现的位置,如果未找到字符 c,则返回 NULL。但两者唯一不同的是,strrchr 函数在字符串 s 中是从后到前(或者称为从右向左)查找字符 c,找到字符 c 第一次出现的位置就返回,返回值指向这个位置。下面的示例代码演示了两者之间的区别:

int main(void)
{
    char str[] = "I welcome any ideas from readers, of course.";
    char *lc = strchr(str, 'o');
    printf("strchr: %s\n", lc);
    char *rc = strrchr(str, 'o');
    printf("strrchr: %s\n", rc);
    return 0;
}

对于上面的示例代码,strchr 函数是按照从前到后的顺序进行查找,所以得到的结果为“ome any ideas from readers,of course.”; 而 strrchr 函数则相反,它按照从后到前的顺序进行查找,所以得到的结果为“ourse.”。

示例代码运行结果为:

strchr: ome any ideas from readers, of course.
strrchr: ourse.

最后还需要注意的是,为什么函数的“c”参数是 int 类型,而不是“char”类型呢?
其实原因很简单,这里用的是字符的 ASCII 码(因为每个字符都对应着一个 ASCII 码),这样在传值的时候既可以传“char”类型的值,又可以传“int”类型的值(0~127)。

二、自定义函数

c语言规定在main函数之后定义的函数,必须先声明才能使用,否则需要将函数定义在main函数之前。

#include<stdio.h>

// 1. 定义一个带参数和返回值的函数
int add(int a,int b)
{
	return a+b;
}

// 2. 定义一个无返回值和参数的函数
void print()
{
	printf("hello\n");
}

// 3. 声明一个函数, c语言规定在main函数之后定义的函数,必须先声明才能使用
void test();

int main()
{
	int sum = add(1,2); //调用函数1
	printf("sum=%d\n",sum); //sum=3

	print();//调用函数2
	
	test();//调用函数3
	return 0;
}

// 3.在main函数之后定义的函数
void test()
{
	printf("test run....\n");
}

2.1 函数的形参与实参

在调用函数的时候,函数大多数都有参数,主调函数和被调用函数之间需要传递数据。
在定义函数时函数名后面括弧中的变量名称为“形式参数”,简称形参。
在调用函数时,函数名后面括号中的变量或表达式称为“实际参数”,简称实参。

1、形参在未出现函数调用时,他们并不占用内存单元,只有在发生函数调用的时候形参才被分配内存,函数调用完成后,形参所占的内存会被释放;
2、实参可以是变量,常量或者表达式;
3、在定义函数时,一定要指定形参的数据类型;
4、形参与实参的数据类型一定要可兼容;
5、在C语言中,实参与形参的数据传递是“值传递”,即单向传递,只由实参传递给形参,而不能由形参传递给实参;
6、如果函数的参数是个数组,那么是可以通过形参修改实参的值的,因为数组名是一个内存地址。

2.2函数的返回类型与返回值

1、函数的返回值通过函数中的return获得,如果函数的返回值为void可以不需要return语句;
2、函数return语句中的返回值数据类型应该与函数定义时相同;
3、如果函数中没有return语句,那么函数将返回一个不确定的值。

2.3 main函数与exit函数和return语句的区别

exit是C语言的库函数,需要导入stblib.h头文件,调用exit的结果就是程序终止,在子函数中调用exit同样代表程序终止,但在子函数中调用return只是子函数终止,程序正常执行。
在main函数中调用exit与调用return是一样的。

练习-实现字母大小写互换
思路:小写字母和大写字母的ASCII相差32, 32刚好就是一个空格, 所以就是±空格的方式来实现。

#include<stdio.h>

char conver(char c)
{
	if(c>='a' && c<='z')
		return c - ' ';
	if(c>='A' && c<='Z')
		return c + ' ';
}

int main()
{
	char a = conver('a');
	printf("%c\n",a); // A
	return 0;
}

练习-实现数字字符串转数字,也就是atoi的功能
思路:遍历字符串的每个字符,分别将其转成数字(字符数字和’0’字符相减就是数字了),注意单位(个,十,百…位),然后再把结果相加即可。

#include<stdio.h>
//获取字符串长度
int my_strlen(char *c)
{
	int len = 0 ; 
	while ( c[len] )
	{
		len++;
	}
	return len;
	
}
 //得到10的n次方
int mypow10(int n)
{
	if(n ==0 )
		return 1;
	if(n ==1)
		return 10;

	int i;
	int base = 10;
	for(i=1;i<n;i++)
	{
		base *=10;
	}
	return base;
}

//字符串数字转数字
int my_atoi(char *c)
{
	int i;
	int size = my_strlen(c);
	int sum = 0;
	for(i =0;i<size;i++)
	{
		int ic = c[i] - '0'; //字符转数字
		int unit =  mypow10(size-i-1); //计算单位
		printf("unit=%d\n",unit);
		sum += ic *unit;//求和
	}
	return sum;
}

int main()
{

	char b[] ="1234";
	printf("%d\n",my_atoi(b));
	return 0;
}

结果如下:

unit=1000
unit=100
unit=10
unit=1
1234

2.4 函数的递归

函数的递归分为前置递归和后置递归 , 前置递归是指把结果放在递归函数之前计算, 后置递归表示把结果放到递归函数之后计算, 他们的调用流程刚好是一个正序和反序的过程。

例如求10进制转2进制就可以采用后置递归的方式, 不断的%2求余数输出即可。

#include<stdio.h>

void printBinary(int num)
{
  unsigned int i = num % 2;
	if(num>2)
	{
		printBinary(num/2);
	}

	printf("%d",i);

}


int main()
{
	printBinary(13); // 1101
	printf("\n");
	return 0;
}

求10进制转16进制

#include<stdio.h>

void printHex(int n)
{
	int i = n % 16;
	if(n > 16)
		printHex(n/16);
	
	switch(i){
		case 15:
			printf("%c",'F');
			break;
		case 14:
			printf("%c",'E');
			break;
		case 13:
			printf("%c",'D');
			break;
		case 12:
			printf("%c",'C');
			break;
		case 11:
			printf("%c",'B');
			break;
		case 10:
			printf("%c",'A');
			break;
		default :
			printf("%d",i);
			break;
	
		}
}

int main()
{
	printHex(810); // 32A
	printf("\n");
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值