Skip to content

Commit 908b366

Browse files
committed
fix some bug in C++
1 parent b6ed3ed commit 908b366

File tree

7 files changed

+138
-84
lines changed

7 files changed

+138
-84
lines changed

C++/Basic.md

+8-5
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
extern double f(int, double); // the same as the two above
2525
extern double f(int, double);
2626

27-
变量定义(definition)用于为变量分配存储空间,还可为变量指定初始值。可以将定义看作是对声明的变量进行实例化,`链接器`需要根据定义来找到变量具体对应的值。下面是前面声明语句对应的定义部分:
27+
变量定义(definition)**用于为变量分配存储空间,还可为变量指定初始值**。可以将定义看作是对声明的变量进行实例化,`链接器`需要根据定义来找到变量具体对应的值。下面是前面声明语句对应的定义部分:
2828

2929
int bar;
3030
int g(int lhs, int rhs) {return lhs*rhs;}
@@ -193,8 +193,9 @@ sizeof 运算符的结果部分地依赖于其作用的类型:
193193
* 对引用类型执行 sizeof 运算得到被引用对象所占空间的大小;
194194
* 对指针执行 sizeof 运算得到指针本身所占空间的大小;
195195
* 对解引用指针执行 sizeof 运算得到指针指向的对象所占空间的大小,指针不需要有效;
196-
* 对数组执行 sizeof 运算得到整个数组所占空间的大小(sizeof 不会把数组转换成指针来处理,可以用数组的大小除以单个元素的大小得到数组中元素的个数)。
197-
* 对 string 对象或 vector 对象执行 sizeof 运算只返回该类型固定部分的大小(**24**),不会计算对象占用了多少空间;
196+
* 对数组执行 sizeof 运算得到整个数组所占空间的大小,等同于对数组中所有的元素各执行一次 sizeof 运算并将所得结果求和。(sizeof 不会把数组转换成指针来处理,可以用数组的大小除以单个元素的大小得到数组中元素的个数)。
197+
* 对 string 对象或 vector 对象执行 sizeof 运算只返回该类型固定部分的大小(**24**),不会计算对象占用了多少空间;
198+
* 对于 enum 类型的变量,它保存的枚举对象本身并不是真的变量,它们只是类型安全的 #define,用来以可读的方式保存一串数字而已。编译器一般是用 int 类型的来保存一个 enum,所以sizeof 结果为 4。
198199
199200
此外求类的大小时遵循下面规则(只统计与类的实例有关的,只与类型相关的不统计):
200201
@@ -209,7 +210,8 @@ sizeof 运算符的结果部分地依赖于其作用的类型:
209210
[[字符数组,八进制坑](http://www.nowcoder.com/questionTerminal/a7b35bc367604e73823d2dded6496e38)]
210211
[[类的大小](http://www.nowcoder.com/questionTerminal/33f3a63dc5d449adb351168ada7f47c6)]
211212
[[C中整型字符常量,C++字符字面量大小](http://www.nowcoder.com/questionTerminal/e16ca070d715455fa8f6916aa324138d)]
212-
[[引用的大小](http://www.nowcoder.com/questionTerminal/31095437d232497c9ea40c5c7a629dc4)]
213+
[[引用的大小](http://www.nowcoder.com/questionTerminal/31095437d232497c9ea40c5c7a629dc4)]
214+
[[枚举类型,typedef 函数指针](http://www.nowcoder.com/questionTerminal/07684925aaaf4885ad574b2a2debe930)]
213215
214216
# 内存对齐
215217
@@ -222,7 +224,7 @@ struct 或者 union 成员对齐规则如下:
222224
1. 第一个数据成员放在offset为0的地方,每个成员按照对齐系数和自身占用字节数中,二者比较小的那个进行对齐;
223225
2. 在数据成员完成各自对齐以后,struct或者union本身也要进行对齐,对齐将按照对齐系数和struct或者union中最大数据成员长度中比较小的那个进行;
224226
225-
先局部成员对齐,然后再全局对齐。([memory_align.cpp](C++_Code/memory_align.cpp))此外,值得注意的是,enum 内部是 int 实现的,所以大小为 4。用 typedef 声明指针时,并不为指针分配空间。
227+
先局部成员对齐,然后再全局对齐。([memory_align.cpp](C++_Code/memory_align.cpp))此外,值得注意的是,**enum 内部是 int 实现的,所以大小为 4。用 typedef 声明指针时,并不为指针分配空间**
226228
227229
[[enum,typedef声明指针](http://www.nowcoder.com/questionTerminal/f12da06f01594f4d8d04a1f242399dc5)]
228230
[[结构体中 : 的含义](http://www.nowcoder.com/questionTerminal/f4e20747a2dd4649bac0c028daa234f4)]
@@ -418,6 +420,7 @@ C++ 提供一种特殊的运算符,逗号运算符,它的优先级别最低
418420

419421
# 更多内容
420422

423+
[what is the size of an enum type data in C++?](http://stackoverflow.com/questions/8115550/what-is-the-size-of-an-enum-type-data-in-c)
421424
[What is the difference between a definition and a declaration?](http://stackoverflow.com/questions/1410563/what-is-the-difference-between-a-definition-and-a-declaration)
422425
[c++中的左值与右值](http://www.cnblogs.com/catch/p/3500678.html)
423426
[C++ Rvalue References Explained](http://thbecker.net/articles/rvalue_references/section_01.html)

C++/Function.md

+1
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ strcat()函数非常类似于 strcpy(),除了它将一个字符串合并到缓
218218

219219
# 更多阅读
220220

221+
[C Function Call Conventions and the Stack](http://www.csee.umbc.edu/~chang/cs313.s02/stack.shtml)
221222
[函数中局部变量的返回](http://blog.csdn.net/jackystudio/article/details/11523353)
222223

223224
[1]: http://7xrlu9.com1.z0.glb.clouddn.com/C++_Function_1.png

C++/God.md

+65-62
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,31 @@
44

55
下面多态函数调用的输出?
66

7-
class B
8-
{
9-
public:
10-
virtual void vfun(int i = 10){
11-
cout << "B:vfun " << i << endl;
12-
}
13-
};
14-
15-
class D : public B
16-
{
17-
public:
18-
virtual void vfun(int i = 20){
19-
cout << "D:vfun " << i << endl;
20-
}
21-
};
22-
23-
int main()
24-
{
25-
D* pD = new D();
26-
B* pB = pD;
27-
pD->vfun();
28-
pB->vfun();
7+
```c++
8+
class B
9+
{
10+
public:
11+
virtual void vfun(int i = 10){
12+
cout << "B:vfun " << i << endl;
2913
}
14+
};
15+
16+
class D : public B
17+
{
18+
public:
19+
virtual void vfun(int i = 20){
20+
cout << "D:vfun " << i << endl;
21+
}
22+
};
23+
24+
int main()
25+
{
26+
D* pD = new D();
27+
B* pB = pD;
28+
pD->vfun(); // D:vfun 20
29+
pB->vfun(); // D:vfun 10
30+
}
31+
```
3032

3133
为了解释清楚,先来看四个概念:
3234

@@ -46,46 +48,46 @@
4648

4749
所以对于上面的例子,pD->vfun()和pB->vfun()调用都是函数D::vfun(),但是缺省参数是静态绑定的,所以 pD->vfun() 时,pD的静态类型是D*,所以它的缺省参数应该是20;同理,pB->vfun()的缺省参数应该是10。
4850

49-
不是很容易接受是吧,所以`绝不要重新定义继承而来的缺省参数`
51+
不是很容易接受是吧,所以`Effective C++ 条款37:绝不要重新定义继承而来的缺省参数`
5052

5153
## 继承中重复的数据成员
5254

5355
派生类中数据成员和基类数据成员重复时,该如何调用?看下面例子:
5456

55-
#include "stdio.h"
56-
 
57-
class A
58-
{
59-
public:
60-
    int _a;
61-
    A()
62-
    {
63-
        _a = 1;
64-
    }
65-
    void print()
66-
    {
67-
        printf("%d", _a);
68-
    }
69-
};
70-
class B: public A
71-
{
72-
public:
73-
    int _a;
74-
    B()
75-
    {
76-
        _a = 2;
77-
    }
78-
};
79-
int main()
80-
{
81-
    B b;
82-
    b.print(); // 1
83-
    printf("%d", b._a); // 2
57+
```c++
58+
#include "stdio.h"
59+
60+
class A {
61+
public:
62+
int _a;
63+
64+
A(){
65+
_a = 1;
66+
}
67+
68+
void print(){
69+
printf("%d", _a);
70+
}
71+
};
72+
73+
class B : public A {
74+
public:
75+
int _a;
76+
77+
B() {
78+
_a = 2;
8479
}
80+
};
8581

86-
首先要知道继承的时候,允许子类存在与父类同名的成员变量,但是并不覆盖父类的成员变量,他们同时存在。
82+
int main() {
83+
B b;
84+
b.print(); // 1    
85+
printf("%d", b._a); // 2
86+
}
87+
```
88+
首先要知道继承的时候,允许**子类存在与父类同名的成员变量,但是并不覆盖父类的成员变量,他们同时存在**
8789

88-
子类公有(public)继承父类,所以子类可以通过对象访问父类的公有成员函数,由于调用的是父类的公有成员函数(该函数中的this指针存放的是父类对象的地址),所以打印的是父类A的_a。
90+
子类公有(public)继承父类,所以子类可以通过对象访问父类的公有成员函数,由于调用的是父类的公有成员函数(**该函数中的this指针存放的是父类对象的地址**),所以打印的是父类A的_a。
8991

9092
# 该死的未定义行为
9193

@@ -110,24 +112,25 @@ C++ 对于这种abs之后超出表示类型的行为`没有定义`,不同编
110112

111113
> If the result cannot be represented by the returned type (such as abs(INT_MIN) in an implementation with two's complement signed values), it causes undefined behavior.
112114
113-
参考:[Cplusplus: abs](http://www.cplusplus.com/reference/cstdlib/abs/?kw=abs)
114-
115115
# 语言的细节
116116

117117
## 字符数组、常量字符串
118118

119-
char a[] = "abcde";
120-
char arr[] = {4, 3, 9, 9, 2, 0, 1, 5};
121-
cout << sizeof(a)<<endl; //6
122-
cout << sizeof(arr) << endl; //8
123-
cout << strlen(a); //5
119+
```c++
120+
char a[] = "abcde";
121+
char arr[] = {4, 3, 9, 9, 2, 0, 1, 5};
122+
cout << sizeof(a)<<endl; //6
123+
cout << sizeof(arr) << endl; //8
124+
cout << strlen(a); //5
125+
```
124126
125127
字符串常量后面会有 '\0',sizeof计算时会加上 '\0' 后计算长度。'\0' 的ASCII码值为0,strlen 计算时遇到 '\0'结束。
126128
127129
128-
129130
# 更多阅读
130131
132+
[Cplusplus: abs](http://www.cplusplus.com/reference/cstdlib/abs/?kw=abs)
133+
[What are all the common undefined behaviours that a C++ programmer should know about? ](http://stackoverflow.com/questions/367633/what-are-all-the-common-undefined-behaviours-that-a-c-programmer-should-know-a)
131134
[What are the common undefined/unspecified behavior for C that you run into?](http://stackoverflow.com/questions/98340/what-are-the-common-undefined-unspecified-behavior-for-c-that-you-run-into)
132135
133136

C++/Keywords.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,11 @@ inline函数背后的整体观念就是,将对此函数的每一个调用都
138138

139139
inline函数可以调用又不至于导致函数调用的开销,但是仍有一些弊端。比如导致代码膨胀,进而造成额外的换页行为,降低指令高速缓存装置的命中率,以及伴随而来的效率损失。
140140

141-
此外,有时候编译器虽然愿意 inlining 某个函数,但还可能为该函数生成一个函数本体。比如如果程序要取某个 inline 函数的地址,编译器必须为此函数生成一个本体。
141+
此外,有时候编译器虽然愿意 inlining 某个函数,但还可能为该函数生成一个函数本体。比如如果程序要取某个 inline 函数的地址,编译器必须为此函数生成一个本体,比如虚函数和递归函数。
142+
143+
**内联函数的定义最好是放在头文件中**,这样每一个包含该头文件的源文件均可以在编译器将调用该内联函数的代码展开。如果仅仅在头文件中声明内联函数,而在一个源文件中定义该函数,那么只有定义它的那个源文件可以在编译器展开内联函数。(这和模版类,函数模版的定义放在头文件中是基于同样的原理。)
144+
145+
[inline 函数的观点](http://www.nowcoder.com/questionTerminal/0cd6af2fd4374df597b49e09302b1a5a)
142146

143147
## typedef
144148

C++/Memory.md

+4-2
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ malloc 的一个具体使用例子在 [gist](https://gist.github.com/xuelangZF/5
118118

119119
如图所示,定义了一个简单的函数function,它接受一个整形参数,做一次乘法操作并返回。当调用function(0)时,arg参数记录了值0入栈,并将call function指令下一条指令的地址0x00bd16f0保存到栈内,然后跳转到function函数内部执行。每个函数定义都会有函数头和函数尾代码,如图绿框表示。因为函数内需要用ebp寄存器保存函数栈帧基址,因此先保存ebp原来的值到栈内,然后将栈指针esp内容保存到ebp。函数返回前需要做相反的操作——将esp指针恢复,并弹出ebp。
120120

121-
之所以会有缓冲区溢出的可能,主要是因为栈空间内保存了函数的返回地址。该地址保存了函数调用结束后后续执行的指令的位置,对于计算机安全来说,该信息是很敏感的。如果有人恶意修改了这个返回地址,并使该返回地址指向了一个新的代码位置,程序便能从其它位置继续执行。也就是说攻击者可以利用缓冲区溢出来窜改进程运行时栈,从而改变程序正常流向,轻则导致程序崩溃,重则系统特权被窃取。
121+
之所以会有缓冲区溢出的可能,主要是因为**栈空间内保存了函数的返回地址**。该地址保存了函数调用结束后后续执行的指令的位置,对于计算机安全来说,该信息是很敏感的。如果有人恶意修改了这个返回地址,并使该返回地址指向了一个新的代码位置,程序便能从其它位置继续执行。也就是说攻击者可以利用缓冲区溢出来窜改进程运行时栈,从而改变程序正常流向,轻则导致程序崩溃,重则系统特权被窃取。
122122

123123
## 溢出原理
124124

@@ -232,7 +232,9 @@ int main()
232232
[缓冲区溢出攻击](http://www.cnblogs.com/fanzhidongyzby/p/3250405.html)
233233
[C/C++内存泄漏及检测](http://blog.jobbole.com/95375/)
234234
[Doc: Valgrind:Memory leak detection](http://valgrind.org/docs/manual/mc-manual.html#mc-manual.leaks)
235-
[用valgrind检查C++程序的内存泄漏](http://zhiqiang.org/note/md/check-cpp-memory-with-valgrind.html)
235+
[用valgrind检查C++程序的内存泄漏](http://zhiqiang.org/note/md/check-cpp-memory-with-valgrind.html)
236+
[C Function Call Conventions and the Stack](http://www.csee.umbc.edu/~chang/cs313.s02/stack.shtml)
237+
236238

237239
[1]: http://7xrlu9.com1.z0.glb.clouddn.com/C++_Memory_1.jpg
238240
[2]: http://7xrlu9.com1.z0.glb.clouddn.com/C++_Memory_2.jpg

C++/Pointer.md

+42-14
Original file line numberDiff line numberDiff line change
@@ -42,19 +42,23 @@ C++ 规定所有变量在使用前必须先定义,即指定其类型,在`编
4242

4343
指针变量可以有空值,即该指针变量不指向任何变量,可以这样表示 `p=NULL`; 实际上NULL代表整数0,也就是使p指向地址为0的单元,这样可以使指针不指向任何有效的单元。看下面的程序:
4444

45-
int* p = 0;
46-
// int* p = 1;
47-
// error: cannot initialize a variable of type 'int *' with an rvalue of type 'int'
45+
```c++
46+
int* p = 0;
47+
// int* p = 1;
48+
// error: cannot initialize a variable of type 'int *' with an rvalue of type 'int'
49+
```
4850

4951
这里 pint指针指向 0 地址处,如果改为 int *p=1,则会报错。
5052

5153
## 指针运算
5254

5355
C++规定,一个指针变量加/减一个整数是将该指针变量的原值(是一个地址)和它指向的变量所占用的内存单元字节数相加或相减。如 `p+i` 代表这样的地址计算:p+i*d,d为p所指向的变量单元所占用的字节数。这样才能保证p+i指向p下面的第i个元素。
5456

55-
int* p = 0;
56-
p += 6;
57-
cout << p << endl; // 0x18
57+
```c++
58+
int* p = 0;
59+
p += 6;
60+
cout << p << endl; // 24
61+
```
5862

5963
`两个指针变量可以相减`:如果两个指针变量指向同一个数组的元素,则两个指针变量值之差是两个指针之间的元素个数。假如p1指向 a[0],p2指向a[4],则p2-p1=(a+4)-(a)=4-0=4,但p1+p2并无实际意义。
6064

@@ -96,22 +100,46 @@ C++规定,一个指针变量加/减一个整数是将该指针变量的原值(
96100
## 指针和const
97101

98102
指向常量的指针(`pointer to const`)不能用于改变其所指对象的值,要想存放常量对象的地址,只能使用指向常量的指针。
99-
100-
const int *a = 3;
101-
int const *a = 3;
102-
int const* a = 3;
103+
104+
```c++
105+
const int *a = 3;
106+
int const *a = 3;
107+
int const* a = 3;
108+
```
103109

104110
指针本身是对象,因此可以把指针本身定为常量。常量指针(`const pointer`)必须初始化,而且一旦初始化完成,则它的值(也就是存放在指针中的那个地址)就不能再改变了。
105-
106-
int errNumb = 0;
107-
int *const curErr = &errNumb;
111+
112+
```c++
113+
int errNumb = 0;
114+
int *const curErr = &errNumb;
115+
```
108116

109117
也可以定义一个指向常量的常量指针(const pointer to const)。
110118

111119
const double pi=3.14;
112120
const double * const pip = &pi;
113121

114-
为了判断const到底对谁起作用(即谁是const的),可以用以下简单规则:**const只对它左边的东西起作用,当const本身就是最左边的修饰符时,它才会对右边的东西起作用**
122+
为了判断const到底对谁起作用(即谁是const的),可以用以下简单规则:**const只对它左边的东西起作用,当const本身就是最左边的修饰符时,它才会对右边的东西起作用**。有时候,情况可能会比较复杂,比如:
123+
124+
```c++
125+
const char * const * pp;
126+
pp++; // OK
127+
(*pp)++; // Error
128+
**pp = 'c'; // Error
129+
```
130+
131+
怎么去理解呢?先从一级指针说起吧:
132+
133+
* const char p: 限定变量p为只读。这样如p=2这样的赋值操作就是错误的。
134+
* const char \*p: p为一个指向char类型的指针,const只限定p指向的对象为只读。这样,p=&a或 p++等操作都是合法的,但如\*p=4这样的操作就错了,因为企图改写这个已经被限定为只读属性的对象。
135+
* char \*const p: 限定此指针为只读,这样p=&a或p++等操作都是不合法的。而\*p=3这样的操作合法,因为并没有限定其最终对象为只读。
136+
* const char \*const p: 两者皆限定为只读,不能改写。
137+
138+
有了以上的对比,再来看二级指针问题:
139+
140+
* const char \*\*p:p为一个指向指针的指针,const限定其最终对象为只读,显然这最终对象也是为char类型的变量。故像\*\*p=3这样的赋值是错误的,而像`*p=?,p++`这样的操作合法。
141+
* const char \* const \*p:限定最终对象和p指向的指针为只读。这样 `*p=?`的操作也是错的。
142+
* const char * const * const p:全部限定为只读,都不可以改写。
115143

116144
# 指针和数组
117145

Coding/sizeof_demo.cpp

+13
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,18 @@ class C{
3939
class D{
4040
};
4141

42+
class BU
43+
{
44+
int number;
45+
union UBffer
46+
{
47+
char buffer[13];
48+
int number;
49+
}ubuf;
50+
void foo(){}
51+
typedef char*(*f)(void*);
52+
enum {hdd,ssd,blueray} disk;
53+
};
4254

4355
int main ( ) {
4456
int *a;
@@ -72,5 +84,6 @@ int main ( ) {
7284
string s("1234");
7385
cout << "String " << sizeof(s) << "\n"; // 24
7486

87+
cout << sizeof(BU) << std::endl;
7588
return 0;
7689
}

0 commit comments

Comments
 (0)