C语言KR圣经笔记 5.10命令行参数

2024-01-19 13:44

本文主要是介绍C语言KR圣经笔记 5.10命令行参数,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 5.10 命令行参数

在支持 C 语言的环境中,有一种方法可以在程序开始执行时将命令行参数传递给程序。当 main 被调用时,会带着两个参数。第一个是程序被调用时带的命令行参数个数(按惯例称为 argc,即参数个数 argument count 的缩写),第二个是指向包含所有参数的字符串数组的指针(argv,参数向量 argument vector 的缩写),数组里每个字符串对应一个参数。我们习惯使用多级指针来操作这些字符串。

最简单的一个例子是程序 echo,echo 会将它的命令行参数回显到一行内,参数之间用空格分隔。也就是说,如下命令

echo hello, world

会打印

hello, world

根据惯例,argv[0] 是被调用程序的名字,因此 argc 至少为 1。若 argc 为1,说明在程序名之后没有命令行参数。在上面的例子中 argc 为 3,而argv[0],argv[1],argv[2] 分别是 "echo", "hello," 和 "world"。第一个可能存在的参数是 argv[1],最后一个是 argv[argc -1],另外,C 标准要求 argv[argc] 必须是空指针。

echo 的第一个版本将 argv 作为字符指针的数组来对待

#include <stdio.h>/* 回显命令行参数;第一版 */
main(int argc, char *argv[])
{int i;for (i = 1; i < argc; i++)printf("%s%s", argv[i], (i < argc-1) ? " " : "");printf("\n");return 0;
}

既然 argv 是指向指针数组的指针,我们可以操作指针而不是数组索引。下面这个版本在 argc 递减时,对指向 char 指针的指针 argv 进行递增。

#include <stdio.h>/* 回显命令行参数;第二版 */
main(int argc, char *argv[])
{while (--argc > 0)printf("%s%s", *++argv, (argc > 1) ? " " : "")printf("\n");return 0;
}

由于 argv 是指向参数字符串数组开头的指针,将其递增 1 (++argv)使它一开始就指向 argv[1] 而不是 argv[0]。后续每次递增将其移到下一个参数;然后 *argv 就是该参数的指针。同时,argc递减,当它变为零时,就没有待打印的参数了。

另外,我们也可以把 printf 写成

    printf((argc > 1) ? "%s " : "%s", *++argv);

这表示 printf 的格式化参数也可以是一个表达式。

第二个例子,是对 4.1 节的样式搜索程序做一些增强。如果你还能记得的话,我们把要搜索的样式深埋在了程序里面,这种做法明显没法让人满意。我们参考 UNIX 程序 grep 来修改这个程序,使要匹配的样式通过命令行的第一个参数来指定。

#include <stdio.h>
#include <string.h>
#define MAXLINE 1000int getline(char *line, int max);/* find:打印匹配第一个参数的行 */
main(int argc, char *argv[])
{char line[MAXLINE];int found = 0;if (argc != 2)printf("Usage: find pattern\n");elsewhile (getline(line, MAXLINE) > 0)if (strstr(line, argv[1]) != NULL) {printf("%s", line);found++;}return found;
}

标准库函数 strstr(s, t) 返回字符串 t 在字符串 s 中首次出现的位置,若 s 不包含 t 则返回 NULL。该函数在 <string.h> 中声明。

现在可以对上面的原型程序进行完善,以此来说明更多的指针结构。假定我们想增加两个可选参数。一个是“打印除了匹配样式之外的所有行”;第二个是“在每个输出的行前面加上行号”。

UNIX 系统里 C 程序的通用惯例,是用负号开头的参数表示一个可选的标志符或参数。如果我们选用 -x (代表“除了” except)来表示取反,用 -n (行数 “number”)来要求输出行编号,则如下命令

find -x -n pattern

将打印每个不匹配样式的行,每行前面有行号。

应当允许可选的参数以任意的顺序出现,而程序的其他部分应当独立于给出的参数个数。更进一步,如果选项参数可以结合起来,对用户会更方便,如

find -nx pattern

这里是完善后的程序:

#include <stdio.h>
#include <string.h>
#define MAXLINE 1000int getline(char *line, int max);/* find:打印与第一个参数匹配的行 */
main(int argc, char *argv[])
{char line[MAXLINE];long lineno = 0;int c, except = 0, number = 0, found = 0;while (--argc > 0 && (*++argv)[0] == '-')while (c = *++argv[0])switch (c) {case 'x':except = 1;break;case 'n':number = 1;break;default:printf("find: illegal option %c\n", c);argc = 0;found = -1;break;}if (argc != 1)printf("Usage: find -x -n pattern\n");elsewhile (getline(line, MAXLINE) > 0) {lineno++;if ((strstr(line, *argv) != NULL)) != except) {if (number)printf("%ld:", lineno);printf("%s", line);found++;}}return found;
}

在每个可选参数之前,argc 递减而 argv 递增。在循环结束时,如果没有错误,则 argc 告诉我们还剩多少参数未处理,而 argv 指向这些剩余参数中的第一个。因此 argc 应当为 1 而 *argv 应指向要匹配的样式。注意 *++argv 是指向参数字符串的指针,因此 (*++argv)[0] 是其首个字符。(另一种合法的写法是 **++argv)。由于 [ ] 比 * 和 ++ 绑定得更紧,因此括号是必须的;若没有括号,表达式会变为 *++(argv[0])。实际上,这正是我们在内层循环中所使用的,这里的任务是在特定的参数字符串中遍历。在内层循环中,表达式 *++argv[0] 对指针 argv[0] 进行递增!

比上面这些指针表达式还复杂的使用场景是很稀少的;在那些情况下,将其拆为两个或三个步骤会更直观。

练习5-10、写一个程序 expr,计算从命令行输入的逆波兰表达式,其中每个操作符或操作数是一个单独的参数。例如  expr 2 3 4 + * 会对 2 * (3+4) 求值。

练习5-11、扩展(练习1-21中的) entab 和 detab 以支持简写, entab -m +n, 表示制表符从第 m 列开始,每 n 列停止。选择(对用户来说)方便的默认行为。

练习5-13、写程序 tail,输出其输入的末尾 n 行。我们定义默认 n 为 10,但可以通过可选参数进行修改,例如 tail -n 打印最后 n 行。不管输入或者 n 的值多么不合理,程序都应该有合理的行为。程序应当最大化地利用存储空间;文本行应当像 5.6 节中的排序程序一样存储,而不能存为固定长度的二维数组。

这篇关于C语言KR圣经笔记 5.10命令行参数的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/622608

相关文章

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本

Go语言中三种容器类型的数据结构详解

《Go语言中三种容器类型的数据结构详解》在Go语言中,有三种主要的容器类型用于存储和操作集合数据:本文主要介绍三者的使用与区别,感兴趣的小伙伴可以跟随小编一起学习一下... 目录基本概念1. 数组(Array)2. 切片(Slice)3. 映射(Map)对比总结注意事项基本概念在 Go 语言中,有三种主要

Java通过反射获取方法参数名的方式小结

《Java通过反射获取方法参数名的方式小结》这篇文章主要为大家详细介绍了Java如何通过反射获取方法参数名的方式,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1、前言2、解决方式方式2.1: 添加编译参数配置 -parameters方式2.2: 使用Spring的内部工具类 -

C语言中自动与强制转换全解析

《C语言中自动与强制转换全解析》在编写C程序时,类型转换是确保数据正确性和一致性的关键环节,无论是隐式转换还是显式转换,都各有特点和应用场景,本文将详细探讨C语言中的类型转换机制,帮助您更好地理解并在... 目录类型转换的重要性自动类型转换(隐式转换)强制类型转换(显式转换)常见错误与注意事项总结与建议类型

Go语言利用泛型封装常见的Map操作

《Go语言利用泛型封装常见的Map操作》Go语言在1.18版本中引入了泛型,这是Go语言发展的一个重要里程碑,它极大地增强了语言的表达能力和灵活性,本文将通过泛型实现封装常见的Map操作,感... 目录什么是泛型泛型解决了什么问题Go泛型基于泛型的常见Map操作代码合集总结什么是泛型泛型是一种编程范式,允

Android kotlin语言实现删除文件的解决方案

《Androidkotlin语言实现删除文件的解决方案》:本文主要介绍Androidkotlin语言实现删除文件的解决方案,在项目开发过程中,尤其是需要跨平台协作的项目,那么删除用户指定的文件的... 目录一、前言二、适用环境三、模板内容1.权限申请2.Activity中的模板一、前言在项目开发过程中,尤

C语言小项目实战之通讯录功能

《C语言小项目实战之通讯录功能》:本文主要介绍如何设计和实现一个简单的通讯录管理系统,包括联系人信息的存储、增加、删除、查找、修改和排序等功能,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录功能介绍:添加联系人模块显示联系人模块删除联系人模块查找联系人模块修改联系人模块排序联系人模块源代码如下

基于Go语言实现一个压测工具

《基于Go语言实现一个压测工具》这篇文章主要为大家详细介绍了基于Go语言实现一个简单的压测工具,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录整体架构通用数据处理模块Http请求响应数据处理Curl参数解析处理客户端模块Http客户端处理Grpc客户端处理Websocket客户端

Python调用另一个py文件并传递参数常见的方法及其应用场景

《Python调用另一个py文件并传递参数常见的方法及其应用场景》:本文主要介绍在Python中调用另一个py文件并传递参数的几种常见方法,包括使用import语句、exec函数、subproce... 目录前言1. 使用import语句1.1 基本用法1.2 导入特定函数1.3 处理文件路径2. 使用ex

MySQL中时区参数time_zone解读

《MySQL中时区参数time_zone解读》MySQL时区参数time_zone用于控制系统函数和字段的DEFAULTCURRENT_TIMESTAMP属性,修改时区可能会影响timestamp类型... 目录前言1.时区参数影响2.如何设置3.字段类型选择总结前言mysql 时区参数 time_zon