内部函数和外部函数

news2024/10/7 13:19:55

文章目录

  • 怎么来的?
  • 内部函数
  • 外部函数
  • 明确一下内外的概念:
  • 外部函数的实例
    • 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的越界问题 。就此作结。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/705850.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

YouTube正测试屏蔽“广告拦截器”,以确保其广告收入

YouTube目前正在进行一项全球范围内的小规模测试&#xff0c;警告用户关掉他们的广告屏蔽器&#xff0c;否则将被限制观看视频的次数。 周三&#xff08;6月28日&#xff09;&#xff0c;Reddit的一位用户发现&#xff0c;在使用YouTube时弹出了一个窗口&#xff0c;提示该用户…

Cali3F: Calibrated Fast Fair Federated Recommendation System

Decentralized Collaborative Learning Framework for Next POI Recommendation 标定的&#xff08;校准的&#xff09;快速公平联邦推荐系统 1. What does literature study? 提出一个经过校准的快速而公平的联邦推荐框架Cali3F&#xff0c;通过集群内参数共享解决了收敛问…

创新引领未来:RFID技术在汽车装配中的智能革命

射频识别&#xff08;RFID&#xff09;技术作为一种自动识别技术&#xff0c;已经在许多领域得到广泛应用。在汽车装配领域&#xff0c;RFID技术的应用可以提高装配过程的效率、降低人工错误率&#xff0c;并帮助实现自动化和智能化生产。本文将介绍RFID技术在汽车装配中的应用…

动态二维码生成器PHP Dynamic QRcode

什么是 PHP Dynamic QRcode &#xff1f; PHP Dynamic QRcode 是一个允许生成和保存动态和静态二维码&#xff08;QR码&#xff09;的应用。它具有简洁、响应灵敏且用户友好的设计。其中包含您网站中可能需要的一般功能&#xff0c;如&#xff1a;记录管理&#xff08;CRUD&…

【2023,学点儿新Java-27】是的——C语言中的const关键字 | 附:按照类型 快速了解与划分:C语言中的关键字 | goto关键字解释

前情回顾&#xff1a; 【2023&#xff0c;学点儿新Java-26】关键字介绍示例代码&#xff1a;assert 断言&#xff08;如何启用断言&#xff09;&#xff0c;以 验证一个数组的长度是否不为零 为例说明【2023&#xff0c;学点儿新Java-25】如何解决浮点计算存在误差&#xff1a…

Selenium Grid入门详解

目录 前言&#xff1a; 一、简介 二、使用场景 三、使用前提 四、使用方式 五、实现在另一台电脑运行脚本 前言&#xff1a; Selenium Grid是一个用于分布式测试的工具&#xff0c;它允许同时在多个机器上执行Selenium测试。通过使用Selenium Grid&#xff0c;你可以在不…

Linux系统Centos7 安装MySQL8.0详细步骤

MySql安装 1.下载wget命令 yum -y install wget 2. 在线下载mysql安装包 wget https://repo.mysql.com//mysql80-community-release-el7-3.noarch.rpm 3.MySQL的GPG升级了&#xff0c;需要更新&#xff0c;如果是新安装的MySQL&#xff0c;执行以下脚本即可&#xff1…

Vite + Vue3 + Electron实现进程通信

Vite Vue3 Electron实现进程通信 实现 渲染进程 / 主进程 通信&#xff08;IPC&#xff09; Electron 是一个基于 Chromium 和 Node.js 的桌面应用程序开发框架&#xff0c;而 Vue3 则是一种流行的前端框架。将两者结合使用可以快速地打造出跨平台的桌面应用程序。在这种组…

Sui x KuCoin Labs夏季黑客松第三批入围项目公布

自Sui x KuCoin Labs夏季黑客松开放注册以来&#xff0c;已收获了众多开发者的报名参与。赛程过半&#xff0c;截至目前为止&#xff0c;第一批和第二批入围项目已在前两周公布&#xff0c;第三批入围名单项目新鲜出炉&#xff0c;进入最终的Demo Day。 第三批入围名单 SuiVi…

在 Linux 中查找 IP 地址的 3 种简单方法

在 Linux 系统中&#xff0c;经常需要查找 IP 地址以进行网络配置、故障排除或安全管理。无论是查找本地主机的 IP 地址还是查找其他设备的 IP 地址&#xff0c;本文将介绍三种简单的方法&#xff0c;帮助你在 Linux 中轻松找到所需的 IP 地址。 总结 通过上述三种简单的方法&…

自动化测试进阶之路(WEB自动化二)

一、设计模式 Pom模块关键字驱动模式 pom模块&#xff1a;page object model 页面对象模式好处&#xff1a; 解决线性脚本的问题解决代码不能重复利用的问题后期的维护问题 分三层&#xff08;页面对象层调用基础层的方法&#xff0c;测试用例层调用页面对象层的方法&#xff0…

Spring Boot 有哪些方式可以实现热部署

一、什么是热部署&#xff1f; 释义 所谓热部署&#xff0c;就是在应用正在运行的时候升级软件&#xff0c;却不需要重新启动应用。 对于Java应用程序来说&#xff0c;热部署就是在运行时更新Java类文件。在基于Java的应用服务器实现热部署的过程中&#xff0c;类装入器扮演着…

EasyDSS视频直播点播平台视频回看列表显示为ID的排查与优化

视频直播点播EasyDSS平台具备灵活的视频能力&#xff0c;包括直播、点播、转码、管理、录像、检索、时移回看等&#xff0c;平台支持音视频采集、视频推拉流、播放H.265编码视频、存储、分发等能力服务&#xff0c;可应用在无人机推流、在线直播、虚拟直播、远程培训等场景中。…

String面试

以下讨论以jdk8为标准&#xff1a; String Pool&#xff1a;字符串常量池 存储字面量位于堆中&#xff0c;不在元空间intern()方法会去常量池找&#xff0c;没有的话就创建一个&#xff0c;返回常量池中的地址&#xff1b;有的话就直接返回对象地址 new String(“”)方法强制创…

3.Hive SQL数据定义语言(DDL)

1. 数据定义语言概述 1.1 常见的开发方式 &#xff08;1&#xff09; Hive CLI、Beeline CLI Hive自带的命令行客户端 优点&#xff1a;不需要额外安装 缺点&#xff1a;编写SQL环境恶劣&#xff0c;无有效提示&#xff0c;无语法高亮&#xff0c;误操作率高 &#xff08;2&…

IMX6ULL系统移植篇-uboot的配置与编译

一. 编译 uboot简介 开发板的资源 &#xff1a;256MB(DDR3) 512MB(NAND)核心板 这里使用的是阿尔法开发板&#xff0c;256MB的 DDR&#xff0c;核心板是 512MB的 Nand-Flash。 二. 编译 uboot 创建 /uboot/alientek_uboot 目录文件&#xff0c;将正点原子提供的 uboot源码包…

chatgpt赋能python:如何将Python图形居中

如何将Python图形居中 介绍 在Python编程中&#xff0c;绘图是非常重要的环节。无论是作为数据可视化的一部分&#xff0c;还是作为界面设计的基础&#xff0c;具有图形界面的Python应用程序都需要在屏幕上将图形元素居中。然而&#xff0c;很多新手在实现图形居中时往往会遇…

常州工学院计算机组成原理2022-2023第一学期补考

一、简答题&#xff08;共30分,每题6分&#xff09; 1、以将指令地址码指示的存储单元中的操作数取出后送至运算器的ACC中为例&#xff0c;完成一条指令的信息流程。 2、什么是RISC&#xff1f;请简述它的主要特点。比较RISC和CISC的异同之处。 RISC全称Reduced Instruction …

CentOS 执行yum命令报错【错误:为仓库 ‘appstream‘ 下载元数据失败: ...】

文章目录 一、遇到问题二、原因分析三、解决问题方案一&#xff1a;更新centos提供的yum源新地址1. yum源仓库配置文件内容2. 更新yum源新地址3. 测试yum安装命令 方案二&#xff1a;更新为 国内的yum源1. 使用 阿里的yum源2. 使用 网易的yum源3. 使用 清华的yum源 四、总结 一…

75、基于51单片机交通灯+人行道+左转系统设计(程序+原理图+Proteus仿真+参考论文+开题报告+任务书+元器件清单等)

摘 要 近年来随着科技的飞速发展&#xff0c;单片机的应用正在不断深入&#xff0c;同时带动传统控制检测技术日益更新。在实时检测和自动控制的单片机应用系统中&#xff0c;单片机往往作为一个核心部件来使用&#xff0c;仅单片机方面知识是不够的&#xff0c;还应根据具…