-
c 结构体的嵌套引用解读
2013-06-23 11:11:28c 结构体的嵌套引用解读 背景:在Linux内核系统中经常能看到如下例1形式的结构体嵌套引用,在代码追踪... 在一个结构内部包含一个类型为该结构体自身的成员是不合法的。 如下: struct SELF_REF1 { int a; strc 结构体的嵌套引用解读
背景:在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++ 中“空引用”与“空指针”的区别
2013-08-21 10:48:23网络上有很多讨论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++程序员应了解的那些事(85)C++空引用”与“空指针”的区别
2020-12-10 21:33:03网络上有很多讨论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> 谨记:引用不能为空,如果可能存在空对象时,请使用指针。
-
引用另一模板的宏_函数模板及宏定义简单聊
2020-12-02 17:13:51函数模板函数模板不是一个具体的函数,编译器不能为其生成可执行代码。定义函数模板后只是一个对函数功能框架的描述,当他具体执行时,将根据传递的实际参数决定其功能。函数模板的定义template //template 关键字 ...函数模板
函数模板不是一个具体的函数,编译器不能为其生成可执行代码。定义函数模板后只是一个对函数功能框架的描述,当他具体执行时,将根据传递的实际参数决定其功能。
函数模板的定义
template //template 关键字 定义模板返回类型 函数名 (形式参数表){... //函数体}
注:
- <> : 表示模板参数(模板类型参数【class /typedef + 自定义合法标识符】和模板非类型参数【通常为一个常数】)
函数模板的声明
可分两部分: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);//显示标识模板类型,避免歧义。
模板函数
即用函数模板生成实际可执行的函数。
区别:
- 函数模板是一个“框架”,他不是真正可以编译生成代码的程序。
- 模板函数是把函数模板中的类型参数实例化后生成的函数,它和普通函数本质是相同的,可以生成可执行的代码。
函数模板的作用
函数模板代替普通函数实现抽象操作,减少重复劳动和维护、调试开销。
宏定义
理解:
宏定义又称宏替换,简称“宏”。宏定义是指在 程序中出现一个宏定义的宏名时,在该程序编译之前,先将宏名用被定义的字符串替换,之后才能进行编译。此替换过程称为宏定义。
标识符:
#define
简单宏定义:
#define //例如: #define PI 3.141592653 // 把程序中出现的PI全部换成3.141592653 然后编译#define AUTHOR "JianYunFeng" //“”内的内容不会被替换,且字符串双引号必须完整 --注1。#define 0X 12ab //错误 ,宏名必须为合法的用户标识符(可为关键字)--注2
注:
- 宏名一般用大写
- 宏定义末尾不加分号
- 宏定义写在函数的外面,作用域为其后的程序,通常在文件的开头。
- 可以用#undef命令终止宏定义的作用域。
- 宏定义可以嵌套。
- 字符串“”中不能嵌套宏定义
CPP编译过程
带参宏定义:
#define () (宏体)//例如:#define J(x,y,z) ((x)^(x)+(y)^(y)+(z)^(z))
注:
- 宏名和参数的括号间不能有空格
- 宏定义只做替换,不做计算及求解
- 宏的替换过程不存在类型,也没有类型转换。
优点:
- 使用宏可提高程序的通用性和易读性,减少不一致性,减少输入错误和便于修改
- 宏定义不分配内存,但是变量定义分配内存,可以节省内存空间。
- 函数只有一个返回值,利用宏规则可以设法得到多个值
- 宏展开不占用运行时间,只占用编译时间,函数调用占用运行时间(包括分配内存、保留现场、值传递、返回值)
缺点:
- 宏展开是源程序变长,函数调用不会。
宏定义中的#的用法
作用:
#用于给参数添加双引号“”。
语法:
#define STR (str) #str//例如:#define STR(ME#AUTHOR) me#author //添加 ----替换后--- "me#author“
注:
- #define STR( ) ) /STR( , ) 等 ,) 和 , 都不会被当做参数。
- 参数过多,编译器会自动省去
宏定义中的##的用法
作用:
##用于左边加引用
语法:
#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.得到一个数值类型所对应的字符串缓冲大小
本文部分内容参考至网络,如有错误,敬请指正,如有侵权,请联系修改,如有疑问,欢迎提出讨论,谢谢。
-
作业八 一堆数组的应用
2016-11-16 10:59:00知识点: (1)一堆数组定义的一般形式为: 类型名 数组名 [数组长度];...(3)C语言规定,只能引用单个的数组元素,而不嫩一次引用整个数组。 (4)数组元素的引用要指定下标,形式为: 数组名[下标]... -
一维数组,字符数组与字符串
2020-07-30 07:52:03一.一维数组的定义 具有一个下标的数组称为一维数组 定义一维数组的形式如下: 数据类型 数组名 [常量表达式] ...c++语言规定不能一次引用整个数组,只能逐个引用数组中的各个元素。 形式如下: 数组 -
Java的方法概念
2019-08-04 10:40:13方法: 方法就是具有特定功能的代码块。 特定: ...返回值类型:基本数据类型|引用数据类型 方法名:合法的标识符 形式参数:参数可以没有,如果有可以是多个,这个参数是局部变量的声明 语句体:解决... -
HTML5
2020-05-17 23:30:13H5标签变化 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:14DOCTYPE声明 定义和用法 – ...!+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)一个基类的对象既... -
JS中的构造函数详细解析
2021-01-19 16:50:41在JavaScript中,任何合法的函数都可以作为对象的构造函数,这既包括系统内置函数,也包括用户自己定义的函数。一旦函数被作为构造函数执行,它内部的this属性将引用函数本身。 通常来说,构造函数没有返回值,它们... -
C#练习(编写一个类的方法addSum,通过索引指示器的方式来访问类中的数据成员,实现同一个委托分别调用两个...
2020-12-07 20:32:07请编写一个类的方法addSum,能实现1+…+100的相加之和。要求addSum方法中的参数类型分别为:值参数、引用参数和输出参数三种不同形式。...,如索引值出现不合法的时候,返回值统一为0. using Syste. -
javase--方法
2020-09-23 15:30:27方法 方法的定义 方法的定义是什么?语法机制是什么? [修饰符列表] 返回值类型 方法名(形式参数列表){ 方法体;...[]符号叫做中括号,以上中括号搜[]...类型包括基本数据类型和引用数据类型,返回值类型可以是:byte sho -
python中的类属性,类方法和静态方法
2020-08-30 15:08:17类属性 和 类方法 属于类对象 而不是这个类的某一个实例对象 该类所有的实例对象共享一份 直接可以通过类名.的形式进行访问 类方法第一个参数为cls ...不能对赋值数据进行检验合法性 如果用set和get方法话 无疑对调用造 -
C\C++头文件的作用
2015-05-16 09:48:32头文件的作用有三: ...二者,头文件中的函数接口和全局变量起占位符的作用和参数类型与表达式匹配的合法行检测的作用,早期的C编译器不具有后者的功能。 三者,便于一二进制的形式发行类库,因此源码实现的细节。 -
第三章 方法的简介
2018-08-05 17:20:151.方法名是合法的标识符即可 2.方法的返回值类型,是java语言中任意一种数据类型(基本数据类型和引用数据类型) 3.如果该方法执行完毕以后,没有任何返回值。返回值类型为void 4.方法的形式参数列表,可以有参数... -
利用数组处理批量数据
2019-11-13 16:24:55一般形式 类型说明符 数组名[常量表达式]; int a[10];---------a为数组名,此数组包含了10个整型元素 数组名的命名和变量相同,遵循标识符的规则–由字母,数字,下划线组成 []中的常量用来表示数组长度 注意---... -
构造函数是否有new的区别
2017-02-17 13:27:29在JavaScript中,任何合法的函数都可以作为对象的构造函数,这既包括系统内置函数,也包括用户自己定义的函数。一旦函数被作为构造函数执行,它内部的this属性将引用函数本身。通常来说,构造函数没有返回值,它们... -
头文件.h的应用以及fortran和c的混合编程
2014-10-30 08:59:00内容自行百度 ...二者,头文件中的函数接口和全局变量起占位符的作用和参数类型与表达式匹配的合法行检测的作用,早期的C编译器不具有后者的功能。 三者,便于一二进制的形式发行类库,因此源码实... -
箸作权转让协议书.doc
2020-12-27 09:59:59箸作权转让协议书 甲方: ...除《中华人民共和国著作权法》第二十二条规定的情况外,在本合同第3条中转让的权利,甲方不得再许可他人以任何形式使用,但甲方可以在其非商业盈利用途引用该玻璃设计作品中部分内容... -
Beginning Python 笔记学API —— Chapter1 字符串
2013-02-16 13:51:441、单引号字符串和转义引用 双引号或单引号字符串里面可以包含另外一种符号,不需要转义,否则用\转义。 2、拼接字符串 挨着写两个字符串会自动连接 【只限于字符串本身,变量不行】 加法运算可以用于拼接... -
C++实验二.docx
2020-05-22 20:28:40如果是上行,则选择输入的楼层号不能比当前楼层号小,否则应给出不合法提示。 (3). 如果是下行,则选择输入的楼层号不能比当前楼层号大,否则应给出不合法提示。 (4).电梯一旦开始运作就会始终运行,直到窗口... -
构造函数--转载
2014-09-23 18:44:00在JavaScript中,任何合法的函数都可以作为对象的构造函数,这既包括系统内置函数,也包括用户自己定义的函数。一旦函数被作为构造函数执行,它内部的this属性将引用函数本身。 通常来说,构造函数没有返回值,它们... -
C++语言程序设计——第三章
2020-06-25 17:31:44C++语言程序设计 第三章 函数调用: 调用函数前要声明函数...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" >...
-
js中forEach的异步问题
-
智慧公园-源码
-
qiushui-sir.github.io-源码
-
基于中文微博特征主题模型的热点话题提取
-
PowerBI重要外部工具详解
-
python命名规范
-
用户交互Scanner
-
分析文件-源码
-
网站:Sheboygan Falls小学的猎鹰家庭-源码
-
用vue开发的小购物商城(购物车)踩坑日记(属于自己能看懂系列纯当做笔记)。
-
1056 暂未理解
-
基于微信的同城小程序、校园二手交易小程序 毕业设计毕设源码使用教程
-
国际象棋系统-源码
-
点名系统
-
2021年 系统分析师 系列课
-
基于python的dango框架购物商城毕业设计毕设源代码使用教程
-
测试-源码
-
MaxScale 实现 MySQL 读写分离与负载均衡
-
激光雷达点云数据截面提取计算方法研究
-
2021-03-03