文章目录
- 怎么来的?
- 内部函数
- 外部函数
- 明确一下内外的概念:
- 外部函数的实例
- fgets()函数
怎么来的?
函数本质上是全局的,因为定义一个函数的目的就是这个函数与其他函数之间相互调用,如果不声明的话,一个函数既可以被本文件中的其他函数调用,也可以被其他文件中的函数调用。但是可以指定某些函数不能被其他函数调用,根据函数能否被其他源文件调用,将函数分为内部函数和外部函数。
内部函数
如果一个函数只能被本文件的其他函数所调用,它被为“内部函数”。在定义内部函数时,加上关键字static,这个关键字可以理解为“我就只停留在这儿了,其他地方我不去”。
static int(int a,int b){}
int(int a,int b){}
外部函数
如果定义外部函数时,在函数首部可以加上关键字extern,这就是外部函数了定义如下:
//这个两个的作用是相同的
extern int(int a,int b){}
int(int a,int b){}
extern这个关键字可以理解为“我可以去其他地方,不限于此处”。其实完全没必要定义,因为C语言规定:如果在定义函数时省略extern,则默认为外部函数。
明确一下内外的概念:
比如上面这个在JuneProject这个文件夹下面有三个C的源文件,分别为main01.c 、main02.c 、main03.c。
对于源文件 main01.c来说,main02.c 、main03.c就是外面;
同理,对于main02.c和main03.c也是这样关系。
首先进入main01.c
//main01.c
#include <stdio.h>
static void swap(int *a,int *b);
void mul();
int main(int argc, char** argv) {
int x,y;
x = 2,y =3;
swap(&x,&y);
printf("x=%d,y=%d",x,y);
return 0;
}
static void swap(int *a,int *b)
{
int temp;
temp = *a;
*a = *b;
*b = temp;
}
void mul()
{
}
.........
main01.c里面有两个函数,一个是main()函数,另一个是swap()函数,还可以添加其他函数。
加上static后,swap()就只能被miain01.c这个文件里面的其他函数调用,swap()就只在miain01.c里面发挥作用,就是main01.的内部函数,而对于main02.c和main03.c来说,它就是外部函数(严谨一些,前提是mian02.c和main03.c里面没有定义swap()函数)。
内部函数:swap()就只停留在main01.c被调用了,其他地方(main02.c,main03.c)我不去。
而不加关键字static,它就会更加自由,就是外部函数了,所以说swap()可以去其他地方(main02.c,main03.c),不限于此处(main01.c),虽然不加extern也是外部函数,但为了更加醒目一些加上extern更规范。接下来就看看怎么这个extern关键字的运用吧!
外部函数的实例
本例用的是dev C++下建立的项目演示的。
只有3main.c这个里面有main()函数。这三个函数的内容分别如下:
//1enter_str.c
void enter_str(char str[80])
{
gets(str);
}
//2select_str.c
//功能:剔除掉不需要的字符的字符串
void select_str(char str[80],char nch)
{
int i,j;
for(i=j=0;str[i]!='\0';i++)//输入的字符大小,以键盘enter作结(相当于添加'\0')
{
if(str[i]!= nch)//nch代表空字符,是不需要的字符。
str[j++] = str[i];//把不需要的字符剔除后保存在str数组中
}
str[j] = '\0';//在这个新的数组后面添上'\0'
}
//3main.c
#include <stdio.h>
//声明函数
extern void enter_str(char str[80]);
extern void select_str(char str[80],char nch);
int main(int argc, char** argv) {
char nc,str[80];
//input
printf("Please enter character:\n");
enter_str(str);//调用enter_str()函数
scanf("%c",&nc);//nc不需要的字符
//output
select_str(str,nc);//调用elect_str()函数
printf("%s\n",str);
return 0;
}
//总结:gets(&arry),scanf("%c",&arry)必须输入的地址,而在传参数的时候和定义的变量的一样。
在3main()主函数所在的这个文件,编译,运行后便是下面的结果。
注解:
extern解读
void enter_str()函数在1enter_str.c文件里面定义的。
void select_str()函数是在2select_str.c文件里面定义的。
extern却是写在调用这些函数的3mian.c文件里面。
写这个extern关键字的时候,不像是内部函数那样写在当前文件里面起作用,而是写在调用它的函数地方,表示的意思这个调用函数把它的作用域扩展到了使用它的地方,即你哪里调用我,这个关键字写上,表示我的作用域就作用在哪里。
输入解读
当gets(str)和scanf(“%c”, &nch)连续使用时,您可以通过在输入中添加换行符(回车键)来结束第一个输入,并开始第二个输入。
例如,假设您首先想要输入一个字符串,然后输入一个字符,您可以按照以下方式输入:
plaintext
请输入一个字符串:Hello, World! [按下回车键结束输入]
请输入一个字符:a [再次按下回车键结束输入]
在上面的示例中,您在输入完字符串后按下回车键,这会将字符串传递给gets()函数并结束第一个输入。然后,您继续输入一个字符,并再次按下回车键,这将字符传递给scanf()函数并完成第二个输入。
优化解读——优化失败
请注意,gets()函数存在安全风险,因为它无法确定输入的字符数量,可能导致溢出。建议使用更安全的替代函数,如fgets()来处理输入。同时,请确保在使用scanf(“%c”, &nch)时清除输入缓冲区,以避免读取上一个输入行尾的换行符。这个例子中怎么用fgets()
下面我们就对这个程序的安全性方面优化,使用fgets()替换gets();
补充知识——来源于C技能树:
fgets()函数
因为gets()函数不会检查存储区是否能够容纳实际输入的数据,多出来的字符简单地溢出到相邻的内存区,所以上面的代码在编译的时候会有warning。fgets()函数和gets()函数的不同:
- 它需要第二个参数来说明最大读入字符数。如果这个参数值为n,fgets()就会读取最多n-1个字符或者读完一个换行符为止(因为会自动添加一个空字符(\n)),由这两者中最先满足的那个结束输入。
- 如果fgets()读取到换行符,就会把它存到字符串里,而不是像gets()那样丢弃。
- 它还需要第三个参数来说明读哪一个文件,从键盘上读取数据时,可以使用stdin(代表standard input)作为参数,这个标识符在stdio.h中定义。
代码如下:(一下和之前的结构一样,都是三个C源程序里面的,只是顺序不同)
#include <stdio.h>
#include <string.h>
extern void enter_str(char str[]);
extern void select_str(char str[], char nch);
int main() {
char nc;
char str[80];
printf("Please enter a sentence:\n");
enter_str(str);
printf("Please enter a character to remove:\n");
scanf(" %c", &nc);
select_str(str, nc);
printf("Result: %s\n", str);
return 0;
}
void select_str(char str[], char nch) {
int i, j;
for (i = j = 0; str[i] != '\0'; i++) {
if (str[i] != nch) {
str[j++] = str[i];
}
}
str[j] = '\0';
}
void enter_str(char str[]) {
fgets(str, sizeof(str), stdin);
}
修改尚未成功,这个第一次输入,按Enter结束后无法进入第二次输入,就直接输出了。有2点问题:(1)无法第二次输入就不能输入剔除字符;(2)长度上也不对,只处理了输入的前8个字符,后面的的字符就没有管了。
————————这里我猜想应该是 sizeof(str)的长度问题。于是修改了长度为800个字节,并且由于fgets()把第一输入读取会将输入缓冲区中的换行符 \n(回车)也存储在字符串中。这可能会导致下一次读取输入时出现问题。所以到scanf时也是\n(回车),没有第二次输入就直接输出了。这个有2个办法:第二次输入scanf之前(1)在清除缓存( fflush(stdin); ),(2) 删除换行符( 😃 str[strcspn(str, “\n”)] = ‘\0’; )
#include <stdio.h>
#include <string.h>
extern void enter_str(char str[800]);
extern void select_str(char str[800], char nch);
int main() {
char nc;
char str[800];
printf("Please enter a sentence:\n");
enter_str(str);
printf("Please enter a character to remove:\n");
fflush(stdin); // 清空输入缓冲区
scanf(" %c", &nc);
select_str(str, nc);
printf("Result: %s\n", str);
return 0;
}
void select_str(char str[800], char nch) {
int i, j;
for (i = j = 0; str[i] != '\0'; i++) {
if (str[i] != nch) {
str[j++] = str[i];
}
}
str[j] = '\0';
}
void enter_str(char str[800]) {
fgets(str, sizeof(str), stdin);
str[strcspn(str, "\n")] = '\0';//删除换行符
}
加了删除换行符,结果没有字符串数组没有一点改变,而且似乎还变小了。
加了清除缓存,结果就结束不了,强行输入‘\0’或者‘\n’就和以前一样了。
两个同时用着,也是结束不了,fget这个函数就很难,会把第一次回车键’\n’存进字符串数组中,一来没法进入第二次输入来输入不需要的字符,用以上的两种方法不行,目前暂无解决,留以后再探究,后面避免遇到fget()和scanf()的组合,get()和scanf可以,但是get的越界问题 。就此作结。