精华内容
下载资源
问答
  • c 结构体的嵌套引用解读

    万次阅读 2013-06-23 11:11:28
    c 结构体的嵌套引用解读 背景:在Linux内核系统中经常能看到如下例1形式的结构体嵌套引用,在代码追踪... 在一个结构内部包含一个类型为该结构体自身的成员是不合法的。 如下:  struct SELF_REF1 { int a; str

    c  结构体的嵌套引用解读

    背景:在Linux内核系统中经常能看到如下例1形式的结构体嵌套引用,在代码追踪、走读的时候令人费解。所以在弄清楚结构体的嵌套调用就很有用处了。

    概念: 

                     1、结构的自引用

            在一个结构内部包含一个类型为该结构体自身的成员是不合法的。

    如下: 

    struct SELF_REF1 {

    int  a;

    struct  SELF_REF1 b;

    int  c;

    };

    引用成员b是另一个完整的结构,其内部还将包含它自己的成员b,这第二个成员又包含一个完整的结构,它还将包括它自己的成员b,这样重复下去永无休止。所以        不合法。

          那么要如何引用自身结构呢,可以使用结构体指针的方式,如下:

          struct  SELF_REF2 {

    int a;

               struct  SELF_REF2 *b;

    int c;

    }

     这个声明和前面那个声明的区别是b现在时一个指针,而不是结构;编译器在结构的长度确定之前就已经知道指针的长度,所以这种自引用是合法的。

    结构体的自引用在实现链表,树等高级的操作时用处很大。

    注意:

    在使用过程中可能出现如下的声明,是不合法的:

    typedef struct {

    int a;

    SELF_REF3 *B;

    int c;

    }SELF_REF3;

    这个声明的目的是为这个结构创建类型名SELF_REF3,但是失败了,类型名直到声明的末尾在定义,所以在结构声明的内部它尚未定义。

                            要解决可以用如下方式:

    typedef struct  SELF_REF3_TAG {

    int a;

    struct SLEF_REF3_TAG *b;

    int c;

    }SELF_REF3;

     

    2、嵌套引用

    在linux 内核中,经常能看到数据结构之间存在依赖关系。也就是说、其中一个结构包含了另一个结构的一个或者多个成员。和自引用一样,至少有一个结构必须在另一个结构的内部以指针的形式存在。

    问题:如果每个结构体都引用了其他结构的标签,哪个结构应该首先声明呢?

    这个问题必须实现不完整声明,它声明一个作为结构体标签的标识符。然后我们可以把这个标签用在不需要知道这个结构长度的声明中,如声明指向这个结构体的指针。

    例:

    struct B;

                    

    struct A {

    struct B *partner;

    >>>>>

    };

    struct B {

    struct A *partner;

    >>>>>>>;

    };



    展开全文
  • 网络上有很多讨论C++ 的“引用”与“指针“的区别的文章,谈到区别,其中有一条:“引用不能为空(NULL),引用必须与合法的存储单元关联,指针则可以是NULL)”,但在实际应用中,有时候为了保持一致性,我们会抛开...

    网络上有很多讨论C++ 的“引用”与“指针“的区别的文章,谈到区别,其中有一条:“引用不能为空(NULL),引用必须与合法的存储单元关联,指针则可以是NULL)”,但在实际应用中,有时候为了保持一致性,我们会抛开这个规则,人为创造出一个“空引用”。

    很多情况下,“空引用”确实可以工作,以致于“引用不能为空”的忠告,被嘲笑为形式主义,仅仅是标准制定者的耸人听闻。一个“空引用”的例子是:

    int * a = NULL;
    int & b = *a;

    于是当访问b的时候,程序异常出现了:

    void f(int & p)
    {
        p = 0;
    }
    f(b);

    当然,可以增加点判断,修正这个问题:

    void f(int & p)
    {
        if (&p) p = 0;
    }
    

    怎么样,是不是有点别扭?但是如果换成成指针,你要输入的字符数是一模一样的:

    void f(int * p)
    {
        if (p) *p = 0;
    } 

     于是,到底是使用“引用”还是“指针”,好像就是智者见智仁者见仁的事情了。

     

    然而,然而。。。。。。

     

    这真的一样吗?

     

    我们来看看复杂一点的例子:

    // test.cpp
    
    #include <iostream>
    
    class A
    {
        int a;
    };
    
    class B
    {
        int b;
    };
    
    class C
        : public A, public B
    {
        int c;
    };
    
    void fb(B & b)
    {
        std::cout << &b << std::endl;
    }
    
    void fb(B * b)
    {
        std::cout << b << std::endl;
    }
    
    int main(int argc, char* argv[])
    {
        C * c = NULL;
    
        fb(c);
    
        fb(*c);
    
        return 0;
    }
    

    编译运行一下看看:

    $ ./test
    0
    0x4

    咦,怎么&b不是0,也就是不是“空引用”了,这时候,即使加上判断,if (&b),也无济于事了。

    大家也许注意到了,上面是linux环境运行,那么windows环境呢:

    >test.exe
    00000000
    00000000

    这时候,“空引用”保持了他的“空”属性,仅在windows平台做C++的开发者,可以松口气了。

    这是怎么回事呢,是你的眼睛欺骗了你?也许是,但是CPU不会欺骗我们,从汇编代码可以看出本质。下面是linux平台编译的代码:

    Dump of assembler code for function main:
       0x0804870a <+0>:     push   %ebp
       0x0804870b <+1>:     mov    %esp,%ebp
       0x0804870d <+3>:     and    $0xfffffff0,%esp
       0x08048710 <+6>:     sub    $0x20,%esp
       0x08048713 <+9>:     movl   $0x0,0x1c(%esp)
       0x0804871b <+17>:    cmpl   $0x0,0x1c(%esp)
       0x08048720 <+22>:    je     0x804872b <main+33>
       0x08048722 <+24>:    mov    0x1c(%esp),%eax
       0x08048726 <+28>:    add    $0x4,%eax
       0x08048729 <+31>:    jmp    0x8048730 <main+38>
       0x0804872b <+33>:    mov    $0x0,%eax
       0x08048730 <+38>:    mov    %eax,(%esp)
       0x08048733 <+41>:    call   0x80486df <fb(B*)>
       0x08048738 <+46>:    mov    0x1c(%esp),%eax
       0x0804873c <+50>:    add    $0x4,%eax
       0x0804873f <+53>:    mov    %eax,(%esp)
       0x08048742 <+56>:    call   0x80486b4 <fb(B&)>
       0x08048747 <+61>:    mov    $0x0,%eax
       0x0804874c <+66>:    leave  
       0x0804874d <+67>:    ret    
    

    这是windows平台的:

    wmain:
    004114D0  push        ebp  
    004114D1  mov         ebp,esp 
    004114D3  sub         esp,0DCh 
    004114D9  push        ebx  
    004114DA  push        esi  
    004114DB  push        edi  
    004114DC  lea         edi,[ebp-0DCh] 
    004114E2  mov         ecx,37h 
    004114E7  mov         eax,0CCCCCCCCh 
    004114EC  rep stos    dword ptr es:[edi] 
    004114EE  mov         dword ptr [c],0 
    004114F5  mov         eax,dword ptr [c] 
    004114F8  mov         dword ptr [rc],eax 
    004114FB  cmp         dword ptr [c],0 
    004114FF  je          wmain+3Fh (41150Fh) 
    00411501  mov         eax,dword ptr [c] 
    00411504  add         eax,4 
    00411507  mov         dword ptr [ebp-0DCh],eax 
    0041150D  jmp         wmain+49h (411519h) 
    0041150F  mov         dword ptr [ebp-0DCh],0 
    00411519  mov         ecx,dword ptr [ebp-0DCh] 
    0041151F  push        ecx  
    00411520  call        fb (411118h) 
    00411525  add         esp,4 
    00411528  cmp         dword ptr [rc],0 
    0041152C  je          wmain+6Ch (41153Ch) 
    0041152E  mov         eax,dword ptr [rc] 
    00411531  add         eax,4 
    00411534  mov         dword ptr [ebp-0DCh],eax 
    0041153A  jmp         wmain+76h (411546h) 
    0041153C  mov         dword ptr [ebp-0DCh],0 
    00411546  mov         ecx,dword ptr [ebp-0DCh] 
    0041154C  push        ecx  
    0041154D  call        fb (41108Ch) 
    00411552  add         esp,4 
    00411555  xor         eax,eax 
    00411557  pop         edi  
    00411558  pop         esi  
    00411559  pop         ebx  
    0041155A  add         esp,0DCh 
    00411560  cmp         ebp,esp 
    00411562  call        @ILT+345(__RTC_CheckEsp) (41115Eh) 
    00411567  mov         esp,ebp 
    00411569  pop         ebp  
    0041156A  ret              
    

    汇编代码有兴趣自己研究,不细说了。


    回过头想想,两个平台的编译器的两种处理方式,都有他的合理性,windows平台增加了容错性,而linux平台在处理引用时减少判断,增加性能。这隐隐体现出windows与linux开发理念的不同。

    最后,请大家记住,引用不能为空,如果可能存在空对象时,请使用指针。
    展开全文
  • 网络上有很多讨论C++的“引用”与“指针“的区别的文章,谈到区别,其中有一条:“引用不能为空(NULL),引用必须与合法的存储单元关联,指针则可以是NULL”,但在实际应用中,有时候为了保持一致性,我们会抛开这...

           网络上有很多讨论C++ 的“引用”与“指针“的区别的文章,谈到区别,其中有一条:“引用不能为空(NULL),引用必须与合法的存储单元关联,指针则可以是NULL”,但在实际应用中,有时候为了保持一致性,我们会抛开这个规则,人为创造出一个“空引用”。

           很多情况下,“空引用”确实可以工作,以致于“引用不能为空”的忠告,被嘲笑为形式主义,仅仅是标准制定者的耸人听闻。一个“空引用”的例子是:

    int * a = NULL;
    int & b = *a;

           于是当访问b的时候,程序异常出现了:

    void f(int & p)
    {
        p = 0;
    }
    f(b);

            当然可以通过增加判断,修正这个问题:

    void f(int & p)
    {
        if(&p) //地址非空
           p = 0;
    }

           怎么样,是不是有点别扭?但是如果换成指针,你要输入的字符数是一模一样的:

    void f(int * p)
    {
        if(p) 
          *p = 0;
    } 

            于是,到底是使用“引用”还是“指针”,好像就是智者见智仁者见仁的事情了。然而这真的一样吗? 我们来看看复杂一点的例子:

    // test.cpp
    #include <iostream> 
    class A
    {
        int a;
    };
    class B
    {
        int b;
    };
    class C: public A, public B
    {
        int c;
    }; 
    void fb(B & b)
    {
        std::cout << &b << std::endl;
    }
    void fb(B * b)
    {
        std::cout << b << std::endl;
    }
    int main(int argc, char* argv[])
    {
        C * c = NULL;
        fb(c);
        fb(*c);
        return 0;
    }

      linux环境编译运行一下看看:

    $ ./test
    0
    0x4

     怎么&b不是0,也就是不是“空引用”了,这时候,即使加上判断,if (&b),也无济于事了!

     windows环境环境编译运行一下,结果见下图。这时候“空引用”保持了他的“空”属性,仅在windows平台做C++的开发者可以松口气了。

    >test.exe
    00000000
    00000000

    <1> 两个平台编译器的两种处理方式都有它的合理性,windows平台增加了容错性,而linux平台在处理引用时减少判断,增加性能。这隐隐体现出windows与linux开发理念的不同。

    <2> 谨记:引用不能为空,如果可能存在空对象时,请使用指针。

    参考文章:

     

     

     

    展开全文
  • 函数模板函数模板不是一个具体的函数,编译器能为其生成可执行代码。定义函数模板后只是一个对函数功能框架的描述,当他具体执行时,将根据传递的实际参数决定其功能。函数模板的定义template //template 关键字 ...
    cf3502f8c3104472354cb7b5fc75a57c.png

    函数模板

    函数模板不是一个具体的函数,编译器不能为其生成可执行代码。定义函数模板后只是一个对函数功能框架的描述,当他具体执行时,将根据传递的实际参数决定其功能。

    函数模板的定义

    template  //template 关键字 定义模板返回类型 函数名 (形式参数表){... //函数体}

    注:

    • <> : 表示模板参数(模板类型参数【class /typedef + 自定义合法标识符】和模板非类型参数【通常为一个常数】)
    ca5df40f948cc4e5ce8c55a0b53d2d14.png

    函数模板的声明

    可分两部分:template部分函数名部分

    template  //T 表示泛型类型void func(T t) { ... //函数实现 (函数体)}

    定义求和函数模板

    template  //定义模板类型 type Sum(type x ,type y) //定义函数模板{return x+y ;}int ir= Sum(5,10); //调用函数模板,实现整数相加double dr =Sum(5.5,10.5) ; //调用函数模板 ,实现实数相加//int/double ir =Sum(5.5 , 10 ) ; //错误:编译器产生歧义,类型必须统一int ir = Sum(5.5,10);//显示标识模板类型,避免歧义。
    f233c7cd9d9dbd8159893a936c57519b.png

    模板函数

    即用函数模板生成实际可执行的函数。

    区别:

    • 函数模板是一个“框架”,他不是真正可以编译生成代码的程序。
    • 模板函数是把函数模板中的类型参数实例化后生成的函数,它和普通函数本质是相同的,可以生成可执行的代码。

    函数模板的作用

    函数模板代替普通函数实现抽象操作,减少重复劳动和维护、调试开销。

    4af59ba9761a42e8f3b2e72b16d1cdee.png

    宏定义

    理解:

    宏定义又称宏替换,简称“宏”。宏定义是指在 程序中出现一个宏定义的宏名时,在该程序编译之前,先将宏名用被定义的字符串替换,之后才能进行编译。此替换过程称为宏定义。

    标识符:

    #define

    简单宏定义:

    #define  //例如: #define PI 3.141592653 // 把程序中出现的PI全部换成3.141592653 然后编译#define AUTHOR "JianYunFeng" //“”内的内容不会被替换,且字符串双引号必须完整 --注1。#define 0X 12ab //错误 ,宏名必须为合法的用户标识符(可为关键字)--注2

    注:

    1. 宏名一般用大写
    2. 宏定义末尾不加分号
    3. 宏定义写在函数的外面,作用域为其后的程序,通常在文件的开头。
    4. 可以用#undef命令终止宏定义的作用域。
    5. 宏定义可以嵌套。
    6. 字符串“”中不能嵌套宏定义
    214ffedef8f9b476e810279ca943e2c4.png

    CPP编译过程

    带参宏定义:

     #define  () (宏体)//例如:#define J(x,y,z) ((x)^(x)+(y)^(y)+(z)^(z))

    注:

    1. 宏名和参数的括号间不能有空格
    2. 宏定义只做替换,不做计算及求解
    3. 宏的替换过程不存在类型,也没有类型转换。

    优点:

    1. 使用宏可提高程序的通用性和易读性,减少不一致性,减少输入错误和便于修改
    2. 宏定义不分配内存,但是变量定义分配内存,可以节省内存空间。
    3. 函数只有一个返回值,利用宏规则可以设法得到多个值
    4. 宏展开不占用运行时间,只占用编译时间,函数调用占用运行时间(包括分配内存、保留现场、值传递、返回值)

    缺点:

    • 宏展开是源程序变长,函数调用不会。
    db1f66e91a6470250abe003a5a4622e0.png

    宏定义中的#的用法

    作用:

    #用于给参数添加双引号“”。

    语法:

    #define STR (str) #str//例如:#define STR(ME#AUTHOR) me#author //添加 ----替换后--- "me#author“

    注:

    1. #define STR( ) ) /STR( , ) 等 ,) 和 , 都不会被当做参数。
    2. 参数过多,编译器会自动省去
    31ea1f3a85f429a344854faa02a71169.png

    宏定义中的##的用法

    作用:

    ##用于左边加引用

    语法:

    #define STR(str) L##str // -- 翻译过后--Lstr//例如:STR("I love U") ---结果: L"I love U"#define FUN(a,b) vo##a##b()// FUN(id ma ,in) --结果为: void main()

    宏定义中# 和## 的用法

    1.使用#把宏参数变为一个字符串,用##把两个宏参数贴合在一起。

    2.合并匿名变量

    3.填充结构

    4.记录文件名

    5.得到一个数值类型所对应的字符串缓冲大小

    本文部分内容参考至网络,如有错误,敬请指正,如有侵权,请联系修改,如有疑问,欢迎提出讨论,谢谢。

    展开全文
  • 知识点: (1)一堆数组定义的一般形式为:  类型名 数组名 [数组长度];...(3)C语言规定,只能引用单个的数组元素,而嫩一次引用整个数组。 (4)数组元素的引用要指定下标,形式为:  数组名[下标]...
  • 一.一维数组的定义 具有一个下标的数组称为一维数组 定义一维数组的形式如下: 数据类型 数组名 [常量表达式] ...c++语言规定能一次引用整个数组,只能逐个引用数组中的各个元素。 形式如下: 数组
  • Java的方法概念

    2019-08-04 10:40:13
    方法: 方法就是具有特定功能的代码块。 特定: ...返回值类型:基本数据类型|引用数据类型 方法名:合法的标识符 形式参数:参数可以没有,如果有可以是多个,这个参数是局部变量的声明 语句体:解决...
  • HTML5

    2020-05-17 23:30:13
    H5标签变化 DOCTYPE声明 定义和用法 – ...+ctrl+e —> 生成html5的标准形式 ...DTD – 文档类型定义 1、DTD:可定义合法的XML文档构建模块,它使用一系列的合法...3、HTML5:不再基于SGML,所以需要再次引用DTD (xml:
  • 认识程序的入口main

    2020-12-10 16:09:24
    *返回值类型可以是任何一种数据类型,包括引用数据类型 2.3方法名 *方法名首字母要求小写,后面每个单词首字母大写 *方法名最好见名知意 *只要是合法的标识符就可以 2.4形式参数列表 简称形参 *形参是局部变量:int ...
  • HTML5标签变化

    2020-05-16 09:20:14
    DOCTYPE声明 定义和用法 – ...!+ctrl+e —> 生成html5的标准形式 ...DTD – 文档类型定义 1、DTD:可定义合法的XML文档构建模块,它...3、HTML5:不再基于SGML,所以需要再次引用DTD (xml:扩展标记语言) <met
  • 复习c++之各种const

    2019-12-24 19:43:31
    常指针 ...此时只是指此指针指向的是一个常量,所以该指针还可以指向其他的成员(包括非常量)但需要注意的是通过该指针修改该变量的值是不合法的,包括非常量 常引用 引用被定义只是就需要被初...
  • 从零单排c++ primer(16)

    千次阅读 2014-04-27 18:38:36
    (1)基类的指针或引用的静态类型可能与其动态类型一致。 (2)之所以存在派生类向基类的类型转换是因为每个派生类对象都包含一个基类部分,而基类的引用或指针可以绑定到该基类部分上。 (3)一个基类的对象既...
  • 在JavaScript中,任何合法的函数都可以作为对象的构造函数,这既包括系统内置函数,也包括用户自己定义的函数。一旦函数被作为构造函数执行,它内部的this属性将引用函数本身。 通常来说,构造函数没有返回值,它们...
  • 请编写一个类的方法addSum,能实现1+…+100的相加之和。要求addSum方法中的参数类型分别为:值参数、引用参数和输出参数三种不同形式。...,如索引值出现不合法的时候,返回值统一为0. using Syste.
  • javase--方法

    2020-09-23 15:30:27
    方法 方法的定义 方法的定义是什么?语法机制是什么? [修饰符列表] 返回值类型 方法名(形式参数列表){ 方法体;...[]符号叫做中括号,以上中括号搜[]...类型包括基本数据类型和引用数据类型,返回值类型可以是:byte sho
  • 类属性 和 类方法 属于类对象 而不是这个类的某一个实例对象 该类所有的实例对象共享一份 直接可以通过类名.的形式进行访问 类方法第一个参数为cls ...能对赋值数据进行检验合法性 如果用set和get方法话 无疑对调用造
  • C\C++头文件的作用

    2015-05-16 09:48:32
    头文件的作用有三: ...二者,头文件中的函数接口和全局变量起占位符的作用和参数类型与表达式匹配的合法行检测的作用,早期的C编译器具有后者的功能。 三者,便于一二进制的形式发行类库,因此源码实现的细节。
  • 第三章 方法的简介

    2018-08-05 17:20:15
    1.方法名是合法的标识符即可 2.方法的返回值类型,是java语言中任意一种数据类型(基本数据类型和引用数据类型) 3.如果该方法执行完毕以后,没有任何返回值。返回值类型为void 4.方法的形式参数列表,可以有参数...
  • 一般形式 类型说明符 数组名[常量表达式]; int a[10];---------a为数组名,此数组包含了10个整型元素 数组名的命名和变量相同,遵循标识符的规则–由字母,数字,下划线组成 []中的常量用来表示数组长度 注意---...
  • 构造函数是否有new的区别

    千次阅读 2017-02-17 13:27:29
    在JavaScript中,任何合法的函数都可以作为对象的构造函数,这既包括系统内置函数,也包括用户自己定义的函数。一旦函数被作为构造函数执行,它内部的this属性将引用函数本身。通常来说,构造函数没有返回值,它们...
  • 内容自行百度 ...二者,头文件中的函数接口和全局变量起占位符的作用和参数类型与表达式匹配的合法行检测的作用,早期的C编译器具有后者的功能。 三者,便于一二进制的形式发行类库,因此源码实...
  • 箸作权转让协议书.doc

    2020-12-27 09:59:59
    箸作权转让协议书 甲方: ...除《中华人民共和国著作权法》第二十二条规定的情况外,在本合同第3条中转让的权利,甲方不得再许可他人以任何形式使用,但甲方可以在其非商业盈利用途引用该玻璃设计作品中部分内容...
  • 1、单引号字符串和转义引用 双引号或单引号字符串里面可以包含另外一种符号,需要转义,否则用\转义。   2、拼接字符串 挨着写两个字符串会自动连接 【只限于字符串本身,变量不行】 加法运算可以用于拼接...
  • C++实验二.docx

    2020-05-22 20:28:40
    如果是上行,则选择输入的楼层号不能比当前楼层号小,否则应给出不合法提示。 (3). 如果是下行,则选择输入的楼层号不能比当前楼层号大,否则应给出不合法提示。 (4).电梯一旦开始运作就会始终运行,直到窗口...
  • 构造函数--转载

    2014-09-23 18:44:00
    在JavaScript中,任何合法的函数都可以作为对象的构造函数,这既包括系统内置函数,也包括用户自己定义的函数。一旦函数被作为构造函数执行,它内部的this属性将引用函数本身。 通常来说,构造函数没有返回值,它们...
  • C++语言程序设计 第三章 函数调用: 调用函数前要声明函数...3.实参类型必须与形参相符:如果相符,编译器会先进行类型转换,判断是否合法 4.值传递可以是传递参数值,即单向传递 5.引用传递可以实现双向传递 6.常
  • 数组的使用

    2011-11-09 02:19:27
    数组元素的类型可以是任何合法的Java类型,包括数组类型,也就是说Java支持了由数组组成的数组,它提供了多种数组类型,但Java支持矩阵形式的多维数组数组类型数组类型是一种和class一样的引用类型。数组类型的...
  • JS中的构造函数解析

    千次阅读 2010-11-02 14:56:00
    在JavaScript中,任何合法的函数都可以作为对象的构造函数,这既包括系统内置函数,也包括用户自己定义的函数。一旦函数被作为构造函数执行,它内部的this属性将引用函数本身。 通常来说,构造函数没有...
  • 由于zio可以在多种场合(如网页开发、app、小程序、客户端、传统设计)中引用,所以不同的软件引用方法各相同,这里仅以在网页上引用为例,其引用规范为: <link rel="stylesheet" href="css/zico.min.css" >...

空空如也

空空如也

1 2 3 4
收藏数 76
精华内容 30
关键字:

引用形式不合法