精华内容
下载资源
问答
  • C++编译器符号有哪些内容?

    万次阅读 2015-09-10 11:06:35
    C++编译器符号有哪些内容?很早就想写一篇关于符号表的学习小结,可是迟迟不能下笔。其一是因为符号表在编译器的设计中占有举足轻重的地位【我们在学习编译原理的时候更多的是注重principles,而没有关心一个...

    C++编译器符号表有哪些内容?

    很早就想写一篇关于符号表的学习小结,可是迟迟不能下笔。其一是因为符号表在编译器的设计中占有举足轻重的地位【我们在学习编译原理的时候更多的是注重principles,而没有关心一个编译器的实现,所以符号表讲解的也比较少】,编译阶段的每“遍”都会和符号表打交道,本人只做过一个Mini C的编译器的前端部分,感觉功底不够;其二是因为我想在原来C语言的基础上,增加C++语言符号表的一些知识,对于C++的符号表至今为止我还没有在Internet上找到相关的专门描述,有一本书《C++编程艺术》【英文《The Art of C++》,侯先生将其翻译为《实战C++》】中的第九章作者给出一个Mini C++的例子,但是你会发现这个例子被称为Mini C更为合适,为什么这么说?因为在Mini C++中我们不能看到它是怎么处理访问控制符,不能看到多态,不能看到继承,也不能看到虚函数等。啊,这些正是C++区别于C的部分它都没有体现出来,为什么还叫做Mini C++呢?当然如果你不了解任何一个编译器的实现方式这个例子还是值得你去实验一下,如果你没有这本书也找不到这本书的电子版,可以发邮件给我。

    如果文中有任何错误的地方,拜托你一定要指点我,谢谢了!

    符号表存储的内容有哪些?从编译器来看,符号表与编译的各个阶段都有交互,符号表的内容也会在编译器的不同阶段包含不同的内容【一般来讲,在词法分析,语法分析阶段编译器都是填充符号表,在语义分析阶段更多得操作是从符号表中查询数据,当然还有删除符号表的内容】。一般来讲,符号表有内存地址和函数/变量的对应关系,编译时节点的各种属性(类型,作用域,分配空间大小,(函数)的参数类型)等。对符号表的具体使用方法每个编译器都不同。

    目标文件中的符号表用来输出函数/变量符号信息,供连接时给其他模块引用。这种符号表中主要包含函数/变量的名称和地址对应关系,其中的地址一般是位置无关码。

    【在此在推荐一本好书《Linker & Loader》,目前在网上还能找到中科大的一位博士翻译的中文版本,这本书比较详细的介绍了连接器和加载器的工作原理】

    我们可以想象在编译器中符号表的管理使用可以分为三步:收集符号属性、根据BNF范式进行语法的合法性检查、在生成目标代码阶段使用。

    一、 收集符号属性

    编译程序扫描说明部分收集有关标识符的属性,并在符号表中建立符号的相应属性信息。例如,编译程序分析到下述两个说明语句

    int iVar;;
    
    double fArray[2];

    则在符号表中收集到关于符号iVar的属性是一个整型变量,关于符号fArray的属性是具有2个double元素的一维数组。这只是一个简单说明,我们想看一下符号表具体的数据结构。但目前我还没有找到一个非常完整的描述。那么先来看一下Java的Class文件中找到一些类似存储符号表信息的方法。

    借鉴Java的Class文件结构

    下面的ClassFile结构Class文件的C语言描述【以下内容可参考《Java虚拟机规范》第四章或者网上文章《解读Java Class文件格式》】:

    struct ClassFile
    
    {
    
           u4 magic;                                   // 识别Class文件格式,具体值为0xCAFEBABE,
    
           u2 minor_version;// Class文件格式副版本号,
    
           u2 major_version;// Class文件格式主版本号,
    
           u2 constant_pool_count;// 常数表项个数,
    
           cp_info **constant_pool;// 常数表,又称变长符号表,
    
           u2 access_flags; // Class的声明中使用的修饰符掩码,
    
           u2 this_class; // 常数表索引,索引内保存类名或接口名,
    
           u2 super_class;// 常数表索引,索引内保存父类名,
    
           u2 interfaces_count;// 超接口个数,
    
           u2 *interfaces; // 常数表索引,各超接口名称,
    
           u2 fields_count; // 类的域个数,
    
           field_info **fields;// 域数据,包括属性名称索引,域修饰符掩码等,
    
           u2 methods_count;// 方法个数,
    
           method_info **methods;// 方法数据,包括方法名称索引,方法修饰符掩码等,
    
           u2 attributes_count; // 类附加属性个数,
    
           attribute_info **attributes;// 类附加属性数据,包括源文件名等。
    
    };

    其中u2为unsigned short,u4为unsigned long:

    cp_info **constant_pool是常量表的指针数组,指针数组个数为constant_pool_count,结构体cp_info如下:

    struct cp_info
    
    {
    
           u1 tag;           //常数表数据类型
    
           u1 *info;        //常数表数据
    
    };

    常数表数据类型Tag定义如下:

    #define CONSTANT_Class                        7
    
    #define CONSTANT_Fieldref                     9
    
    #define CONSTANT_Methodref                 10
    
    #define CONSTANT_InterfaceMethodref    11
    
    #define CONSTANT_String                       8
    
    #define CONSTANT_Integer                      3
    
    #define CONSTANT_Float                         4
    
    #define CONSTANT_Long                        5
    
    #define CONSTANT_Double               6
    
    #define CONSTANT_NameAndType          12
    
    #define CONSTANT_Utf8                         1

    每种类型对应一个结构体保存该类型数据,例如CONSTANT_Class 的info指针指向的数据类型应为CONSTANT_Class_info

    struct CONSTANT_Class_info
    
    {
    
           u1 tag;
    
           u2 name_index;
    
    };

    CONSTANT_Utf8的info指针指向的数据类型应为CONSTANT_Utf8_info

    struct CONSTANT_Utf8_info
    
    {
    
           u1 tag;
    
           u2 length;
    
           u1 *bytes;
    
    };

    Tag和info的详细说明参考《Java虚拟机规范》第四章4.4节。

    access_flags为类修饰符掩码,域与方法都有各自的修饰符掩码。

    #define ACC_PUBLIC                         0x0001 
    
    #define ACC_PRIVATE               0x0002
    
    #define ACC_PROTECTED                0x0004
    
    #define ACC_STATIC                        0x0008
    
    #define ACC_FINAL                          0x0010
    
    #define ACC_SYNCHRONIZED          0x0020
    
    #define ACC_SUPER                          0x0020
    
    #define ACC_VOLATILE                    0x0040
    
    #define ACC_TRANSIENT                 0x0080 
    
    #define ACC_NATIVE                        0x0100
    
    #define ACC_INTERFACE                  0x0200 
    
    #define ACC_ABSTRACT                   0x0400 
    
    #define ACC_STRICT                        0x0800

    field_info **fields是类域数据的指针数组,指针数组个数为fields_count,结构体field_info定义如下:

    struct field_info
    
    {
    
           u2 access_flags;     //域修饰符掩码
    
           u2 name_index;      //域名在常数表内的索引
    
           u2 descriptor_index;//域的描述符,其值是常数表内的索引
    
           u2 attributes_count;//域的属性个数
    
           attribute_info **attributes; //域的属性数据,即域的值
    
    };

    例如一个域定义如下:

    private final static byte UNSET=127;

    则该域的修饰符掩码值为:ACC_PRIVATE | ACC_STATIC | ACC_FINAL=0x001A。常数表内name_index索引内保存数据为UNSET,常数表内descriptor_index索引内保存的数据为B(B表示byte, 其他类型参考《Java虚拟机规范》第四章4.3.2节)。attributes_count的值为1,其中attributes是指针数组。指针数组个数为attributes_count,在此为1,attribute_info结构体如下:

    struct attribute_info
    
    {
    
           u2 attribute_name_index; //常数表内索引
    
           u4 attribute_length; //属性长度
    
           u1 *info; //根据属性类型不同而值不同
    
    }; 

    attribute_info可以转换(cast)为多种类型ConstantValue_attribute,Exceptions_attribute,LineNumberTable_attribute,LocalVariableTable_attribute,Code_attribute等。

    因为域的属性只有一种:ConstantValue_attribute,因此此结构体转换为

    struct ConstantValue_attribute
    
    {
    
           u2 attribute_name_index;  //常数表内索引
    
           u4 attribute_length;  //属性长度值,永远为2
    
           u2 constantvalue_index; //常数表内索引,保存域的值
    
    //在此例中,常数表内保存的值为127
    
    };

    method_info **methods是方法数据的指针数组,指针数组个数为methods_count,结构体method_info定义如下:

    struct method_info
    
    {
    
           u2 access_flags;   //方法修饰符掩码
    
           u2 name_index;    //方法名在常数表内的索引
    
           u2 descriptor_index; //方法描述符,其值是常数表内的索引
    
           u2 attributes_count;//方法的属性个数
    
           attribute_info **attributes;//方法的属性数据,
    
    //保存方法实现的Bytecode和异常处理
    
    };

    例如一个方法定义如下:

    public static boolean canAccessSystemClipboard()
    
    {
    
           ...
    
    }

    则access_flags的值为 ACC_PUBLIC | ACC_STATIC =0x0009,常数表内name_index索引内保存数据为canAccessSystemClipboard,常数表内descriptor_index索引内保存数据为()Z;(括号表示方法参数,Z表示返回值为布尔型,详细说明参照《Java虚拟机规范》第四章4.3.2节)。attribute_info **attributes是方法的属性指针数组,个数为attributes_count,数组内保存的是常数表索引,info为Code_attribute或Exceptions_attribute。

    ClassFile结构体中的attribute_info **attributes是附加属性数组指针,个数为attributes_count。

    struct SourceFile_attribute
    
    {
    
           u2 attribute_name_index;//常数表内索引
    
           u4 attribute_length;   //属性长度值,永远为2
    
           u2 sourcefile_index;  //常数表内索引,info保存源文件名
    
    };

    C++的符号表的特别之处

    因为Java的Class文件是一个中间文件,在Class文件中保存的信息是很多的,而且和C++编译器也是不完全相同,那么上面的结构中那些是我们在C++编译器生成的符号表中也必须有的?我想上面的field_info、method_info、namespace和const等在C++的符号表也是需要的,但是这些内容也不完全相同。

    那我们先看以下几个常见的问题,加深C++编译器的理解

    « 符号表如何存储静态变量(包括全局变量和局部变量)和类非静态成员变量有哪些区别?

    对于静态变量,C++编译器的处理方法和Java解释器处理的方法有类似的地方。那就是访问控制符public/private/protected和static一样都会写入符号表;对于非静态成员变量符号表存储的什么内容?有没有这些访问控制符?类的非静态成员在类中的表示是通过偏移量来访问,只有和对象邦定以后才能找到其真实的地址,那么在编译后的目标文件中没有为成员分配地址,访问控制符也就没有写入到目标文件。【注1:为什么访问控制符是在编译期间处理,而不放在运行期间处理?如果要放在运行期间处理必须完成新的连接器和加载器来保证,另外重要的是运行效率的问题】【注2:参考CSDN上一篇文章,一个类的成员变量修改了访问控制符,在另外一个文件被引用,是否必须编译修改的文件才能连接成功?《今天面试碰到的一个以前没有想过的问题(顺便给一点分出去)》】。

    « 符号表如何处理const、volatile变量?

    C++编译器把Const对象放在了符号表之中,C语言一般是放在只读数据区。【为什么C++编译器这么做?我想一个原因就是减少一些存储操作次数】。对于volatile变量我们应该怎么考虑?声明的voliate告诉编译器不能优化它呀,我们只能在符号表中增加标识位来告诉编译器自己不优化volatile变量。

    « 符号表怎么处理虚函数、虚继承、多继承的情况?

    其实对于这些处理在Lippman的《Inside the C++ Object Model》中有详细的介绍。也许你知道那些应该放在符号表中,那些是不能的。例如对于局部静态对象static class obj;的定义那些不需要放在符号表中?【注:局部静态变量只要求在函数执行时初始化一次】,为了保证Obj满足要求,需要附加标志变量,这个附加变量能放在符号表中吗?不能,原因很简单,它需要在运行时控制对象的构造和析构。

    一个多继承,有虚函数的例子:

    struct __mptr
    
    {
    
       int delta;  //多继承下第二个对象要调整的偏移量
    
       int index; //是否为虚函数的标志
    
       union {
    
          ptrtofunc  faddr;
    
          int        v_offset;
    
       };
    
    };

    这个数据应该放在哪里?程序的目标代码中还是符号表中?显然这个数据应该放在符号表之中。当然对于C++编译器的不通现实,数据结构可能是不同的,但是我们知道了那些数据应该放在符号表之中啦。

    C++编译器中对于异常是如何完成的?看看《深入探索C++对象模型》,然后再从汇编上分析,在符号表上的处理并没有很大的技巧。但是对于模板的处理,符号表就要起很大的作用啦。目前大多数C++编译器为了减少模板带来的代码扩张基本上都会增加一次编译,准确的获取要实例化的模板对象,可惜的对此内容知之甚少,不敢乱讲。

    也许你觉得我扯了大半天,也没有告诉你一个符号表的准确结构。真的很抱歉,我也没有设计过C++编译器,当然没有办法给出一个完整的结构,我主要是想探索一下怎么设计一个C++编译器使用的符号表。【对于符号表的管理目前常用的是Hash table,可以参考相关文档设计Hash表以及解决冲突的办法】

    二、 根据BNF范式进行语法的合法性检查

    根据文法,可以识别代码。在此处的语法检查主要指的符号表的检查,是编译器出错处理的一部分。例如我们重复定义变量,其根本原因就是变量信息进入符号表时发现已经存在了数据项【当然这个数据项不是上面所说的Hash冲突】。

    是否有符号表的语义检查?从我看来是没有的,语义是什么?语义指的是代码实际代表的意义,符号表仅仅是符号,没有实际的语义。

    三、 在生成目标代码阶段使用

    我想看过编译原理的都经常看到这样的函数lookup(id),指的是在符号表中查找id对象。在生成目标代码的时候就是要根据程序的语义,不断地查找符号表,然后生成目标代码。

    每个符号变量在目标代码生成时需要确定其在存储分配的位置(主要是相对位置)。语言程序中的符号变量由它被定义的存储类别(如在C、FORTRAN语言中)或被定义的位置(如分程序结构的位置)来确定。首先要确定其被分配的区域。例如,在C++中常见的段(sections)有.bss,.data,.text,.rdata,.init,.fini等【可参考《Linker & Loader》或者自己通过反汇编查看,如通过GCC的Objdump -D ExeFile或者Visual Studio中的dumpbin就可以看到可执行文件中所有的段】。其次是根据变量出现的次序,(一般来说)决定该变量在某个区中所处的具体位置,这通常使用在该区域中相对区头的相对位置确定。而有关区域的标志及相对位置都是作为该变量的语义信息被收集在该变量的符号表属性中。

    四、 后记

    如果在以后有时间有机会的话能够阅读的GCC/G++的源代码(不知道未来的N多年中真的是否有这样的机会),我一定重写这篇文章。但是现在,如果你发现我的错误,一定通知我。如果你也有一些涉及到编译器的问题(C and/or C++ Compiler),我也非常乐意和你一起探讨。

    在此恳请指点文中的错误。

    为了使大家能够看到我所参考的文章,我把他们都重新的放在一起,不仅仅是一个连接。希望没有侵犯原作者的版权吆。

    参考的文章:

    【1】 符号表,http://www.cppblog.com/pengkuny/archive/2006/12/18/16581.html

    【2】 Mini Java编译器,http://blog.csdn.net/sandy_xu/category/106115.aspx

    【3】 今天面试碰到的一个以前没有想过的问题(顺便给一点分出去),http://topic.csdn.net/t/20030227/18/1474390.html

    【4】 Const的思考,http://www.openitpower.com/wenzhang/115/12008_1.html

    【5】 解读Java Class文件格式,http://blog.csdn.net/tyrone1979/archive/2006/07/23/964560.aspx

    推荐读物:

    【1】 编译原理。个人手头有好几本,每本都有他的特点。

    【2】 Linker & Loader,网上可以找到中英文版本。

    【3】 深入探索C++对象模型,网上中英文版本都可以找到。

    本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/abortexit/archive/2007/04/24/1583306.aspx

    展开全文
  • 在编写与vector有关的函数时总是会出现如下警告:--------------------------------------------------------------------警告信息 warning C4018: 有符号/无符号不匹配-------------------------------------------...

     


    在编写与vector有关的函数时总是会出现如下警告:

    --------------------------------------------------------------------

    警告信息 warning C4018: 有符号/无符号不匹配

    --------------------------------------------------------------------

     

    仔细研究之后发现是进行了与v.size()有关的操作引起的,用其中容器中与size()是被定义为unsigned int 型,而我们在遍历时会用int型导致警告的产生,因此在int 前面添加 unsigned即可。

    例子如下:

    -------------------------------------------------------------

    上面的函数中 将for循环中的i设置为unsigned int 型

     for(unsigned int i=0;i<v.size();i++)
    ------------------------------------------------------------

    完整程序如下:

    展开全文
  • <br />在编写与vector有关的函数时总是会出现如下警告: -------------------------------------------------------------------- 警告信息 warning C4018: 有符号/无符号不匹配 -------------...

    在编写与vector有关的函数时总是会出现如下警告:

    --------------------------------------------------------------------

    警告信息 warning C4018: 有符号/无符号不匹配

    --------------------------------------------------------------------

    仔细研究之后发现是进行了与v.size()有关的操作引起的,用其中容器中与size()是被定义为unsigned int 型,而我们在遍历时会用int型导致警告的产生,因此在int 前面添加 unsigned即可。

     

    例子如下:

     

    1. /** 
    2.     求向量中所有元素的交错和 
    3.     @param v欲求交错和的向量 
    4.     @return 返回所有元素的交错和 
    5. */  
    6. double alternating_sum(const vector<double>& v)  
    7. {  
    8.     if(v.size()==0) return false;  
    9.     double sum=0.0;  
    10.     for(unsigned int i=0;i<v.size();i++)  
    11.     {  
    12.         if(i%2==0)  
    13.             sum+=v[i];  
    14.         else sum-=v[i];  
    15.     }  
    16.     return sum;  
    17. }  

     

     

    -------------------------------------------------------------

    上面的函数中 将for循环中的i设置为unsigned int 型

     for(unsigned int i=0;i<v.size();i++)
    ------------------------------------------------------------

     

    展开全文
  • ES6之符号符号属性

    万次阅读 2019-12-16 23:50:02
    在 JS 已的基本类型(字符串、数值、布尔类型、 null 与 undefined )之外, ES6 引入了一种新的基本类型:符号(Symbol)。符号起初被设计用于创建对象私有成员,而这也是 JS 开发者期待已久的特性。在符号诞生...

    在 JS 已有的基本类型(字符串、数值、布尔类型、 null 与 undefined )之外, ES6 引入了一种新的基本类型:符号(Symbol)。符号起初被设计用于创建对象私有成员,而这也是 JS 开发者期待已久的特性。在符号诞生之前,将字符串作为属性名称导致属性可以被轻易访问,无论命名规则如何。而“私有名称”意味着开发者可以创建非字符串类型的属性名称,由此可以防止使用常规手段来探查这些名称。

    “私有名称”提案最终发展成为 ES6中的符号,而本章将会教你如何有效使用它。虽然它只保
    留了实现细节(即:引入了非字符串类型的属性名)而丢弃了私有性意图,但它仍然显著有别于对象的其余属性。

    创建一个符号

    符号没有字面量形式,这在 JS 的基本类型中是独一无二的,有别于布尔类型的 true 或数
    值类型的 42 等等。你可以使用全局 Symbol 函数来创建一个符号值,正如下面这个例子:

    let firstName = Symbol();
    let person = {};
    person[firstName] = "Nicholas";
    console.log(person[firstName]); // "Nicholas"
    
    

    此代码创建了一个符号类型的 firstName 变量,并将它作为 person 对象的一个属性,而每
    次访问该属性都要使用这个符号值。为符号变量适当命名是个好主意,这样你就可以很容易
    地说明它的含义。

    由于符号值是基本类型的值,因此调用 new Symbol() 将会抛出错误。你可以通过 new
    Object(yourSymbol) 来创建一个符号实例,但尚不清楚这能有什么作用。

    Symbol 函数还可以接受一个额外的参数用于描述符号值,该描述并不能用来访问对应属性,
    但它能用于调试,例如:

    let firstName = Symbol("first name");
    let person = {};
    person[firstName] = "Nicholas";
    console.log("first name" in person); // false
    console.log(person[firstName]); // "Nicholas"
    console.log(firstName); // "Symbol(first name)"
    
    

    Symbol是独一无二的,"first name"只是对Symbol的描述

    符号的描述信息被存储在内部属性 [[Description]] 中,当符号的 toString() 方法被显式
    或隐式调用时,该属性都会被读取。在本例中, console.log() 隐式调用了 firstName 变量
    的 toString() 方法,于是描述信息就被输出到日志。此外没有任何办法可以从代码中直接
    访问 [[Description]] 属性。我建议始终应给符号提供描述信息,以便更好地阅读代码或进
    行调试。

    由于符号是基本类型的值,因此你可以使用 typeof 运算符来判断一个变量是否为符
    号。 ES6 扩充了 typeof 的功能以便让它在作用于符号值的时候能够返回 “symbol” ,例如

    let symbol = Symbol("test symbol");
    console.log(typeof symbol); // "symbol"
    
    

    使用符号值

    你可以在任意能使用“需计算属性名”的场合使用,还能在对象的“需计算字面量属性名”中使用符号,此外还可以在合使用符号。Object.defineProperty() 或 Object.defineProperties() 调用中使用它

    共享符号值

    你或许想在不同的代码段中使用相同的符号值,例如:假设在应用中需要在两个不同的对象
    类型中使用同一个符号属性,用来表示一个唯一标识符。跨越文件或代码来追踪符号值是很
    困难并且易错的,为此, ES6 提供了“全局符号注册表”供你在任意时间点进行访问。

    若你想创建共享符号值,应使用 Symbol.for() 方法而不是 Symbol() 方法。 Symbol.for()
    方法仅接受单个字符串类型的参数,作为目标符号值的标识符,同时此参数也会成为该符号
    的描述信息。例如:

    let uid = Symbol.for("uid");
    let object = {};
    object[uid] = "12345";
    console.log(object[uid]); // "12345"
    console.log(uid); // "Symbol(uid)"
    

    Symbol.for() 方法首先会搜索全局符号注册表,看是否存在一个键值为 “uid” 的符号值。
    若是,该方法会返回这个已存在的符号值;否则,会创建一个新的符号值,并使用该键值将
    其记录到全局符号注册表中,然后返回这个新的符号值。这就意味着此后使用同一个键值去
    调用 Symbol.for() 方法都将会返回同一个符号值,就像下面这个例子:

    let uid = Symbol.for("uid");
    let object = {
    [uid]: "12345"
    };
    console.log(object[uid]); // "12345"
    console.log(uid); // "Symbol(uid)"
    let uid2 = Symbol.for("uid");
    console.log(uid === uid2); // true
    console.log(object[uid2]); // "12345"
    console.log(uid2); // "Symbol(uid)"
    

    本例中, uid 与 uid2 包含同一个符号值,因此它们可以互换使用。第一次调用
    Symbol.for() 创建了这个符号值,而第二次调用则从全局符号注册表中将其检索了出来。

    共享符号值还有另一个独特用法,你可以使用 Symbol.keyFor() 方法在全局符号注册表中根
    据符号值检索出对应的键值,例如:

    let uid = Symbol.for("uid");
    console.log(Symbol.keyFor(uid)); // "uid"
    let uid2 = Symbol.for("uid");
    console.log(Symbol.keyFor(uid2)); // "uid"
    let uid3 = Symbol("uid");
    console.log(Symbol.keyFor(uid3)); // undefined
    
    

    符号值的转换

    类型转换是 JS 语言重要的一部分,能够非常灵活地将一种数据类型转换为另一种。然而符号
    类型在进行转换时非常不灵活,因为其他类型缺乏与符号值的合理等价,尤其是符号值无法
    被转换为字符串值或数值。因此将符号作为属性所达成的效果,是其他类型所无法替代的。

    let uid = Symbol.for("uid"),
    desc = String(uid);
    console.log(desc);
    

    String() 方法调用了 uid.toString() 来获取符号的字符串描述信息。但若你想直接将符号
    转换为字符串,则会引发错误:

    let uid = Symbol.for("uid"),
    desc = uid + ""; // 引发错
    

    检索符号属性

    Object.keys() 与 Object.getOwnPropertyNames() 方法可以检索对象的所有属性名称,前者
    返回所有的可枚举属性名称,而后者则返回所有属性名称而无视其是否可枚举。然而两者都
    不能返回符号类型的属性,以保持它们在 ES5 中的功能不发生变化。而 ES6 新增了
    Object.getOwnPropertySymbols() 方法,以便让你可以检索对象的符号类型属性。

    Object.getOwnPropertySymbols() 方法会返回一个数组,包含了对象自有属性名中的符号值,
    例如:

    let uid = Symbol.for("uid");
    let object = {
    [uid]: "12345"
    };
    let symbols = Object.getOwnPropertySymbols(object);
    console.log(symbols.length); // 1
    console.log(symbols[0]); // "Symbol(uid)"
    console.log(object[symbols[0]]); // "12345"
    
    

    使用知名符号暴露内部方法

    ES6 定义了“知名符号”来代表 JS 中一些公共行为,而这些行为此前被认为只能是内部操作。
    每一个知名符号都对应全局 Symbol 对象的一个属性,例如 Symbol.create 。

    这些知名符号是:

    • Symbol.hasInstance :供 instanceof 运算符使用的一个方法,用于判断对象继承关
      系。

    Symbol.hasInstance 方法只接受单个参数,即需要检测的值。如果该值是本函数的一个实

    • Symbol.isConcatSpreadable:一个布尔类型值,在集合对象作为参数传递给Array.prototype.concat() 方法时,指示是否要将该集合的元素扁平化。
    • Symbol.iterator :返回迭代器的一个方法。
    • Symbol.match :供 String.prototype.match() 函数使用的一个方法,用于比较字符串。
    • Symbol.replace :供 String.prototype.replace()函数使用的一个方法,用于替换子字符串。
    • Symbol.search :供 String.prototype.search() 函数使用的一个方法,用于定位子字符串。
    • Symbol.species :用于产生派生对象的构造器。
    • Symbol.split :供 String.prototype.split() 函数使用的一个方法,用于分割字符串。
    • Symbol.toPrimitive :返回对象所对应的基本类型值的一个方法。
    • Symbol.toStringTag :供String.prototype.toString()函数使用的一个方法,用于创建对象的描述信息。
    • Symbol.unscopables :一个对象,该对象的属性指示了哪些属性名不允许被包含在
      with 语句中。

    示例:

    Symbol.hasInstance 属性

    每个函数都具有一个 Symbol.hasInstance 方法,用于判断指定对象是否为本函数的一个实
    例。这个方法定义在 Function.prototype 上,因此所有函数都继承了面对 instanceof 运算
    符时的默认行为。 Symbol.hasInstance 属性自身是不可写入、不可配置、不可枚举的,从而
    保证它不会被错误地重写。

    Symbol.hasInstance 方法只接受单个参数,即需要检测的值。如果该值是本函数的一个实
    例,则方法会返回 true 。为了理解该方法是如何工作的,可研究下述代码:

    obj instanceof Array;
    
    

    这句代码等价于:

    Array[Symbol.hasInstance](obj);
    

    Symbol.isConcatSpreadable

    JS 在数组上设计了 concat() 方法用于将两个数组连接到一起,此处示范了如何使用该方
    法:

    let colors1 = [ "red", "green" ],
    colors2 = colors1.concat([ "blue", "black" ]);
    console.log(colors2.length); // 4
    console.log(colors2); // ["red","green","blue","black"]
    

    Symbol.isConcatSpreadable 属性是一个布尔类型的属性,它表示目标对象拥有长度属性与数
    值类型的键、并且数值类型键所对应的属性值在参与 concat() 调用时需要被分离为个体。
    该符号与其他的知名符号不同,默认情况下并不会作为任意常规对象的属性。它只出现在特
    定类型的对象上,用来标示该对象在作为 concat() 参数时应如何工作,从而有效改变该对
    象的默认行为。你可以用它来定义任意类型的对象,让该对象在参与 concat() 调用时能够
    表现得像数组一样,例如:

    let collection = {
    0: "Hello",
    1: "world",
    length: 2,
    [Symbol.isConcatSpreadable]: true
    };
    let messages = [ "Hi" ].concat(collection);
    console.log(messages.length); // 3
    console.log(messages); // ["hi","Hello","world"]
    
    

    Symbol.match 、 Symbol.replace 、 Symbol.search 与Symbol.split

    在 JS 中,字符串与正则表达式有着密切的联系,尤其是字符串具有几个可以接受正则表达式
    作为参数的方法:

    • match(regex) :判断指定字符串是否与一个正则表达式相匹配;
    • replace(regex, replacement) :对正则表达式的匹配结果进行替换;
    • search(regex) :在字符串内对正则表达式的匹配结果进行定位;
    • split(regex) :使用正则表达式将字符串分割为数组。

    这些与正则表达式交互的方法,在 ES6 之前其实现细节是对开发者隐藏的,使得开发者无法
    将自定义对象模拟成正则表达式(并将它们传递给字符串的这些方法)。而 ES6 定义了 4 个
    符号以及对应的方法,将原生行为外包到内置的 RegExp 对象上。

    这 4 个符号表示可以将正则表达式作为字符串对应方法的第一个参数传入, Symbol.match
    对应 match() 方法, Symbol.replace 对应 replace() , Symbol.search 对应 search()
    , Symbol.split 则对应 split() 。这些符号属性被定义在 RegExp.prototype 上作为默认
    实现,以供对应的字符串方法使用。

    • Symbol.match :此函数接受一个字符串参数,并返回一个包含匹配结果的数组;若匹配
      失败,则返回 null 。
    • Symbol.replace :此函数接受一个字符串参数与一个替换用的字符串,并返回替换后的
      结果字符串。
    • Symbol.search :此函数接受一个字符串参数,并返回匹配结果的数值索引;若匹配失
      败,则返回 -1。
    • Symbol.split :此函数接受一个字符串参数,并返回一个用匹配值分割而成的字符串数
      组。

    在对象上定义这些属性,允许你创建能够进行模式匹配的对象,而无需使用正则表达式,并
    且允许在任何需要正则表达式的方法中使用该对象。这里有一个例子,展示了这些符号的用
    法:

    // 有效等价于 /^.{10}$/
    let hasLengthOf10 = {
        [Symbol.match]: function(value) {
         return value.length === 10 ? [value.substring(0, 10)] : null;
        },
        [Symbol.replace]: function(value, replacement) {
            return value.length === 10 ?
            replacement + value.substring(10) : value;
        },
        [Symbol.search]: function(value) {
            return value.length === 10 ? 0 : -1;
        },
        [Symbol.split]: function(value) {
            return value.length === 10 ? ["", ""] : [value];
        }
    };
    let message1 = "Hello world", // 11 characters
    message2 = "Hello John"; // 10 characters
    let match1 = message1.match(hasLengthOf10),
    match2 = message2.match(hasLengthOf10);
    console.log(match1); // null
    console.log(match2); // ["Hello John"]
    let replace1 = message1.replace(hasLengthOf10, "Howdy!"),
    replace2 = message2.replace(hasLengthOf10, "Howdy!");
    console.log(replace1); // "Hello world"
    console.log(replace2); // "Howdy!"
    let search1 = message1.search(hasLengthOf10),
    search2 = message2.search(hasLengthOf10);
    console.log(search1); // -1
    console.log(search2); // 0
    let split1 = message1.split(hasLengthOf10),
    split2 = message2.split(hasLengthOf10);
    console.log(split1); // ["Hello world"]
    console.log(split2); // ["", ""]
    

    Symbol.toPrimitive

    对于大部分常规对象,“数值模式”依次会有下述行为:

    1. 调用 valueOf() 方法,如果方法返回值是一个基本类型值,那么返回它;
    2. 否则,调用 toString() 方法,如果方法返回值是一个基本类型值,那么返回它;
    3. 否则,抛出一个错误。
      类似的,对于大部分常规对象,“字符串模式”依次会有下述行为:
    4. 调用 toString() 方法,如果方法返回值是一个基本类型值,那么返回它;
    5. 否则,调用 valueOf() 方法,如果方法返回值是一个基本类型值,那么返回它;
    6. 否则,抛出一个错误。
      在多数情况下,常规对象的默认模式都等价于数值模式(只有 Date 类型例外,它默认使用
      字符串模式)。通过定义 Symbol.toPrimitive 方法,你可以重写这些默认的转换行为。

    使用 Symbol.toPrimitive 属性并将一个函数赋值给它,便可以重写默认的转换行为,例如:

    function Temperature(degrees) {
        this.degrees = degrees;
    }
    Temperature.prototype[Symbol.toPrimitive] = function(hint) {
        switch (hint) {
        case "string":
            return this.degrees + "\u00b0"; // 温度符号
        case "number":
            return this.degrees;
        case "default":
            return this.degrees + " degrees";
        }
    };
    let freezing = new Temperature(32);
    console.log(freezing + "!"); // "32 degrees!"
    console.log(freezing / 2); // 16
    console.log(String(freezing)); 
    

    Symbol.toStringTag

    ES6 通过 Symbol.toStringTag 重定义了相关行为,该符号代表了所有对象的一个属性,定义
    了 Object.prototype.toString.call() 被调用时应当返回什么值。对于数组来说,在
    Symbol.toStringTag 属性中存储了 “Array” 值,于是该函数的返回值也就是 “Array” 。同样,你可以在自设对象上定义 Symbol.toStringTag 的值:

    function Person(name) {
        this.name = name;
    }
    Person.prototype[Symbol.toStringTag] = "Person";
    let me = new Person("Nicholas");
    console.log(me.toString()); // "[object Person]"
    console.log(Object.prototype.toString.call(me)); // "[object Person]"
    
    

    Symbol.unscopables

    Symbol.unscopables 符号在 Array.prototype 上使用,以指定哪些属性不允许在 with 语句
    内被绑定。 Symbol.unscopables 属性是一个对象,当提供该属性时,它的键就是用于忽略
    with 语句绑定的标识符,键值为 true 代表屏蔽绑定。以下是数组的 Symbol.unscopables
    属性的默认值:

    // 默认内置在 ES6 中
    Array.prototype[Symbol.unscopables] = Object.assign(Object.create(null), {
        copyWithin: true,
        entries: true,
        fill: true,
        find: true,
        findIndex: true,
        keys: true,
        values: true
    });
    

    总结

    符号是 JS 新引入的基本类型值,它用于创建不可枚举的属性,并且这些属性在不引用符号的
    情况下是无法访问的。

    虽然符号类型的属性不是真正的私有属性,但它们难以被无意修改,因此在需要提供保护以
    防止开发者改动的场合中,它们非常合适。

    你可以为符号提供描述信息以便更容易地辨识它们的值。全局符号注册表允许你使用相同的
    描述信息,以便在不同的代码段中共享符号值,这样相同的符号值就可以在不同位置用于相
    同目的。

    Object.keys() 或 Object.getOwnPropertyNames() 不会返回符号值,因此 ES6 新增了一个
    Object.getOwnPropertySymbols() 方法,允许检索符号类型的对象属性。而你依然可以使用
    Object.defineProperty() 与 Object.defineProperties() 方法对符号类型的属性进行修改。

    “知名符号”使用了全局符号常量(例如 Symbol.hasInstance ),为常规对象定义了一些功
    能,而这些功能原先仅限内部使用。这些符号按规范使用 Symbol. 的前缀,允许开发者通过
    多种方式去修改常规对象的行为。

    展开全文
  • nm 命令 程序符号信息查看

    千次阅读 2017-05-27 08:19:46
    显示关于对象文件、可执行文件以及对象文件库里的符号信息。 语法 nm [ -A ] [ -C ] [ -X {32|64|32_64}] [ -f ] [ -h ] [ -l ] [ -p ] [ -r ] [ -T ] [ -v ] [ -B | ...
  • iOS崩溃堆栈信息符号化解析

    千次阅读 2016-08-24 10:59:09
    最近一段时间,在iOS开发调试过程中以及上线之后,程序经常会出现崩溃...现在网上很多关于解析崩溃堆栈信息符号化的博客,但是大多质量参差不齐,或者有些细节没有注意到。今天总结一下对iOS崩溃符号化的使用和技巧
  • j出错原因分析: detector 是一个Vector容器 ,detecot.size() 在容器说明中 被定义为: unsigned int 类型, 而j是int 类型 所以会出现: 有符号/无符号不匹配 警告 错误改正 : 定义j为unsigned 类型后就可以了 即: for...
  • 符号

    千次阅读 2016-11-03 22:53:29
    符号表是一种供编译器存储关于源程序各种元素信息的数据结构。 符号表的功能包括以下几个方面: 1)收集源程序中各元素的信息。 2)上下文语义相关性检查的依据。 3)存储分配的依据。 实际上,符号...
  • 查看函数库.a函数符号信息

    千次阅读 2017-02-10 14:01:25
    对于一个给定的函数库,nm命令可以列出函数库中定义的所有符号,包括每个符号的值和类型。还可以给出在原程序中这个函数(符号)是在多少行定义的,不过这必须要求编译该函数库的时候加“-l”选项。  关于符号...
  • 11. 符号符号解析

    千次阅读 多人点赞 2018-12-04 21:35:00
    每个可重定位目标模块m都一个符号表,它包含了在m中定义和引用的符号,三种链接器符号: 类型 特征 举例 Global symbols(模块内部定义的全局符号) 由模块m定义并能被其他模块引用的符号。例如,非...
  • 符号表之一:符号属性

    千次阅读 2017-12-10 23:59:06
    符号表贯穿词法分析到语义处理的全过程,是用来存放在程序中出现的符号标识符(变量、函数、过程等)的语义属性在词法分析及语法分析阶段不断积累和更新符号表中的信息,并在词法分析到代码生成甚至动态链接的各个...
  • 机器如何识别有符号数和无符号数? 机器为了简化有符号数的操作引入了补码表示法(大多数PC都这样表示),运算中连同符号位一起运算,而ADD,SUB通常并不区分有符号数和无符号数,而是通过FLAGS中的...
  • dladdr - 获取某个地址的符号信息

    万次阅读 2014-01-22 20:57:41
    名称 dladdr() - 获取某个地址的符号信息 概要 cc [ flag]... cfile ... -ldl [library]... #include dlfcn.h int dladdr(void *address, Dl_info *dlip); 多线程应用信息 该例行程序是线程安全的。 说明 dladdr() ...
  • C# byte转有符号整数

    千次阅读 2019-04-29 15:19:24
    第一反应是想起书本上的理论,无符号数表示范围是多少到多少,有符号数的表示范围是多少到多少,这就把问题复杂化了, 别忘了uint类型: uint raw = (uint)(bytes[7] * 256 + bytes[8]); //本项目的实际情况,用...
  • 符号执行入门

    万次阅读 多人点赞 2018-05-09 08:12:08
    0x00 前言此前阅读了AEG相关的一些文章,发现符号执行可以说是基石,如果不能充分理解符号执行就很难真正深入AEG的研究。于是我找了一些符号执行领域...对于符号执行入门,两篇文章可以参考。其一是2010年David B...
  • 空格符号复制html,空格网名符号复制

    万次阅读 2021-06-14 06:13:25
    空格网名符号复制搜狗输入法,v+1 在按下d 就是空格 :这里空格【】 这里还有一个空格: []弄了,在游戏里就是空白名空格符号复制法一:搜狗输入法将半角转换全角,快捷键(Shift+空格键),按空格(根据:半角空格...
  • VxWorks 符号

    千次阅读 2014-07-22 11:48:41
    符号表初始化 符号表用于建立符号名称、类型和值之间的关系。其中,名称为null结尾的任意字符串;类型为标识各种符号的整数;值为一个字符指针。符号表主要用来作为目标模块加载的基础,但在需要名称和值关联的任何...
  • arcmap的点图层的图例制作中,大小和颜色都是非常重要的信息,如果二者能够结合起来分别代表不同的属性信息,就能把更丰富的信息用更直观的方式体现出来。 方法比较简单,首先是打开属性对话框,点到symbology页。 ...
  • Linux特殊符号

    千次阅读 2019-03-20 15:50:29
    单引号,被单引号括住的内容,被视为单一字符串,引号内如果表示变量的$符号,没有作用,会视为一般符号处理 “ ” 双引号,被双引号括住的内容,被视为单一字符串,它防止通配符扩展,但允许变量扩展 ...
  • 1.符号信息和调试信息 符号信息(symbols)和调试信息(debug info)是由不同段区分的。 使用 readelf -S binfile 可以查看ELF文件的所有段。 调试信息相关的段: # readelf -S a.out | grep debug [27] .debug...
  • 符号化Symbol(符号)体系

    千次阅读 2016-08-18 11:31:58
    符号化Symbol(符号)体系 http://apps.hi.baidu.com/share/detail/23143648# 符号化Symbol(符号)体系 ArcGIS Engine9.3为开发人员提供了32种符号,主要分为三大类符号MarkerSymbol(点符号)、 LineSymbol(线符号)...
  • 符号文件

    千次阅读 2013-05-23 14:03:32
    先不管什么是符号文件,先看看在没有符号文件支持时的情况,下面是VC8.0在没有符号文件支撑时的一个调用堆栈 > DllTwo.dll!003d1cde() [下面的框架可能不正确和/或缺失,没有为 DllTwo.dll 加载...
  • char型,有符号or无符号

    千次阅读 2012-08-31 08:54:26
    下午一个程序测试了好长时间,发现总一个bug导致内存出错。分析到最后,发现是一个char型的变量,当将其减到小于0时,需要将其重新赋值。但问题就出现在了这里,当其小于0时,并没有进入if语句,将其打印出来,...
  • 近日,在调试某一dump文件时,指定并load了自己dll的pdb文件,...当时没有在意,偶然间,在另一台机器通过windbg调试,没想到显示了完整的调用栈,逐怀疑就ntdll.dll的符号引起的,因为windbg自动连接到微软的符号
  • 并不包含调试信息(未加载任何符号)

    万次阅读 2012-06-21 18:07:47
    今天调试一C++程序,按下F5,老是弹出一对话框显示信息:debugging information for 'myproject.exe' cannot be found or does not match. No symbols loaded....符号文件未加载。 起初,我以为是没有生成.pdb文
  • windbg符号路径设置

    千次阅读 2018-08-30 00:23:16
    windbg没有引入符号文件(pdb)会导致系统库上的变量,堆栈等信息无法显示。 0x01 简介 PDB(Program Database),是微软开发的用于存储程序调试信息的文件格式。pdb文件是由源码在编译期生成,存储了源文件名称,...
  • 网上搜到的去年sybase在上海交大的一道笔试题,猛一下还真想不出来怎么整,总是以为正数的时候无法判断,后经实验室一牛师弟点拨,方才编出以下代码,主要利用了无符号数和有符号数相减结果为无符号数的性质。...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 861,008
精华内容 344,403
关键字:

信息符号有哪些