内部函数和外部函数

文章目录

  • 怎么来的?
  • 内部函数
  • 外部函数
  • 明确一下内外的概念:
  • 外部函数的实例
    • 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,则默认为外部函数。

明确一下内外的概念:

内部函数和外部函数_第1张图片
比如上面这个在JuneProject这个文件夹下面有三个C的源文件,分别为main01.c 、main02.c 、main03.c。
对于源文件 main01.c来说,main02.c 、main03.c就是外面;
同理,对于main02.c和main03.c也是这样关系。

首先进入main01.c

//main01.c
#include 
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()函数)。

内部函数和外部函数_第2张图片
内部函数: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 
//声明函数 
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()主函数所在的这个文件,编译,运行后便是下面的结果。
内部函数和外部函数_第3张图片
注解:
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 
#include 

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个字符,后面的的字符就没有管了。
内部函数和外部函数_第4张图片

————————这里我猜想应该是 sizeof(str)的长度问题。于是修改了长度为800个字节,并且由于fgets()把第一输入读取会将输入缓冲区中的换行符 \n(回车)也存储在字符串中。这可能会导致下一次读取输入时出现问题。所以到scanf时也是\n(回车),没有第二次输入就直接输出了。这个有2个办法:第二次输入scanf之前(1)在清除缓存( fflush(stdin); ),(2) 删除换行符( str[strcspn(str, “\n”)] = ‘\0’; )

#include 
#include 

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';//删除换行符
}

加了删除换行符,结果没有字符串数组没有一点改变,而且似乎还变小了。
内部函数和外部函数_第5张图片
加了清除缓存,结果就结束不了,强行输入‘\0’或者‘\n’就和以前一样了。
内部函数和外部函数_第6张图片
两个同时用着,也是结束不了,fget这个函数就很难,会把第一次回车键’\n’存进字符串数组中,一来没法进入第二次输入来输入不需要的字符,用以上的两种方法不行,目前暂无解决,留以后再探究,后面避免遇到fget()和scanf()的组合,get()和scanf可以,但是get的越界问题 。就此作结。

你可能感兴趣的:(#,C,java,算法,数据结构)