网易首页 > 网易号 > 正文 申请入驻

如何在 C 语言中安全地读取用户输入 | Linux 中国

0
分享至

导读:getline() 提供了一种更灵活的方法,可以在不破坏系统的情况下将用户数据读入程序。

本文字数:4087,阅读时长大约: 5分钟

getline() 提供了一种更灵活的方法,可以在不破坏系统的情况下将用户数据读入程序。

在 C 语言中读取字符串是一件非常危险的事情。当读取用户输入时,程序员可能会尝试使用 C 标准库中的gets函数。它的用法非常简单:

  1. char *gets(char *string);

gets()从标准输入读取数据,然后将结果存储在一个字符串变量中。它会返回一个指向字符串的指针,如果没有读取到内容,返回NULL值。

举一个简单的例子,我们可能会问用户一个问题,然后将结果读入字符串中:

  1. #include

  2. #include

  3. int main()

  4. {

  5. char city[10]; // 例如 "Chicago"

  6. // 这种方法很糟糕 .. 不要使用 gets

  7. puts("Where do you live?");

  8. gets(city);

  9. printf("<%s> is length %ld\n", city, strlen(city));

  10. return 0;

  11. }

输入一个相对较短的值就可以:

  1. Where do you live?

  2. Chicago

  3. is length 7

然而,gets()函数非常简单,它会天真地读取数据,直到它认为用户完成为止。但是它不会检查字符串是否足够容纳用户的输入。输入一个非常长的值会导致gets()存储的数据超出字符串变量长度,从而导致覆盖其他部分内存。

  1. Where do you live?

  2. Llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch

  3. is length 58

  4. Segmentation fault (core dumped)

最好的情况是,覆盖部分只会破坏程序。最坏的情况是,这会引入一个严重的安全漏洞,恶意用户可以通过你的程序将任意数据插入计算机的内存中。

这就是为什么在程序中使用gets()函数是危险的。使用gets(),你无法控制程序尝试从用户读取多少数据,这通常会导致缓冲区溢出。

安全的方法

fgets()函数历来是安全读取字符串的推荐方法。此版本的gets()提供了一个安全检查,通过仅读取作为函数参数传递的特定数量的字符:

  1. char *fgets(char *string, int size, FILE *stream);

fgets()函数会从文件指针读取数据,然后将数据存储到字符串变量中,但最多只能达到size指定的长度。我们可以更新示例程序来测试这一点,使用fgets()而不是gets()

  1. #include

  2. #include

  3. int main()

  4. {

  5. char city[10]; // 例如 "Chicago"

  6. puts("Where do you live?");

  7. // fgets 虽好但是并不完美

  8. fgets(city, 10, stdin);

  9. printf("<%s> is length %ld\n", city, strlen(city));

  10. return 0;

  11. }

如果编译运行,你可以在提示符后输入任意长的城市名称。但是,程序只会读取size= 10 数据存储到字符串变量中。因为 C 语言在字符串末尾会添加一个空(\0)字符,这意味着fgets()只会读取 9 个字符到字符串中。

  1. Where do you live?

  2. Minneapolis

  3. is length 9

虽然这肯定比fgets()读取用户输入更安全,但代价是如果用户输入过长,它会“切断”用户输入。

新的安全方法

更灵活的解决方案是,如果用户输入的数据比变量可能容纳的数据多,则允许字符串读取函数为字符串分配更多内存。根据需要调整字符串变量大小,确保程序始终有足够的空间来存储用户输入。

getline()函数正是这样。它从输入流读取输入,例如键盘或文件,然后将数据存储在字符串变量中。但与fgets()gets()不同,getline()使用realloc()调整字符串大小,确保有足够的内存来存储完整输入。

  1. ssize_t getline(char **pstring, size_t *size, FILE *stream);

getline()实际上是一个名为getdelim()的类似函数的装饰器,它会读取数据一直到特殊分隔符停止。本例中,getline()使用换行符(\n)作为分隔符,因为当从键盘或文件读取用户输入时,数据行由换行符分隔。

结果证明这是一种更安全的方法读取任意数据,一次一行。要使用getline(),首先定义一个字符串指针并将其设置为NULL,表示还没有预留内存,再定义一个size_t类型的“字符串大小” 的变量,并给它一个零值。当你调用getline()时,你需要传入字符串和字符串大小变量的指针,以及从何处读取数据。对于示例程序,我们可以从标准输入中读取:

  1. #include

  2. #include

  3. #include

  4. int main()

  5. {

  6. char *string = NULL;

  7. size_t size = 0;

  8. ssize_t chars_read;

  9. // 使用 getline 读取长字符串

  10. puts("Enter a really long string:");

  11. chars_read = getline(&string, &size, stdin);

  12. printf("getline returned %ld\n", chars_read);

  13. // 检查错误

  14. if (chars_read < 0) {

  15. puts("couldn't read the input");

  16. free(string);

  17. return 1;

  18. }

  19. // 打印字符串

  20. printf("<%s> is length %ld\n", string, strlen(string));

  21. // 释放字符串使用的内存

  22. free(string);

  23. return 0;

  24. }

使用getline()读取数据时,它将根据需要自动为字符串变量重新分配内存。当函数读取一行的所有数据时,它通过指针更新字符串的大小,并返回读取的字符数,包括分隔符。

  1. Enter a really long string:

  2. Supercalifragilisticexpialidocious

  3. getline returned 35

  4. > is length 35

注意,字符串包含分隔符。对于getline(),分隔符是换行符,这就是为什么输出中有换行符的原因。如果你不想在字符串值中使用分隔符,可以使用另一个函数将字符串中的分隔符更改为空字符。

通过getline(),程序员可以安全地避免 C 编程的一个常见陷阱:你永远无法知道用户可能会输入哪些数据。这就是为什么使用gets()不安全,而fgets()又太笨拙的原因。相反,getline()提供了一种更灵活的方法,可以在不破坏系统的情况下将用户数据读入程序。

(题图:MJ/4b23132f-8916-42ae-b2da-06fd2812bea8)

via:

作者: 选题: 译者: 校对:

本文由 原创编译, 荣誉推出

LCTT 译者 :MjSeven

翻译: 177.0 篇

贡献: 1993 天

2018-01-30

2023-07-16

https://linux.cn/lctt/MjSeven

欢迎遵照 CC-BY-SA 协议规定转载,

如需转载,请在文章下留言 “ 转载:公众号名称”,

我们将为您添加白名单,授权“ 转载文章时可以修改”。

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
底层打工人的共鸣反击,满屏幕都是“打得好”,问题还是出现了!

底层打工人的共鸣反击,满屏幕都是“打得好”,问题还是出现了!

走读新生
2024-06-21 00:40:40
总决赛拿1000分太难了!库里差72分,科比35分,詹姆斯差几分第一

总决赛拿1000分太难了!库里差72分,科比35分,詹姆斯差几分第一

刺头体育
2024-06-20 21:35:41
广汽本田裁员名额被抢空,部分员工早已计划跳槽到造车新势力

广汽本田裁员名额被抢空,部分员工早已计划跳槽到造车新势力

界面新闻
2024-06-19 16:37:15
外媒:美国违反合同,要将瑞士订购的“爱国者”导弹移交乌克兰

外媒:美国违反合同,要将瑞士订购的“爱国者”导弹移交乌克兰

环球时报国际
2024-06-20 15:32:15
对中国毫无认同感!38万苗族人挤进美国,背叛与鲜血酿出“恶果”

对中国毫无认同感!38万苗族人挤进美国,背叛与鲜血酿出“恶果”

青栀伊人
2024-06-17 22:11:27
蓝白军团真团结!进球后不庆祝,查看麦卡伤势,拼出美洲杯首球

蓝白军团真团结!进球后不庆祝,查看麦卡伤势,拼出美洲杯首球

奥拜尔
2024-06-21 09:41:29
这届618,抖音颓了,天猫笑了

这届618,抖音颓了,天猫笑了

钛媒体APP
2024-06-20 18:58:32
江西男子被“托梦”告知地下有宝藏,深挖7米后,被眼前一幕震撼

江西男子被“托梦”告知地下有宝藏,深挖7米后,被眼前一幕震撼

青丝人生
2024-01-29 17:59:05
洪金宝:我不在乎钱,如果以我当年的财力,现在可能是半个李嘉诚

洪金宝:我不在乎钱,如果以我当年的财力,现在可能是半个李嘉诚

吾爱纪实
2024-06-20 11:17:38
华晨宇香港演唱会造型曝光,想走林佳树路线,网友热议太“奇葩”

华晨宇香港演唱会造型曝光,想走林佳树路线,网友热议太“奇葩”

娱乐白名单
2024-06-19 10:55:48
论真核的作用!去年德国11场输6场,克罗斯回归后出战5场德国全胜

论真核的作用!去年德国11场输6场,克罗斯回归后出战5场德国全胜

直播吧
2024-06-20 11:52:52
上海市委书记、上海警备区党委第一书记陈吉宁简历

上海市委书记、上海警备区党委第一书记陈吉宁简历

探秘桂北
2024-05-03 00:11:57
“人造数学天才”,一个非常拙劣的谎言,彻头彻尾的炒作

“人造数学天才”,一个非常拙劣的谎言,彻头彻尾的炒作

爆角追踪
2024-06-18 17:25:17
中午,医生来把尿管拔了,我给妈妈清洗了一下。她什么也没问...

中午,医生来把尿管拔了,我给妈妈清洗了一下。她什么也没问...

小刀99
2024-05-11 09:45:09
难以相信!日本的国际形象很差吗?网友:佩服得五体投地!

难以相信!日本的国际形象很差吗?网友:佩服得五体投地!

有趣的羊驼
2024-06-06 14:22:52
巨贪自创快乐日记,震碎所有人的三观

巨贪自创快乐日记,震碎所有人的三观

陈皮吃话梅
2023-10-18 14:10:46
事态升级!南医大拒绝央视采访,该校学生发声:支持老师先救人!

事态升级!南医大拒绝央视采访,该校学生发声:支持老师先救人!

音乐时光的娱乐
2024-06-21 00:36:28
张维迎:瞧!他们说假话不脸红

张维迎:瞧!他们说假话不脸红

我是娱有理
2024-05-01 07:18:24
李莉女士怀抱着无畏的精神,披露国际风云,却不幸陷入网暴漩涡中

李莉女士怀抱着无畏的精神,披露国际风云,却不幸陷入网暴漩涡中

橘色数码
2024-06-06 12:51:20
张路:英格兰好像没有发挥出球员的能力,问题在哪儿也说不太清

张路:英格兰好像没有发挥出球员的能力,问题在哪儿也说不太清

直播吧
2024-06-21 02:05:25
2024-06-21 10:02:44
Linux
Linux
Linux 中国开源社区
8016文章数 73124关注度
往期回顾 全部

科技要闻

美媒:苹果正与百度阿里百川等谈AI合作

头条要闻

媒体:中国外交部刚批评美国 五角大楼就送来"神助攻"

头条要闻

媒体:中国外交部刚批评美国 五角大楼就送来"神助攻"

体育要闻

1-0"吊打"意大利 西班牙这就叫冠军相?

娱乐要闻

叶舒华参加柯震东生日聚会,五毒俱全

财经要闻

普华永道,引火烧身

汽车要闻

售价11.79-14.39万元 新一代哈弗H6正式上市

态度原创

亲子
游戏
时尚
房产
本地

亲子要闻

你这声丈母家叫的 估计你同学妈妈心都化了 网友:情商智商双在线

《超越善恶 20周年纪念版》6.25发售 登陆全平台

黑色的透视单品,就选这6件!

房产要闻

海棠湾!一所重量级国际学校真的来了!

本地新闻

2024·合肥印象|用崭新视角对话城市发展

无障碍浏览 进入关怀版