【Deep C (and C++)】深入理解C/C++(3)
译自Deep C (and C++) by Olve Maudal and Jon Jagger,本身半桶水不到,如果哪位网友发现有错,留言指出吧:)
第二位候选者表现不错,那么,相比大多数程序员,他还有什么潜力没有被挖掘呢?
可以从以下几个角度去考察:
有关平台的问题—32位与64位的编程经验;
内存对齐;
CPU以及内存优化;
C语言的精髓;
接下来,主要分享一下以下相关内容:
内存模型;
优化;
C语言之精髓;
内存模型:
静态存储区(static storage):如果一个对象的标识符被声明为具有内部链接或是外部链接,或是存储类型说明符是static,那么这个对象具有静态生存期。这个对象的生命周期是整个程序的运行周期。
PS:内部链接,也就是编译单元内可见,是需要使用static来修饰的,连接程序不可见;外部链接,是指别的编译单元可见,也就是链接程序可见。我这里还不太清楚为什么需要三种情况来说明。
int* immortal(void)
{
static int storage = 42;
return &storage;
}
自动存储区(automatic storage):如果一个对象没有被指明是内部链接还是外部链接,并且也没有static修饰,那么,这个对象具有自动生存期,也称之为本地生存期。一般使用auto说明符来修饰,只在块内的变量声明中允许使用,这样是默认的情况,因此,很少看到auto说明符。简单地说,自动存储区的变量,在一对{}之间有效。
int* zombie(void)
{
auto int storage = 42;
return &storage;
}
分配的存储区域(allocated storage):调用calloc函数,malloc函数,realloc函数分配的内存,称之为分配的存储区域。他们的作用域(生命周期会是更好的术语吗?)在分配和释放之间。
int* finite(void)
{
int* ptr = malloc(sizeof(int*));
*ptr = 42;
return ptr;
}
优化相关:
一般来说,编译的时候,你都应该打开优化选项。强制编译器更努力的去发现更多的潜在的问题。
上面,同样地代码,打开优化选项的编译器得到了警告信息:a 没有初始化。
C语言的精髓:
C语言的精髓体现在很多方面,但其本质在于一种社区情感(communitysentiment),这种社区情感建立在C语言的基本原则之上。
C语言原理简介:
1、 相信程序员;
2、 保持语言简单精炼;
3、 对每一种操作,仅提供一种方法;(译者注:?)
4、 尽可能的快,但不保证兼容性;
5、 保持概念上的简单;
6、 不阻止程序员做他们需要做的事。
现在来考察一下我们的候选者关于C++的知识:)
你:1到10分,你觉得你对C++的理解可以打几分?
第一个候选者:我觉得我可以打8到9分。
第二个候选者:4分,最多也就5分了。我还需要多加学习C++。
这时,C++之父Bjarne Stroustrup在远方传来声音:我觉得我可以打7分。(OH,MY GOD!!)
那么,下面的代码段,会输出什么?
#include <iostream>
struct X
{
int a;
char b;
int c;
};
int main(void)
{
std::cout << sizeof(X) << std::endl;
}
第二个候选者:这个结构体是一个朴素的结构体(POD:plain old data),C++标准保证在使用POD的时候,和C语言没有任何区别。因此,在你的机器上(64位机器,运行在32位兼容模式下),我觉得会输出12.
顺便说一下,使用func(void)而不是用func()显得有点诡异,因为C++中,void是默认情况,这个相对于C语言的默认是任意多的参数,是不一样的。这个规则同样适用于main函数。当然,这不会带来什么伤害。但这样的代码,看起来就像是顽固的C程序员在痛苦的学习C++的时候所写的。下面的代码,看起来更像C++:
#include <iostream>
struct X
{
int a;
char b;
int c;
};
int main()
{
std::cout << sizeof(X) << std::endl;
}
第一个候选者:这个程序会打印12.
你:好。如果我添加一个成员函数,会怎么样?比如:
#include <iostream>
struct X
{
int a;
char b;
int c;
void set_value(int v) { a = v; }
};
int main()
{
std::cout << sizeof(X) << std::endl;
}
第一个候选者:啊?C++中可以这样做吗?我觉得你应该使用类(class)。
你:C++中,class和struct有什么区别?
候选者:在一个class中,你可以有成员函数,但是我不认为在struct中可以拥有成员函数。莫非可以?难道是默认的访问权限不同?(Is it the default visibility that is different?)
不管怎样,现在程序会输出16.因为,会有一个指针指向这个成员函数。
你:真的?如果我多增加两个函数呢?比如:
#include <iostream>
struct X
{
int a;
char b;
int c;
void set_value(int v) { a = v; }
int get_value() { return a; }
void increase_value() { a++; }
};
int main()
{
std::cout << sizeof(X) << std::endl;
}
第一个候选者:我觉得对打印24,多了两个指针?
你:在我的机器上,打印的值比24小。
候选者:啊!对了,当然,这个struct有一个函数指针的表,因此他仅仅需要一个指向这个表的指针!我确实对此有一个很深的理解,我差点忘记了,呵呵。
你:事实上,在我的机器上,这段代码输出了12.
候选者心里犯嘀咕:哦?可能是某些诡异的优化措施在捣鬼,可能是因为这些函数永远不会被调用。
你对第二个候选者说:你怎么想的?
第二个候选者:在你的机器上?我觉得还是12?
你:好,为什么?
候选者:因为以这种方式来增加成员函数,不会增加struct的所占内存的大小。对象对他的函数一无所知,反过来,是函数知道他具体属于哪一个对象。如果你把这写成C语言的形式,就会变得明朗起来了。
你:你是指这样的?
struct X
{
int a;
char b;
int c;
};
void set_value(struct X* this, int v) { this->a = v; }
int get_value(struct X* this) { return this->a; }
void increase_value(struct X* this) { this->a++; }
第二个候选者:恩。就想这样的。现在很明显很看出,类似这样的函数是不会增加类型和对象的内存大小的。
你:那么现在呢?
#include <iostream>
struct X
{
int a;
char b;
int c;
virtual void set_value(int v) { a = v; }
int get_value() { return a; }
void increase_value() { a++; }
};
int main()
{
std::cout << sizeof(X) << std:
补充:软件开发 , C++ ,