精华内容
下载资源
问答
  • Python 命令行参数详解

    千次阅读 多人点赞 2021-08-13 00:02:27
    通常,对于大型项目程序而言,执行程序的一个必要的步骤是正确处理命令行参数,这些参数是提供给包含某种参数化信息的程序或脚本的参数。命令行参数参数化程序执行的一种常见且简单的方法。本文主要介绍以下三种...

    0. 命令行参数

    通常,对于大型项目程序而言,执行程序的一个必要的步骤是正确处理命令行参数,这些命令行参数是提供给包含某种参数化信息的程序或脚本的参数。例如,在计算机视觉项目中,图像和不同类型的文件通常作为命令行参数传递给脚本,用于使程序可以处理不同图片或者不同类型文件。
    命令行参数是参数化程序执行的一种常见且简单的方法,下面主要介绍三种常见的获取和解析命令行参数的方法。

    1. sys.argv

    为了处理命令行参数,Python 中内置了 sys.argv 模块,通过模块中的 sys.argv 就可以访问到所有的命令行参数,它的返回值是包含所有命令行参数的列表 (list)。当程序执行时,Python 从命令行获取所有值并将它们存储在 sys.argv 列表中。列表的第一个元素 sys.argv[0] 是脚本的完整路径(或脚本名称——取决于具体操作系统)。列表的第二个元素是脚本的第一个命令行参数,即 sys.argv[1],依此类推。这可以通过下图中清晰的看出,其中 script_1.py 脚本使用两个参数执行:

    sys.argv
    接下来,让我们看看 sys.argv 是如何工作的,首先编写 scripy_1.py 脚本:

    import sys
    print("正在运行的脚本名称: '{}'".format(sys.argv[0]))
    print("脚本的参数数量: '{}'".format(len(sys.argv)))
    print("脚本的参数: '{}'".format(str(sys.argv)))
    

    如果我们不使用任何参数执行这个脚本:

    python script_1.py
    

    将会看到如下信息:

    正在运行的脚本名称: 'script_1.py'
    脚本的参数数量: '1'
    脚本的参数: '['script_1.py']'
    

    如果我们使用多个参数执行此脚本:

    python script_1.py OpenCV -i test.png
    

    将得到以下信息:

    正在运行的脚本名称: 'script_1.py'
    脚本的参数数量: '4'
    脚本的参数: '['script_1.py', 'OpenCV', '-i', 'test.png']'
    

    如上所示,列表的第一个元素 script_1.py (sys.argv[0]) 是脚本名称。列表的第二个元素 (sys.argv[1]) OpenCV 是脚本的第一个参数。但同时也可以看到,sys.argv 将命令行选项 -i 也识别为参数,这样并不能方便的满足我们的需求,因此引入 getopt 模块来识别命令行选项。

    2. getopt

    getopt 模块是专门处理命令行参数的模块,用于获取命令行选项和参数。命令行选项使得程序的参数更加灵活,其支持短选项模式(-)和长选项模式(–)。
    该模块提供了两个方法及一个异常处理来解析命令行参数。

    2.1 getopt.getopt 方法

    getopt.getopt 方法用于解析命令行参数列表,其语法格式如下:

    getopt.getopt(args, options[, long_options])
    

    方法参数说明如下表所示:

    参数说明
    args要解析的命令行参数列表,一般是sys.argv[1:],需要过滤掉脚本名(sys.argv[0])
    options以字符串的格式定义,options 后的冒号 “:” 表示如果设置该选项,必须有附加的参数,否则就不附加参数
    long_options以列表的格式定义,long_options 后的等号 “=” 表示该选项必须有附加的参数,不带冒号表示该选项不附加参数

    该方法返回值由两个元素组成: 第一个是 (option, value) 元组的列表。 第二个是参数列表,包含那些没有 - 或 – 的参数。
    下面编写 script_2.py 脚本进行演示:

    import sys
    import getopt
    
    
    def main(argv):
        input_file = ""
        output_file = ""
        # "hi:o:": 短格式分析串, h 后面没有冒号, 表示后面不带参数; i 和 o 后面带有冒号, 表示后面带参数
        # ["help", "input_file=", "output_file="]: 长格式分析串列表, help后面没有等号, 表示后面不带参数; input_file和output_file后面带冒号, 表示后面带参数
        # 返回值包括 `opts` 和 `args`, opts 是以元组为元素的列表, 每个元组的形式为: (选项, 附加参数),如: ('-i', 'test.png');
        # args是个列表,其中的元素是那些不含'-'或'--'的参数
        opts, args = getopt.getopt(argv[1:], "hi:o:", ["help", "input_file=", "output_file="])
    
        for opt, arg in opts:
            if opt in ("-h", "--help"):
                print('script_2.py -i <input_file> -o <output_file>')
                print('or: test_arg.py --input_file=<input_file> --output_file=<output_file>')
                sys.exit()
            elif opt in ("-i", "--input_file"):
                input_file = arg
            elif opt in ("-o", "--output_file"):
                output_file = arg
        print('输入文件为:', input_file)
        print('输出文件为:', output_file)
    
        # 打印不含'-'或'--'的参数
        for i in range(0, len(args)):
            print('不含'-'或'--'的参数 %s 为:%s' % (i + 1, args[i]))
            
    if __name__ == "__main__":
        main(sys.argv)
    

    使用带有命令行选项的命令执行此脚本,以下两种方式是等价的:

    # 方式1
    python scripy_1.py -i test.png -o output.png OpenCV
    # 方式2
    python scripy_1.py --input_file test.png --output_file output.png OpenCV
    

    输出得到以下信息:

    输入文件为: test.png
    输出文件为: output.png
    不含'-''--'的参数 1 为:OpenCV
    

    2.2 Exception getopt.GetoptError

    在参数列表中没有找到所传递参数,或选项的需要的参数为空时会触发该异常。异常的参数是一个字符串,表示错误的原因。属性 msgopt 为相关选项的错误信息。
    在上述代码中添加异常处理,检查此错误信息:

    # ...
    def main(argv):
        input_file = ""
        output_file = ""
        try:
            opts, args = getopt.getopt(argv[1:], "hi:o", ["help", "input_file=", "output_file="])
        except getopt.GetoptError as e:
            print(e.msg)
            print(e.opt)
            sys.exit(2)
    # ...
    

    使用错误的格式选项传递参数执行脚本:

    python scripy_1.py -f
    

    输出以下错误信息:

    option -f not recognized
    f
    

    3. argparse

    当程序中使用采用复杂参数或多个文件名时,推荐使用 Python 的 argparse 库,它以系统的方式处理命令行参数,从而可以编写用户友好的命令行程序。Python 标准库 argparse 同样也是用于解析命令行参数的模块。首先,由程序确定所需的参数,然后, argparse 将这些参数解析为 sys.argv。此外,argparse 会生成帮助和使用信息提示,并在提供无效参数时发出错误。
    为了介绍此模块,编写 script_3.py,如下所示:

    import argparse
    parser = argparse.ArgumentParser()
    parser.parse_args()
    

    不带参数运行此脚本不会向 stdout 显示任何内容。但是,如果使用 --help-h 选项,将得到脚本的使用信息提示:

    usage: scripy_3.py [-h]
    optional arguments:
    -h, --help show this help message and exit
    

    指定其他参数会导致错误,例如使用如下命令:

    scripy_3.py -i
    

    则会报导致错误:

    usage: scripy_3.py [-h]
    argparse_minimal.py: error: unrecognized arguments: -i
    

    由于未定义参数,因此不允许其他参数,接下来就添加一个参数,编写 script_4.py 脚本:

    import argparse
    parser = argparse.ArgumentParser()
    parser.add_argument("first_argument", help="this is the string text in connection with first_argument")
    args = parser.parse_args()
    print(args.first_argument)
    

    这里添加了 add_argument() 方法。此方法用于指定程序将接受哪些命令行选项,此处添加了 first_argument 参数。此外, argparse 模块存储所有参数,将其名称与每个添加参数的名称相匹配——在此处为 first_argument 。为了获得参数值,需要使用 args.first_argument
    如果此脚本以下示方法执行,则输出为 10:

    python scripy_4.py 10
    

    但如果脚本在没有参数的情况下执行,则将输出以下信息:

    usage: scripy_4.py [-h] first_argument
    scripy_4.py: error: the following arguments are required: first_argument
    

    最后,如果我们使用 -h 选项执行脚本,输出将如下所示:

    usage: scripy_4.py [-h] first_argument
    
    positional arguments:
      first_argument  this is the string text in connection with first_argument
    
    optional arguments:
      -h, --help      show this help message and exit
    

    默认情况下,argparse 将提供的选项视为字符串。因此,如果参数不是字符串,则应使用 type 选项。使用 script_5.py 脚本,其中添加了两个参数,这两个参数是 int 类型:

    import argparse
    parser = argparse.ArgumentParser()
    parser.add_argument("first_number", help="first number to be added", type=int)
    parser.add_argument("second_number", help="second number to be added", type=int)
    args = parser.parse_args()
    print("args: '{}'".format(args))
    print("the sum is: '{}'".format(args.first_number + args.second_number))
    args_dict = vars(parser.parse_args())
    print("args_dict dictionary: '{}'".format(args_dict))
    print("first argument from the dictionary: '{}'".format(args_dict["first_number"]))
    

    在前面的示例中,通过调用 vars() 函数将参数存储在字典中:

    args_dict = vars(parser.parse_args())
    print("args_dict dictionary: '{}'".format(args_dict))
    print("first argument from the dictionary: '{}'".format(args_dict["first_number"]))
    

    如果不带参数执行脚本:

    python script_5.py
    

    则输出如下:

    usage: scripy_5.py [-h] first_number second_number
    scripy_5.py: error: the following arguments are required: first_number, second_number
    

    此外,如果我们使用 -h 选项执行脚本:

    python script_5.py --help
    

    输出将如下所示:

    usage: scripy_1.py [-h] first_number second_number
    
    positional arguments:
      first_number   first number to be added
      second_number  second number to be added
    
    optional arguments:
      -h, --help     show this help message and exit
    

    如果此脚本以如下方式执行:

    python script_5.py 123 456
    

    则输出如下:

    args: 'Namespace(first_number=123, second_number=456)'
    the sum is: '579'
    args_dict dictionary: '{'first_number': 123, 'second_number': 456}'
    first argument from the dictionary: '123'
    

    更多 argparse 的高级介绍可以在官方文档中看到,其中包括了大量示例

    展开全文
  • 对于电脑屏幕也是,有条件的情况下尽可能满足以下3点: 1、分辨率尽量大,大分辨率的好处是屏幕可以同时展示更多的内容,开个软件什么的也比较完整。 2、 屏幕的色域值越高越好,特别在一些高动态对比的画面或者...

    点赞后看,养成习惯
    喜欢的话 可以点个关注哟
    你们的点赞支持对博主们来说很重要哦 !!!

    要说笔记本电脑的参数,我们可以从两个角度去切入,一个是关于性能,另一个是关于体验。

    关于性能的参数有:CPU、显卡、内存、硬盘,这综合决定了你电脑运行时是否流畅,顺滑。

    关于使用体验的有:屏幕、散热、接口、拓展性,这决定了你的视觉体验、触感体验以及充电体验是否良好等。

    接下来,我一个一个来说明:


    在这里插入图片描述



    01 CPU:中央处理器,计算机的大脑

    我们经常说一个人才思敏捷,实际上便是说他的脑子转得快,思路清晰。

    对笔记本电脑也是如此,一个好的CPU,很大程度上代表着流畅的反应速度、高效的数据处理能力以及相对较低的功耗。

    在品牌选择上,我建议预算足够优先选择Intel,预算不高的情况下再选用AMD。

    至于具体参数,主要看核心数、主频这两个参数,以上两个参数越高,就代表性能越好。

    这里,教大家简单地读CPU参数,CPU由几个部分组成 :年代+性能指数(1-9)+代号 +英文字符

    关于性能指数,简单来说大于5的可以够得上游戏本配置;等于5的那就是中等配置;如果是9的,那就是牛逼配置!

    比如 i7 7700HQ,说的是酷睿7代,性能指数为7,00是它的代号,HQ指的是笔记本焊接板。

    关于字母含义再举几个例子:

    台式机超频版 i9 9900K(睿频高)

    台式机至尊版 i7 7980XE(X)(主频睿频都牛逼)

    笔记本性能版 i7 4710MQ(可拆卸、可更换)

    在这里插入图片描述


    02 显卡:GPU,决定你能玩什么类型的游戏

    大多数情况下,游戏相对于CPU其实更需要显卡的性能支持。你可以理解为显卡是你接触游戏世界的一个通行证,显卡的性能上限决定你到底可以运行哪类游戏。

    简单来说,显卡与玩游戏息息相关,显卡越牛,你就能运行越大型的游戏,开越高级的特效。

    大多数情况下,独立显卡的性能都优于集成显卡。

    至于具体参数,主要看四个参数:显存位宽、频率、显存带宽、流处理器。这四个参数都是越大越好。

    目前市面上流行的游戏系列显卡主要是GTX、RTX

    拿个显卡举例:GTX 1660 6G

    其中GTX是显卡系列名称,16是一个代号,不用理会太多。
    而这个6呢,代表的是性能等级,从1到9性能逐渐变强。
    而这个6G指的是显存,如果你想要大型游戏,显存得过关,至少4G以上才算得上是游戏本。

    简单总结一下,看一个独立显卡,最基本我们看两点,性能+显存。性能从1到9,越高越好,显存至少4G以上。





    在这里插入图片描述


    03 内存:RAM,大脑的缓冲期,里面有正在使用的软件或马上要使用的软件

    内存,RAM,载入数据块,用于存放临时程序与数据,其容量大小直接决定你在电脑后台可以开多少进程(软件或服务等),以及进程的响应速度和运行流畅度。

    就内存而言,总容量越大越好

    如果你是4G内存,估计多开几个软件就会卡。

    如果你是8G内存,这是市面上主流配置,够绝大多数情况下使用。

    如果你是16G内存,那你已经领先了绝大多数笔记本电脑。

    如果你有加内存条的打算,首先建议你可以下载CPU-Z软件查看本身的内存信息,了解好本台计算机的内存类型和内存频率。其次询问专业人员,也可以是卖你电脑的人,说主板是否支持多加内存条。

    在这里插入图片描述


    04 硬盘:作为内存的缓冲期,内存从硬盘中读取软件信息

    硬盘,读取数据速度较慢,用于存放安装程序、视频、音频、文档等各种数据,其容量大小决定了你在电脑中能安装多少软件,能存放多少歌曲、电影、电子书,而不会影响运行速度。

    硬盘有机械硬盘和固态硬盘之分,你要知道最基础的一点

    读取速度上,绝对的,固态硬盘 > 机械硬盘,只要是固态硬盘,读取速度就比机械硬盘快无数倍,虽然固态硬盘数据丢失找不回来。

    固态硬盘再细分,可以按照接口和支持协议来分。

    SATA3接口:这是和机械硬盘一样的接口,接上去的是普通固态硬盘。读写速度撑死就是500M/s左右。

    而为了升级读写速率,后面陆陆续续推出了Mini M2接口,PCIE接口(适用于台式机)、M.2接口(适用于笔记本):读写速录可达 1200Mb/s。

    还有就是非常强的支持NVME协议的M.2接口M.2,读写速率可达3000MB/s + 。

    总之,有条件情况下,用固态准没错的。
    没条件情况下,买机械硬盘,着重看两点:

    转数,转数越大,性能越好。

    容量,容量越大,可存储的东西越多。

    在这里插入图片描述
    在这里插一条我个人的理解,有关于内存和硬盘的关系:

    打个比方,如果把硬盘比作收纳箱的话,那内存就是你的随身衣服的口袋,这里假定
    收纳箱里的东西都需要先放到口袋里才能被我们直接使用,那么:
    收纳箱的空间决定我们到底可以拥有多少工具、电器
    而随身的口袋能决定我们能同时使用多少工具和电器



    05 屏幕:直接决定我们的视觉体验

    就像看视频的时候,你大概率是能开1080P,就开1080P,除非网速受限的情况下才会做调整。
    对于电脑屏幕也是,有条件的情况下尽可能满足以下3点:

    1、分辨率尽量大,大分辨率的好处是屏幕可以同时展示更多的内容,开个软件什么的也比较完整。

    2、 屏幕的色域值越高越好,特别在一些高动态对比的画面或者游戏中,你可以更好发现细节差异,低色域的常常糊成一片,看不出灰度差距。

    3、屏幕刷新高,对笔记本来说,有144hz的更好,但价格很贵,还不如自己再补一个外置显示器。

    当然也有不少人没有太过于注重笔记本电脑本身的屏幕参数,而选择购买一个好的外置显示器,高分辨率+超大寸,感官体验也是一级棒。这就根据大家自身情况而定了。

    还有一点要提醒大家,分辨率高,刷新率高的屏幕,对CPU、GPU性能要求当然也高。假如你手头只有一台4000配置的笔记本,却想要配上一台4000价位的4K显示屏的话,那还是就只要想想吧。
    在这里插入图片描述


    06 散热:体现笔记本性能、摸起来烫不烫、噪音大不大

    你想想啊,你文档写着写着,游戏打着打着,突然手臂感觉要被烫伤了,这是一种多么郁闷的体验。(比较夸张的举例)

    散热很重要,但人们有一个普遍的错误认知,那就是:
    风扇狂转、表面很烫、出风口烫 = 散热不好

    实际上笔记本电脑散热设计有一个原则:如果无法避免热量,那就把发热区域远离人经常碰的地方。


    在这里插入图片描述


    07 接口:与你日常充电、数据传输息息相关

    关于接口,目前已知,USB-C是目前最先进的USB接口

    在电脑内部支持协议上,雷电3>USB3.1>USB3.0,雷电3协议同时支持充电和数据传输

    雷电3协议支持的USB-C就像手机充电口一样,既可以充电又可以传输数据,非常方便。

    除此之外,10Gbps的USB-C接口支持4K显示屏,方便外接显示屏。

    简单一句话,如果你的电脑能够有一个高传输速率的USB-C接口,你充电、传输数据、外接显示屏都很方便。


    在这里插入图片描述


    08 扩展性:代表有多少种可以更换的部件

    这对有升级电脑配置有需求的人群来说非常关键,尤其是对电脑配置有职业要求的人,买笔记本电脑的时候多考虑能更换的部件,能为之后工作省不少钱

    一般来说呢,游戏本能更换的比较多,比如内存、固态、显卡

    在这里插入图片描述




    以上文章,作为自己的学习笔记,仅供参考

    本文完,感谢你的阅读!!!

    最后,如果本文对你有所帮助,希望可以点个赞支持一下。你们的鼓励将会是博主原创的动力。
    在这里插入图片描述

    展开全文
  • 函数参数中的3个点表示什么

    千次阅读 2018-10-12 16:42:42
    这种函数被称作“具有变长度参数表的函数”,或简称为“变参数函数”。我们写程序中有时也可能需要定义这种函数。要定义这类函数,就必须使用标准头文件&lt;stdarg.h&gt;,使用该文件提供的一套机制,并需要...

    转载于网友的一片文章,写的很好!

             标准库提供的一些参数的数目可以有变化的函数。例如我们很熟悉的printf,它需要有一个格式串,还应根据需要为它提供任意多个“其他参数”。这种函数被称作“具有变长度参数表的函数”,或简称为“变参数函数”。我们写程序中有时也可能需要定义这种函数。要定义这类函数,就必须使用标准头文件<stdarg.h>,使用该文件提供的一套机制,并需要按照规定的定义方式工作。本节介绍这个头文件提供的有关功能,它们的意义和使用,并用例子说明这类函数的定义方法。
             一个变参数函数至少需要有一个普通参数,其普通参数可以具有任何类型。在函数定义中,这种函数的最后一个普通参数除了一般的用途之外,还有其他特殊用途。下面从一个例子开始说明有关的问题。
            假设我们想定义一个函数sum,它可以用任意多个整数类型的表达式作为参数进行调用,希望sum能求出这些参数的和。这时我们应该将sum定义为一个只有一个普通参数,并具有变长度参数表的函数,这个函数的头部应该是(函数原型与此类似):
    int sum(int n, ...)
            我们实际上要求在函数调用时,从第一个参数n得到被求和的表达式个数,从其余参数得到被求和的表达式。在参数表最后连续写三个圆点符号,说明这个函数具有可变数目的参数。凡参数表具有这种形式(最后写三个圆点),就表示定义的是一个变参数函数。注意,这样的三个圆点只能放在参数表最后,在所有普通参数之后。
            为了能在变参数函数里取得并处理不定个数的“其他参数”,头文件<stdarg.h>提供了一套机制。这里提供了一个特殊类型va_list。在每个变参数函数的函数体里必须定义一个va_list类型的局部变量,它将成为访问由三个圆点所代表的实际参数的媒介。下面假设函数sum里所用的va_list类型的变量的名字是vap。在能够用vap访问实际参数之前,必须首先用“函数”va_start做这个变量初始化。函数va_start的类型特征可以大致描述为:
    va_start(va_list vap, 最后一个普通参数)
    实际上va_start通常并不是函数,而是用宏定义实现的一种功能。在函数sum里对vap初始化的语句应当写为:
    va_start(vap, n);
    在完成这个初始化之后,我们就可以通过另一个宏va_arg访问函数调用的各个实际参数了。宏va_arg的类型特征可以大致地描述为:
    类型 va_arg(va_list vap, 类型名)
            在调用宏va_arg时必须提供有关实参的实际类型,这一类型也将成为这个宏调用的返回值类型。对va_arg的调用不仅返回了一个实际参数的值(“当前”实际参数的值),同时还完成了某种更新操作,使对这个宏va_arg的下次调用能得到下一个实际参数。对于我们的例子,其中对宏va_arg的一次调用应当写为:
    v = va_arg(vap, int);
    这里假定v是一个有定义的int类型变量。
            在变参数函数的定义里,函数退出之前必须做一次结束动作。这个动作通过对局部的va_list变量调用宏va_end完成。这个宏的类型特征大致是:
    void va_end(va_list vap);
    下面是函数sum的完整定义,从中可以看到各有关部分的写法:
    int sum(int n, ...) {
          va_list vap;
           int i, s = 0;
           va_start(vap, n);
           for (i = 0; i < n; i++) s += va_arg(vap, int);
           va_end(vap);
           return s;
    }
    这里首先定义了va_list变量vap,而后对它初始化。循环中通过va_arg取得顺序的各个实参的值,并将它们加入总和。最后调用va_end结束。
    下面是调用这个函数的几个例子:
    k = sum(3, 5+8, 7, 26*4);
    m = sum(4, k, k*(k-15), 27, (k*k)/30);
             在编写和使用具有可变数目参数的函数时,有几个问题值得注意。首先,虽然在上面描述了头文件所提供的几个宏的“类型特征”,实际上这仅仅是为了说明问题。因为实际上我们没办法写出来有关的类型,系统在预处理时进行宏展开,编译时即使发现错误,也无法提供关于这些宏调用的错误信息。所以,在使用这些宏的时候必须特别注意类型的正确性,系统通常无法自动识别和处理其中的类型转换问题。
            第二:调用va_arg将更新被操作的va_list变量(如在上例的vap),使下次调用可以得到下一个参数。在执行这个操作时,va_arg并不知道实际有几个参数,也不知道参数的实际类型,它只是按给定的类型完成工作。因此,写程序的人应在变参数函数的定义里注意控制对实际参数的处理过程。上例通过参数n提供了参数个数的信息,就是为了控制循环。标准库函数printf根据格式串中的转换描述的数目确定实际参数的个数。如果这方面信息有误,函数执行中就可能出现严重问题。编译程序无法检查这里的数据一致性问题,需要写程序的人自己负责。在前面章节里,我们一直强调对printf等函数调用时,要注意格式串与其他参数个数之间一致性,其原因就在这里。
            第三:编译系统无法对变参数函数中由三个圆点代表的那些实际参数做类型检查,因为函数的头部没有给出这些参数的类型信息。因此编译处理中既不会生成必要的类型转换,也不会提供类型错误信息。考虑标准库函数printf,在调用这个函数时,不但实际参数个数可能变化,各参数的类型也可能不同,因此不可能有统一方式来描述它们的类型。对于这种参数,C语言的处理方式就是不做类型检查,要求写程序的人保证函数调用的正确性。
    假设我们写出下面的函数调用:
    k = sum(6, 2.4, 4, 5.72, 6, 2);
            编译程序不会发现这里参数类型不对,需要做类型转换,所有实参都将直接传给函数。函数里也会按照内部定义的方式把参数都当作整数使用。编译程序也不会发现参数个数与6不符。这一调用的结果完全由编译程序和执行环境决定,得到的结果肯定不会是正确的。

    展开全文
  • 什么是引用?函数传递参数的三种方式

    千次阅读 多人点赞 2019-07-06 16:00:52
    1.什么是引用? https://www.cnblogs.com/duwenxing/p/7421100.html 前言:引用是C++一个很重要的特性,最近看了很多有关引用的资料和博客,故在此对引用的相关知识进行总结 一、什么是引用 引用,顾名思义...

    1.什么是引用?

     

    https://www.cnblogs.com/duwenxing/p/7421100.html

     

    前言:引用是C++一个很重要的特性,最近看了很多有关引用的资料和博客,故在此对引用的相关知识进行总结

    一、什么是引用

    引用,顾名思义是某一个变量或对象的别名,对引用的操作与对其所绑定的变量或对象的操作完全等价

    语法:类型 &引用名=目标变量名;

    特别注意:

    1.&不是求地址运算符,而是起标志作用

    2.引用的类型必须和其所绑定的变量的类型相同

    复制代码

    1 #include<iostream>
    2 using namespace std;
    3 int main(){
    4     double a=10.3;
    5     int &b=a; //错误,引用的类型必须和其所绑定的变量的类型相同
    6     cout<<b<<endl;
    7 }

    复制代码

    3.声明引用的同时必须对其初始化,否则系统会报错

    复制代码

    1 #include<iostream>
    2 using namespace std;
    3 int main(){
    4     int &a; //错误!声明引用的同时必须对其初始化
    5     return 0;
    6 }

    复制代码

    4.引用相当于变量或对象的别名,因此不能再将已有的引用名作为其他变量或对象的名字或别名

    5.引用不是定义一个新的变量或对象,因此内存不会为引用开辟新的空间存储这个引用

    复制代码

    1 #include<iostream>
    2 using namespace std;
    3 int main(){
    4     int value=10;
    5     int &new_value=value;
    6     cout<<"value在内存中的地址为:"<<&value<<endl;
    7     cout<<"new_value在内存中的地址为:"<<&new_value<<endl;
    8     return 0;
    9 }

    复制代码

    6.对数组的引用

    语法:类型 (&引用名)[数组中元素数量]=数组名;

    复制代码

     1 #include<iostream>
     2 using namespace std;
     3 int main(){
     4     int a[3]={1,2,3};
     5     int (&b)[3]=a;//对数组的引用 
     6     cout<<&a[0]<<" "<<&b[0]<<endl;
     7     cout<<&a[1]<<" "<<&b[1]<<endl;
     8     cout<<&a[2]<<" "<<&b[2]<<endl;
     9     return 0;
    10 }

    复制代码

    7.对指针的引用

    语法:类型 *&引用名=指针名;//可以理解为:(类型*) &引用名=指针名,即将指针的类型当成类型*

    复制代码

    1 #include<iostream>
    2 using namespace std;
    3 int main(){
    4     int a=10;
    5     int *ptr=&a;
    6     int *&new_ptr=ptr;
    7     cout<<&ptr<<" "<<&new_ptr<<endl;
    8     return 0; 
    9 }

    复制代码

     

    二、引用的应用

    A.引用作为函数的参数

    复制代码

     1 #include<iostream>
     2 using namespace std;
     3 void swap(int &a,int &b){//引用作为函数的参数
     4     int temp=a;
     5     a=b;
     6     b=temp; 
     7 }
     8 int main(){
     9     int value1=10,value2=20;
    10     cout<<"----------------------交换前----------------------------"<<endl;
    11     cout<<"value1的值为:"<<value1<<endl; 
    12     cout<<"value2的值为:"<<value2<<endl;
    13     swap(value1,value2); 
    14     cout<<"----------------------交换后----------------------------"<<endl;
    15     cout<<"value1的值为:"<<value1<<endl; 
    16     cout<<"value2的值为:"<<value2<<endl;
    17     return 0;
    18 }

    复制代码

    特别注意:

    1.当用引用作为函数的参数时,其效果和用指针作为函数参数的效果相当。当调用函数时,函数中的形参就会被当成实参变量或对象的一个别名来使用,也就是说此时函数中对形参的各种操作实际上是对实参本身进行操作,而非简单的将实参变量或对象的值拷贝给形参

    2.通常函数调用时,系统采用值传递的方式将实参变量的值传递给函数的形参变量。此时,系统会在内存中开辟空间用来存储形参变量,并将实参变量的值拷贝给形参变量,也就是说形参变量只是实参变量的副本而已;并且如果函数传递的是类的对象,系统还会调用类中的拷贝构造函数来构造形参对象。而使用引用作为函数的形参时,由于此时形参只是要传递给函数的实参变量或对象的别名而非副本,故系统不会耗费时间来在内存中开辟空间来存储形参。因此如果参数传递的数据较大时,建议使用引用作为函数的形参,这样会提高函数的时间效率,并节省内存空间

    3.使用指针作为函数的形参虽然达到的效果和使用引用一样,但当调用函数时仍需要为形参指针变量在内存中分配空间,而引用则不需要这样,故在C++中推荐使用引用而非指针作为函数的参数

    4.如果在编程过程中既希望通过让引用作为函数的参数来提高函数的编程效率,又希望保护传递的参数使其在函数中不被改变,则此时应当使用对常量的引用作为函数的参数。

    5.数组的引用作为函数的参数:C++的数组类型是带有长度信息的,引用传递时如果指明的是数组则必须指定数组的长度

    复制代码

     1 #include<iostream>
     2 using namespace std;
     3 void func(int(&a)[5]){//数组引用作为函数的参数,必须指明数组的长度 
     4 //函数体 
     5 }
     6 int main(){
     7     int number[5]={0,1,2,3,4};
     8     func(number); 
     9     return 0; 
    10  }

    复制代码

     B.常引用

    语法:const 类型 &引用名=目标变量名;

    常引用不允许通过该引用对其所绑定的变量或对象进行修改

    复制代码

    1 #include<iostream>
    2 using namespace std;
    3 int main(){
    4     int a=10;
    5     const int &new_a=a;
    6     new_a=11;//错误!不允许通过常引用对其所绑定的变量或对象进行修改 
    7     return 0;
    8 }

    复制代码

    特别注意:

    先看下面的例子

    复制代码

     1 #include<iostream>
     2 #include<string> 
     3 using namespace std;
     4 string func1(){
     5     string temp="This is func1";
     6     return temp;
     7 }
     8 void func2(string &str){
     9     cout<<str<<endl;
    10 }
    11 int main(){
    12     func2(func1());
    13     func2("Tomwenxing");
    14     return 0;
    15 }

    复制代码

    运行上面的程序编译器会报错

    这是由于func1()和“Tomwenxing”都会在系统中产生一个临时对象(string对象)来存储它们,而在C++中所有的临时对象都是const类型的,而上面的程序试图将const对象赋值给非const对象,这必然会使程序报错。如果在函数func2的参数前添加const,则程序便可正常运行了

    复制代码

     1 #include<iostream>
     2 #include<string> 
     3 using namespace std;
     4 string func1(){
     5     string temp="This is func1";
     6     return temp;
     7 }
     8 void func2(const string &str){
     9     cout<<str<<endl;
    10 }
    11 int main(){
    12     func2(func1());
    13     func2("Tomwenxing");
    14     return 0;
    15 }

    复制代码

    C.引用作为函数的返回值

    语法:类型 &函数名(形参列表){ 函数体 }

    特别注意:

    1.引用作为函数的返回值时,必须在定义函数时在函数名前将&

    2.用引用作函数的返回值的最大的好处是在内存中不产生返回值的副本

    复制代码

     1 //代码来源:RUNOOB
     2 #include<iostream>
     3 using namespace std;
     4 float temp;
     5 float fn1(float r){
     6     temp=r*r*3.14;
     7     return temp;
     8 } 
     9 float &fn2(float r){ //&说明返回的是temp的引用,换句话说就是返回temp本身
    10     temp=r*r*3.14;
    11     return temp;
    12 }
    13 int main(){
    14     float a=fn1(5.0); //case 1:返回值
    15     //float &b=fn1(5.0); //case 2:用函数的返回值作为引用的初始化值 [Error] invalid initialization of non-const reference of type 'float&' from an rvalue of type 'float'
                               //(有些编译器可以成功编译该语句,但会给出一个warning) 
    16     float c=fn2(5.0);//case 3:返回引用
    17     float &d=fn2(5.0);//case 4:用函数返回的引用作为新引用的初始化值
    18     cout<<a<<endl;//78.5
    19     //cout<<b<<endl;//78.5
    20     cout<<c<<endl;//78.5
    21     cout<<d<<endl;//78.5
    22     return 0;
    23 }

    复制代码

     上例中4个case的说明解释:

    case 1:用返回值方式调用函数(如下图,图片来源:伯乐在线):

    返回全局变量temp的值时,C++会在内存中创建临时变量并将temp的值拷贝给该临时变量。当返回到主函数main后,赋值语句a=fn1(5.0)会把临时变量的值再拷贝给变量a

    case 2:用函数的返回值初始化引用的方式调用函数(如下图,图片来源:伯乐在线)

    这种情况下,函数fn1()是以值方式返回到,返回时,首先拷贝temp的值给临时变量。返回到主函数后,用临时变量来初始化引用变量b,使得b成为该临时变量到的别名。由于临时变量的作用域短暂(在C++标准中,临时变量或对象的生命周期在一个完整的语句表达式结束后便宣告结束,也就是在语句float &b=fn1(5.0);之后) ,所以b面临无效的危险,很有可能以后的值是个无法确定的值。

     如果真的希望用函数的返回值来初始化一个引用,应当先创建一个变量,将函数的返回值赋给这个变量,然后再用该变量来初始化引用:

    1 int x=fn1(5.0);
    2 int &b=x;

     case 3:用返回引用的方式调用函数(如下图,图片来源:伯乐在线)

    这种情况下,函数fn2()的返回值不产生副本,而是直接将变量temp返回给主函数,即主函数的赋值语句中的左值是直接从变量temp中拷贝而来(也就是说c只是变量temp的一个拷贝而非别名) ,这样就避免了临时变量的产生。尤其当变量temp是一个用户自定义的类的对象时,这样还避免了调用类中的拷贝构造函数在内存中创建临时对象的过程,提高了程序的时间和空间的使用效率。

    case 4:用函数返回的引用作为新引用的初始化值的方式来调用函数(如下图,图片来源:伯乐在线)

    这种情况下,函数fn2()的返回值不产生副本,而是直接将变量temp返回给主函数。在主函数中,一个引用声明d用该返回值初始化,也就是说此时d成为变量temp的别名。由于temp是全局变量,所以在d的有效期内temp始终保持有效,故这种做法是安全的。

    3.不能返回局部变量的引用。如上面的例子,如果temp是局部变量,那么它会在函数返回后被销毁,此时对temp的引用就会成为“无所指”的引用,程序会进入未知状态。

    4.不能返回函数内部通过new分配的内存的引用。虽然不存在局部变量的被动销毁问题,但如果被返回的函数的引用只是作为一个临时变量出现,而没有将其赋值给一个实际的变量,那么就可能造成这个引用所指向的空间(有new分配)无法释放的情况(由于没有具体的变量名,故无法用delete手动释放该内存),从而造成内存泄漏。因此应当避免这种情况的发生

    5当返回类成员的引用时,最好是const引用。这样可以避免在无意的情况下破坏该类的成员。

    6.可以用函数返回的引用作为赋值表达式中的左值

    复制代码

     1 #include<iostream>
     2 using namespace std;
     3 int value[10];
     4 int error=-1;
     5 int &func(int n){
     6     if(n>=0&&n<=9)
     7         return value[n];//返回的引用所绑定的变量一定是全局变量,不能是函数中定义的局部变量 
     8     else
     9         return error;
    10 }
    11 
    12 int main(){
    13     func(0)=10;
    14     func(4)=12;
    15     cout<<value[0]<<endl;
    16     cout<<value[4]<<endl;
    17     return 0; 
    18 }

    复制代码

    D.用引用实现多态

    在C++中,引用是除了指针外另一个可以产生多态效果的手段。也就是说一个基类的引用可以用来绑定其派生类的实例

    class Father;//基类(父类)
    class Son:public Father{.....}//Son是Father的派生类
    Son son;//son是类Son的一个实例
    Father &ptr=son;//用派生类的对象初始化基类对象的使用

    特别注意:

    ptr只能用来访问派生类对象中从基类继承下来的成员如果基类(类Father)中定义的有虚函数,那么就可以通过在派生类(类Son)中重写这个虚函数来实现类的多态。

     

    三、总结

    1.在引用的使用中,单纯给某个变量去别名是毫无意义的,引用的目的主要用于在函数参数的传递中,解决大块数据或对象的传递效率和空间不如意的问题

    2.用引用传递函数的参数,能保证参数在传递的过程中不产生副本,从而提高传递效率,同时通过const的使用,还可以保证参数在传递过程中的安全性

    3.引用本身是目标变量或对象的别名,对引用的操作本质上就是对目标变量或对象的操作。因此能使用引用时尽量使用引用而非指针

    2.函数传参的三种方式:

         对于自定义函数,我们需要建立实参与形参之间的关系,这种关系可以分为传值调用和传址调用,二者在结果上的区别就是传值调用不能改变实参的值,而传址调用可以改变原实参的值。

    下面以自定义swap函数为例说明:

    一、传值调用:不能改变实参的值,较容易理解,略。

    二、传址调用:

        1、引用传递:(常用)

            注意事项:①、int &a=n;表示给n起了一个别名a;

                             ②、函数内对a的操作及对n进行操作。

            示例代码:

    #include<bits/stdc++.h>
    using namespace std;
    void swap(int &x,int &y)  //*
    {
        int temp;
        temp=x;x=y;y=temp;
    }
    int main()
    {
        int a,b;
        cin>>a>>b;
        swap(a,b);
        cout<<"after swap:"<<a<<" "<<b;
        return 0;
    }
        2、指针传递:

            注意事项:①、int *p(定义一个指针变量),若在定义过程中对指针p赋值(int *p=?),则给p的应是一个地址;在正常语句中,*p=?表示给p所指向的目标变量赋值。

            示例代码:

    #include<bits/stdc++.h>
    using namespace std;
    void swap( int *x,int *y )//定义时将a,b的地址传给指针型变量x和y;
    {
        int temp=*x;//对指针型变量所指向的目标变量a,b赋值;
        *x=*y;
        *y=temp;
    }
    int main()
    {
        int a,b;
        cin>>a>>b;
        swap(&a,&b);  //实参向形参传递的应为指针型变量;
        cout<<"after swap:"<<a<<" "<<b;
        return 0;
     
    }
    ========================================================================

    最后关于数组与指针问题的几点注意事项:

    ①、a[1]表示的是一个数值,*(a+1)表示的是数值;a+1表示的是a[1]的地址(同样&a[1]表示的也是a[1]的地)。

    如:

    #include<bits/stdc++.h>
    using namespace std;
    int a[101];
    int main()
    {
        a[1]=9;
        cout<<"a[1]="<<a[1]<<endl;
        cout<<"adress :a[1]="<<a+1<<endl;
        return 0;
    }

    ②、a表示的是a数组的首地址,即a[0]的地址,如果用指针swap(a,b);那么交换的仅仅是a,b的第一个数。

    ③、指针型变量p+1表示指向数组的下一个同数据类型元素,而不是下一个字节。
    --------------------- 
    作者:不拿金牌不改名 
    来源:CSDN 
    原文:https://blog.csdn.net/qq_41661919/article/details/79467179 
    版权声明:本文为博主原创文章,转载请附上博文链接!

    还可以参考这个链接:https://blog.csdn.net/jiahaowanhao/article/details/79533520

    展开全文
  • python中[–2::–1]什么意思?

    千次阅读 2021-01-27 00:28:55
    nums = [1,2,3,4,5,6,7,8,9,10]print(nums[-2::-1])输出结果:[9, 8, 7, 6, 5, 4, 3, 2, 1]Python完整的切片表达式包含两个“:",用于分隔三个参数(start_index、end_index、step),当只有一个“:”时,默认第三个...
  • 这篇文章主要是考虑到有不少人问我在appium中的一些英文参数、关键字是什么意思,特地查询了些各种资料,自己整理了一番(后来才发现有官方文档 ) 第一类:【appium】appium自动化入门之环境搭建(上) 第二类:...
  • 如果事情适合预测建模,请使用XGboost。XGBoost算法已成为许多数据科学家的终极武器。它是一种高度复杂的算法,功能强大,足以处理各种规则的数据。 使用XGBoost构建模型很容易。但是,使用XGBoost改进模型很...
  • 意思是创建用户并分配权限。格式:create user 用户名 identified by 密码 default tablespace 表空间表;例如:create user study identified by study default tablespace data_test;oracle数据库的主要特点:...
  • 金钱的原则,人力手动调节注意力成本太高,非常值得。For循环或类似于for循环的方法受限于太过分明的层次,不够简洁与灵活,注意力成本高,易出错。本文介绍sklearn模块的GridSearchCV模块,能够在指定的范围内...
  • yolov5训练相关参数解释

    万次阅读 多人点赞 2020-07-13 09:24:25
    hyp:参数咱暂时用上,是指定一些超参数用的(学习率啥的); epochs: 轮数,默认300,需要指定; batch-size:一次喂多少数据,我这内存就能给16,所以可以传按默认16; img-size: 训练和测试数据集的图片...
  • 因为项目中使用到了支付,当前国内支付无非就支付宝、微信、银联这几种方式,支付宝和银联支付今天暂时讨论,搞过的都清楚,支付宝还是比较简单的,相对于前端来说,不会太复杂,几乎接近于完美(这么说人也是做...
  • 该文档里面详细介绍了函数中各个参数意思,以及使用示例,非常清晰。如果看懂英文文档,可以查看文档下半部分的用法说明,我会对该方法的用法进行一个归纳。 首先上一个测试用例: 测试用例 源码 ...
  • 前言 基础库 2.12.0 开发者工具 1.03.2008270 微信小程序的二维码跳转规则 为了方便小程序开发者更便捷地...如原有二维码链接为 http://www.qq.com/a/123456 ,其中12345为业务参数,则可配置规则 http://www.qq.c
  • Logstash 命令行参数

    万次阅读 2017-03-28 19:40:35
    Logstash 提供了一个 shell 脚本叫 logstash 方便快速运行。...事实上你可以写任何具体配置,直接运行 bin/logstash -e ” 达到相同效果。这个参数的默认值是下面这样: input { stdin { } } output { stdout { } }
  • 相信大家都用过LayoutInflater(布局填充器),常用姿势有以下两种。mInflater.inflate(R.layout.item, parent, false);和mInflater.inflate(R.layout.item, null); 最开始我用这个是在...当时也没觉得两种有什么区别
  • c语言中void啥意思

    万次阅读 2021-05-20 06:25:05
    c语言中,void的意思是“无类型”,相应的“void *”为无类型指针,常用在程序编写中对定义函数的参数类型、返回值、函数中指针类型进行声明,其作用是对函数返回和参数的进行限定。c语言中,void的意思是“无类型”...
  • 缓和曲线参数再推导

    千次阅读 2019-07-12 09:14:10
    缓和曲线参数再推导前言一、缓和曲线特性二、缓和曲线参数计算二、缓和曲线参数计算合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格...
  • 关于S参数的一些理解

    万次阅读 多人点赞 2017-10-17 20:55:52
    S参数是表征无源网络特性的一种模型,在仿真中即用S参数来代表无源网络,在射频、微波和信号完整性领域的应用都很广泛。 本文将从S参数的定义,S参数的表达方式,S参数的特性,混合模式S参数,S参数测量等多个方面...
  • 4.0电机参数的含义及测量方法

    千次阅读 多人点赞 2019-11-06 19:47:35
    4.0电机参数的含义及测量方法 1.首先给出workbench中设置电机参数的两张图片: 根据workbench中对电机需要的参数来看,作如下的整理翻译及解释 2.Magnetic structure 电机磁结构 英文名 中文翻译 ...
  • 本篇文章主要讲的是JavaScript中最正常不过的现象——函数参数传递,本篇文章篇幅长,但一定能引发属于你自己的思考!
  • c语言intmain()是什么意思

    千次阅读 2021-05-19 09:01:23
    intmain()表示函数有返回值,那么在main函数结束的时候要有return语句,如果没有就会有warningvoid表示函数没有参数。拓展资料:1、输出函数在主函数里,是以浮点型数据输出的,这个返回值是在屏幕上显示的。而 int ...
  • 伺服增益参数简述及其调整

    千次阅读 2021-01-17 23:13:50
    按照设备需求选择,选择好合适的控制模式后,需要对伺服增益参数进行合理的调整。使得伺服驱动器能快速、准确的驱动电机,最大限度发挥机械性能。伺服增益通过多个参数进行调整,它们之间会相互影响。
  • 本系列几章系统地介绍了开发中Hive常见的用户配置属性(有时称为参数,变量或选项),并说明了哪些版本引入了哪些属性,常见有哪些属性的使用,哪些属性可以进行Hive调优,以及如何使用的问题。以及日常Hive开发中...
  • sqlmap详细参数

    千次阅读 2019-04-06 15:00:47
    一、Sqlmap是什么 Sqlmap是开源的自动化SQL注入工具,由Python写成,具有如下特点: 完全支持MySQL、Oracle、PostgreSQL、Microsoft SQL Server、Microsoft Access、IBM DB2、SQLite、Firebird、Sybase、SAP ...
  • 系统错误null是什么意思 Java中NULL用法的简单示例: public Employee getByName(String name) { int id = database.find(name); if (id == 0) { return null; } return new Employee(id); } 这种方法有什么...
  • 论文中baseline是什么意思

    千次阅读 多人点赞 2021-03-31 10:33:07
    问题1:benchmark和...问题2:经常在论文中看见baseline但是明白里面是在写的什么? 问题3:什么样的paper叫baseline paper ? 问题四:在查找中看到知乎上的一个问题:深度学习论文中的baseline方法如何调参?
  • 计算机术语POST是指的什么意思

    千次阅读 2021-06-25 11:14:24
    2008-03-17 回答postPost()功能将指定...语法Post( handle, messageno, word, long )参数handle:long类型,指定窗口的系统句柄,将向该窗口邮寄消息messageno:UnsignedInteger类型,指定要邮寄的消息号 word:l...
  • Post方式与参数详解

    千次阅读 2018-09-25 09:52:35
    兼容现有的模式,但又想支持json,焦点就是在参数的接收上,让其能够完美的兼容上述两种参数传递,这里可以从HttpMessageConverter着手,这个就是用来将请求的参数映射到spring mvc方法中的实体参数的。...
  • 一、什么是shell脚本? 1、shell的概念: Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁。 Shell 既是一种命令语言,又是一种程序设计语言。 Shell 是指一种应用程序,这个应用程序提供了一...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 234,264
精华内容 93,705
关键字:

参数不完整什么意思