ios判断程序是否处于调试状态

5950次浏览

当一个应用被调试的时候,会给进程设置一个标识(P_TRACED),我们可以通过检测该进程是否有设置这个标识来检测进程是否正在被调试以保护好我们的应用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <sys/types.h>
#include <sys/sysctl.h>
 
static int check_debugger( ) __attribute__((always_inline));
 
static int check_debugger( )
{
    size_t size = sizeof(struct kinfo_proc);
    struct kinfo_proc info;
    int ret,name[4];
 
    memset(&info, 0, sizeof(struct kinfo_proc));
 
    name[0] = CTL_KERN;
    name[1] = KERN_PROC;
    name[2] = KERN_PROC_PID;
    name[3] = getpid();
 
    if((ret = (sysctl(name, 4, &info, &size, NULL, 0)))){
        return ret;  //sysctl() failed for some reason
    }
 
    return (info.kp_proc.p_flag & P_TRACED) ? 1 : 0;
}

如果你不确信产生的目标代码以inline的方式编译该函数,你也可以将其转化成宏的方式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#define DEBUGGER_CHECK {                                \
    size_t size = sizeof(struct kinfo_proc);            \
    struct kinfo_proc info;                             \
    int ret,name[4];                                    \
                                                        \
    memset(&info, 0, sizeof(struct kinfo_proc));        \
                                                        \
    name[0] = CTL_KERN;                                 \
    name[1] = KERN_PROC;                                \
    name[2] = KERN_PROC_PID;                            \
    name[3] = getpid();                                 \
                                                        \
    if(ret = (sysctl(name, 4, &info, &size, NULL, 0))){ \
        return (EXIT_FAILURE);                          \
    }                                                   \
                                                        \
    if(info.kp_proc.p_flag & P_TRACED){                 \
        /* Code to react to debugging goes here */      \
    }                                                   \
}

C++的STL是如何实现算法和数据结构分离的

454次浏览

STL看起来是使用了面向对象,但实际上是大部分都是面向过程了。
STL的很多算法,就拿sort函数来说吧。

1
void sort(_RandomAccessIterator __first, _RandomAccessIterator __last, _Compare __comp);

只要数据结构的跌代器是随机访问的就可以使用。比如vector, list,同时也兼容普通数组int[]。

这里说到跌代器,STL有一整套跌代器的实现标准:
1、实现begin和end函数,是要全局的
如vecotr:

1
vecotr<T>::Iterator begin(vecotr<T>);

而不是 vecotr的成员函数begin,这点要区分。
2、跌代器实现前至++运算
3、跌代器实现 * 运算
4、跌代器实现 != 运算

基本这四点就可以完成了,可以根据这个规则自己实现一个跌代器。

有了跌代器后,那么对于算法来说他们基本就一样了,开头,结尾,自增,以次访问就可以了。

所以一个sort就可以

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
vecotr<int> a;
string b;
list<float> c;
 
sort(a.begin(), a.end());
sort(b.begin(),b.end());
sort(c.begin(),c.end());
 
static bool less(int a1, int a2)
{
    return a1 < a2;
}
 
sort(a.begin(), a.begin()+5, less); // 对前5个排序
sort(a.begin(), a.end(), less);
sort(a.begin(), a.end(), [](int a1, int a2) {
    return a1 <= a2; // 匿名函数
});

结论就是算法跟数据结构是通过跌代器进行沟通的,所以学好跌代器,STL才算学好,要会用,也要懂为原理。

c++字节压缩ByteData

1367次浏览

不管什么语言,一个变量至少要占1个字节(Byte),像char,bool,byte类型都是占1个字节,无符号的范围是0~255,有符号是-128~127。

在特殊情况下,当明确知道取值范围小于0~255,如0~3。这时候就会考虑怎么能再压缩内存。因为实际用不到1个字节,只用2个位(bit)即可,但只占2位的的类型是没有的。也许你会问,这省了这么点内存有什么用,即使使用int也没有什么大不了的。

对的,当只有一个两个的时候是不需要考虑,不过当面临大数组,成百上千,甚至上万个的时候,2个位跟1个字节相差了4倍,可以节省3/4的内存,压缩75%还是很可观的。

还有一种情况是数据库对二进制数据存储是有长度限制的,做服务器的话都知道用户数据上会有大量的0~1标记,这时候使用字节压缩1个用户就会节省87.5%的储存或内存。1w个,10w个用户那节省的可不是一点点了。

好了,不多说了,核心代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 读取函数
// @param data 存放二进制数据
// @param eachBit 每eachBit个bit位代表一个值
// @param index 获取指定索引位置的值
int get(const vector<unsigned char>& data, int eachBit, int index)
{
    int length = (int)data.size();
    int i = (index * eachBit) / 8;
    int  m = (index * eachBit) % 8;
    unsigned int mask = (0x01 << eachBit) - 0x01;
 
    if (i < 0 || i >= length || m < 0) {
        return 0;
    }
 
    return (data[i] & (mask << m)) >> m;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 设置函数
// @param data 存放二进制数据
// @param eachBit 每eachBit个bit位代表一个值
// @param index 指定要修改的索引位置
// @param value 新的值
void set(vector<unsigned char>& data, int eachBit, int index, int value)
{
    int length = (int)data.size();
    int i = (index * eachBit) / 8;
    int  m = (index * eachBit) % 8;
    unsigned int mask = (0x01 << eachBit) - 0x01;
 
    if (i < 0 || i >= length || m < 0) {
        return ;
    }
 
    mask = mask << m;
    value = value << m;
 
    // assert(value == (value & mask));
 
    data[i] = (data[i] & ~mask) | (value & mask);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 样例
#include<stdio.h>
#include<vector>
using namespace std;
 
int main()
{
    vector<unsigned char> data = {34, 2, 57, 123, 65, 93, 33};
 
    int a = get(data, 2, 8);
    set(data, 2, 8, a+1);
    int b = get(data, 2, 8);
 
    printf("a=%d b=%d \n", a, b);
}
1
2
// 打印结果
a=1 b=2

上面为通用算法代码,下面的是用c++对代码进行了包装,增加[]访问及迭代器的操作
点击下载:ByteData.zip

如何提高 C/C++ 编程能力

288次浏览

一万小时定律:作家格拉德威尔在《异类》一书中指出:人们眼中的天才之所以卓越非凡,并非天资超人一等,而是付出了持续不断的努力。1万小时的锤炼是任何人从平凡变成超凡的必要条件。
他将此称为“一万小时定律”。要成为某个领域的专家,需要10000小时,按比例计算就是:如果每天工作八个小时,一周工作五天,那么成为一个领域的专家至少需要五年

c/c++尤其适合这句话,如果下定决心要学c/c++,那么就不要有速成的思想

1、多敲代码,多思考
2、多敲代码,多思考
3、多敲代码,多思考

以上3点就是提高c/c++编程能力最捷径的方法了!!

编程提升技巧:

1、重写算法,选一个算法(如排序),查看并敲写所有现有的算法。然后思考有没有更好的办法,或者换一个写法会怎么,能不能再优化一些,也可以基于其中的某一个算法思考。
2、编写大型项目,大型项目跟小项目完全是两码事,会遇到各种各样的问题,在解决问题中会锻炼代码编写的能力,及思维能力。
3、参与不同类型的项目开发,不同项目会有不同项目的需求及问题,可以拓展眼界范围。
4、学习开发模式
5、学习其它编程语言(如java,c#,lua,js,php等),了解其中的优缺点。
6、总之,多敲代码,多思考

c++ Singleton 单例

1646次浏览

单例模式也称为单件模式、单子模式,可能是使用最广泛的设计模式。其意图是保证一个类仅有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。有很多地方需要这样的功能模块,如系统的日志输出,GUI应用必须是单鼠标,MODEM的联接需要一条且只需要一条电话线,操作系统只能有一个窗口管理器,一台PC连一个键盘。

单例模式有许多种实现方法,在C++中,甚至可以直接用一个全局变量做到这一点,但这样的代码显的很不优雅。 使用全局对象能够保证方便地访问实例,但是不能保证只声明一个对象——也就是说除了一个全局实例外,仍然能创建相同类的本地实例。

《设计模式》一书中给出了一种很不错的实现,定义一个单例类,使用类的私有静态指针变量指向类的唯一实例,并用一个公有的静态方法获取该实例。
单例模式通过类本身来管理其唯一实例,这种特性提供了解决问题的方法。唯一的实例是类的一个普通对象,但设计这个类时,让它只能创建一个实例并提供对此实例的全局访问。唯一实例类Singleton在静态成员函数中隐藏创建实例的操作。习惯上把这个成员函数叫做Instance(),它的返回值是唯一实例的指针。
定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class CSingleton
{
private:
	CSingleton()   //构造函数是私有的
	{
	}
	CSingleton(const CSingleton &);  
	CSingleton & operator = (const CSingleton &); 
 
	static CSingleton *m_pInstance;
public:
	static CSingleton * getInstance()
	{
		if(m_pInstance == NULL)  //判断是否第一次调用
			m_pInstance = new CSingleton();
		return m_pInstance;
	}
};

用户访问唯一实例的方法只有getInstance()成员函数。如果不通过这个函数,任何创建实例的尝试都将失败,因为类的构造函数是私有的。getInstance()使用懒惰初始化,也就是说它的返回值是当这个函数首次被访问时被创建的。这是一种防弹设计——所有getInstance()之后的调用都返回相同实例的指针:
CSingleton* p1 = CSingleton :: getInstance();
CSingleton* p2 = p1-> getInstance();
CSingleton & ref = * CSingleton :: getInstance();

关于Singleton(const Singleton);和 Singleton & operate = (const Singleton&);函数,需要声明成私有的,并且只声明不实现。这样,如果如果通过 CSingleton copy = *CSingleton::getInstance()等方式来使用单例时,不管是在友元类中还是其他的,编译器都是报错。如果在c++11中,可以Singleton(const Singleton) = delete;来明确函数是删除的。

单例类CSingleton有以下特征:
它有一个指向唯一实例的静态指针m_pInstance,并且是私有的;
它有一个公有的函数,可以获取这个唯一的实例,并且在需要的时候创建该实例;
它的构造函数是私有的,这样就不能从别处创建该类的实例。

大多数时候,这样的实现都不会出现问题。有经验的读者可能会问,m_pInstance指向的空间什么时候释放呢?更严重的问题是,该实例的析构函数什么时候执行?

如果在类的析构行为中有必须的操作,比如关闭文件,释放外部资源,那么上面的代码无法实现这个要求。我们需要一种方法,正常的删除该实例。
可以在程序结束时调用getInstance(),并对返回的指针掉用delete操作。这样做可以实现功能,但不仅很丑陋,而且容易出错。因为这样的附加代码很容易被忘记,而且也很难保证在delete之后,没有代码再调用getInstance函数。

一个妥善的方法是让这个类自己知道在合适的时候把自己删除,或者说把删除自己的操作挂在操作系统中的某个合适的点上,使其在恰当的时候被自动执行。
我们知道,程序在结束的时候,系统会自动析构所有的全局变量。事实上,系统也会析构所有的类的静态成员变量,就像这些静态成员也是全局变量一样。利用这个特征,我们可以在单例类中定义一个这样的静态成员变量,而它的唯一工作就是在析构函数中删除单例类的实例。如下面的代码中的CGarbo类(Garbo意为垃圾工人):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class CSingleton
{
private:
	CSingleton()
	{
	}
	CSingleton(const CSingleton &) = delete;  
	CSingleton & operator = (const CSingleton &) = delete; 
 
	static CSingleton *m_pInstance;
	class CGarbo   //它的唯一工作就是在析构函数中删除CSingleton的实例
	{
	public:
		~CGarbo()
		{
			if(CSingleton::m_pInstance)
				delete CSingleton::m_pInstance;
		}
	};
	static CGarbo Garbo;  //定义一个静态成员变量,程序结束时,系统会自动调用它的析构函数
public:
	static CSingleton * getInstance()
	{
		if(m_pInstance == NULL)  //判断是否第一次调用
			m_pInstance = new CSingleton();
		return m_pInstance;
	}
};

类CGarbo被定义为CSingleton的私有内嵌类,以防该类被在其他地方滥用。
程序运行结束时,系统会调用CSingleton的静态成员Garbo的析构函数,该析构函数会删除单例的唯一实例。
使用这种方法释放单例对象有以下特征:
在单例类内部定义专有的嵌套类;
在单例类内定义私有的专门用于释放的静态成员;
利用程序在结束时析构全局变量的特性,选择最终的释放时机;
使用单例的代码不需要任何操作,不必关心对象的释放。

进一步的讨论
但是添加一个类的静态对象,总是让人不太满意,所以有人用如下方法来重新实现单例和解决它相应的问题,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 
class CSingleton
{
private:
	CSingleton()   //构造函数是私有的
	{
	}
	CSingleton(const CSingleton &) = delete;  
	CSingleton & operator = (const CSingleton &) = delete;  
public:
	static CSingleton * getInstance()
	{
		static CSingleton instance;   //局部静态变量
		return &instance;
	}
};

一般来说一个程序里会有大量的单例,如果每个都这么写肯定是很累的,那么就考虑实现一个单例模板。一般来说呢,会有好几个版本的模板实现。这里就不一一说明了,只提供我自己觉得算是比较好的一种方式,同时也包括了原子操作及非原子操作。该代码使用了c++11特性。
如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
 
class $atomic {};        // 原子,线程安全
class $nonatomic {};     // 非原子,非线程安全
 
template <class T, class Atomicable = $nonatomic> 
class Singleton
{
 
protected:
    Singleton() {}
    virtual ~Singleton() {}
 
public:
    static inline T* getInstance();
 
private:
    static T* __getInstance(const $nonatomic&);
    static T* __getInstance(const $atomic&);
};
 
template inline T* Singleton<T, Atomicable>::getInstance()
{
    static_assert(std::is_same<Atomicable, $nonatomic>::value
                  || std::is_same<Atomicable, $atomic>::value, "Atomicable must be nonatomic or atomic");
 
    // 参考STL,用类型来规避 if...else...
    static T* s = __getInstance(Atomicable());
    return s;
}
 
template T* Singleton<T, Atomicable>;::__getInstance(const $nonatomic&)
{
    static std::auto_ptr auto_s;
    if (auto_s.get() == nullptr) {
        auto_s.reset(new T);
    }
    return auto_s.get();
}
 
template T* Singleton<T, Atomicable>::__getInstance(const $atomic&)
{
    static std::auto_ptr auto_s;
 
    if (auto_s.get() == nullptr) {
        static std::mutex mutex;
        mutex.lock();
        if (auto_s.get() == nullptr) {
            auto_s.reset(new T);
        }
        mutex.unlock();
    }
 
    return auto_s.get();
}
 
#define SINGLETON_DECLARE(T, ...)    \
public: \
static T* getInstance() { return Singleton<T, ##__VA_ARGS__>::getInstance(); } \
private:    \
    T();    \
    T(const T&) = delete;    \
    T&amp; operator=(const T&) = delete; \
    friend class Singleton<T, ##__VA_ARGS__>;
 
 
/******** Singleton Simple *********/
/*
 // Simple.h
 // =================================
 class Simple
 {
    SINGLETON_DECLARE(Simple, [ $nonatomic | $atomic ])
 public:
 
 };
 
 // Simple.cpp
 // =================================
 Simple::Simple()
 {
 
 }
 
 **************************************/

之所以在__getInstance(const $atomic&)函数里面对auto_s.get() == nullptr 是否为空做了两次判断,因为该方法调用一次就产生了对象,pInstance == nullptr 大部分情况下都为false,如果按照原来的方法,每次获取实例都需要加锁,效率太低。而改进的方法只需要在第一次调用的时候加锁,可大大提高效率。在第二次判断是因为,可能当mutex.lock()成功后,auto_s.get()已经不为空了。虽然概率很低,但在多线程的情况下还是要必须规避的。

实现的略有点复杂,不过用起来只用一行代码就要可以了。还有一个额外的功能是,不管什么类,通过Singleton::getInstance()都将得到一个该类的单例。也就是说你甚至可以不在类的头里声明SINGLETON_DECLARE,也可以使用单例。

面向对象是个骗局?!

1797次浏览

网上有一篇文章《Object Orientation Isa Hoax》——面向对象是一个骗局,标题很有煽动性。其中收集一些关于“面向对象的反动言论”,很多言论竟出自众多大师之口。酷壳个人网站版主陈皓对这些言论发表了一篇博文,对其中一些大师的对话摘录下来并对他们做了翻译。现把全文转载于此。全文如下:

今天在网上看到网页叫“Object Orientation Isa Hoax”——面向对象是一个骗局,标题很有煽动性(注:该网站上还有一个网页叫Object Orientation Is Dead),好吧,打开看看上面有些什么,发现这个网页是在收集一些关于“面向对象的反动言论”,没想到的是,很多言论出自很多大师之口。比如:Alexander Stepanov和Bjarne Stroustrup。这些言论挺有意思的,所以,我摘两段在下面:

第一段是Alexander Stepanov的(不要告诉我你不知道这个人,STL之父,关于他的故事,可以到这里看看)。他N年前作过一段采访,原文在这里(我非常建议大家去读一下这篇采访,相当过瘾),译文在这里(不过有地方把原意都译反了,我重译了一下),其中有一个问答被上述的那个面向对象反动言论的网页收录了:


Question:

28_100929092635_1I think STL and Generic Programming mark a definite departure from the common C++ programming style, which I find is almost completely derived from SmallTalk. Do you agree?

提问:

我认为STL和泛型编程标志着非同一般的C++编程风格,而这种风格几乎完全是从SmallTalk派生过来的。你同意吗?

Answer:

Yes. STL is not object oriented. I think that object orientedness is almost as much of a hoax as Artificial Intelligence. I have yet to see an interesting piece of code that comes from these OO people. In a sense, I am unfair to AI: I learned a lot of stuff from the MIT AI Lab crowd, they have done some really fundamental work: Bill Gosper’s Hakmem is one of the best things for a programmer to read. AI might not have had a serious foundation, but it produced Gosper and Stallman (Emacs), Moses (Macsyma) and Sussman (Scheme, together with Guy Steele). I find OOP technically unsound. It attempts to decompose the world in terms of interfaces that vary on a single type. To deal with the real problems you need multisorted algebras – families of interfaces that span multiple types. I find OOP philosophically unsound. It claims that everything is an object. Even if it is true it is not very interesting – saying that everything is an object is saying nothing at all. I find OOP methodologically wrong. It starts with classes. It is as if mathematicians would start with axioms. You do not start with axioms – you start with proofs. Only when you have found a bunch of related proofs, can you come up with axioms. You end with axioms. The same thing is true in programming: you have to start with interesting algorithms. Only when you understand them well, can you come up with an interface that will let them work.

回答:

是的。STL不是面向对象的。我认为面向对象和人工智能差不多,都是个骗局。我至今仍然没有从那些OO编程的人那里看到一丁点有意思的代码。从某种意义上来说,我这么说对人工智能(AI)并不公平:因为我听说过很多MIT(麻省理工大) AI实验室里一帮人搞出来的东西,而且他们的确直正干了一些基础性的工作:Bill Gosper的Hakmem是程序员最好的读物之一。AI或许没有一个实实在在的基础,但它造就了Gosper和Stallman(Emacs), Moses(Macsyma)和Sussman(Scheme, 和Guy Steele一起)。

我发现OOP在技术上是荒谬的,它企图把世界解构成不同的单一类型的接口,为了处理实际问题,你需要多种代数方法——横跨多种类型的接口族;

我发现OOP在哲学上是荒谬的,它声称一切都是对象。即使这是真的也不是很有趣——因为说一切都是对象跟什么都没说一样;

我发现OOP的方法论是错误的,它从类开始,就好像数学要从公理开始一样。你不从公理开始,你就要从证明开始。直到你找到了一大堆相关证据后你才能归纳出公理,然后以公理结束。在程序设计方面存在着同样的事实:你要从有趣的算法开始。只有很好地理解了算法,你才有可能提炼出接口以让其工作。


下面,我们再来看C++的发明者Bjarne Stroustrup,在1998年IEEE采访时的一段话(全篇见这里),下面是其中的几段话:(我的翻译如下)

28_100929092934_1So what is OO? Certainly not every good program is object-oriented, and not every object-oriented program is good. If this were so, “object-oriented” would simply be a synonym for “good,” and the concept would be a vacuous buzzword of little help when you need to make practical decisions. I tend to equate OOP with heavy use of class hierarchies and virtual functions (called methods in some languages). This definition is historically accurate because class hierarchies and virtual functions together with their accompanying design philosophy were what distinguished Simula from the other languages of its time. In fact, it is this aspect of Simula’s legacy that Smalltalk has most heavily emphasized.

那么,什么是OO面向对象?当然,不会是所有的程序都是面向对象的,而且,也不是所有的面向对象程序就是好的。如果面向对象是好的,那么“Object-Oriented”应该成为“Good”的同义词,并且,OO概念只会成为一个假大空的口号,在你需要做出实际决定时只可能帮你那么一丁点。我倾向于把OOP等价于大量使用继承类和虚函数(某些语言的调用方法)。从历史上来说,这个定义是精确的,因为,在那个时候,只有类的继承和虚函数一起存在的设计哲学,才能把Simula和其它语言分别开来。事实上,Smalltalk相当地强调着这种Simula的遗留问题。

Defining OO as based on the use of class hierarchies and virtual functions is also practical in that it provides some guidance as to where OO is likely to be successful. You look for concepts that have a hierarchical ordering, for variants of a concept that can share an implementation, and for objects that can be manipulated through a common interface without being of exactly the same type. Given a few examples and a bit of experience, this can be the basis for a very powerful approach to design.

用继承类和虚函数来定义OO在实际上可以让很多OO指导性的东西更能成功一些。在解决问题时,寻找的那些有层级次序的对象,以应对不同对象也可以重用同一个实现,并且对象可以被某个共同的接口来操作而不需要完全相同的类型。在你了解了一些示例和拥有了一些经验后,OO可以成为Design的一个强有力的基础。

However, not every concept naturally and usefully fits into a hierarchy, not every relationship among concepts is hierarchical, and not every problem is best approached with a primary focus on objects. For example, some problems really are primarily algorithmic. Consequently, a general-purpose programming language should support a variety of ways of thinking and a variety of programming styles. This variety results from the diversity of problems to be solved and the many ways of solving them. C++ supports a variety of programming styles and is therefore more appropriately called a multiparadigm, rather than an object-oriented, language (assuming you need a fancy label).

然后,并不是每一个对象都自然地有效地适合继承,并不是每一个对象间的关系都是继承,也并不是每一个问题的最佳解决途径需要主要地通过对象。例如,很多问题主要是算法问题(译注:如业务逻辑,数据流等)。我们知道,一个一般性的编程语言都应该有能力支持不同的思路和不同的编程风格。这样,对于问题的多样性,我们可以使用许许多多不同的的方法去解决他们,这就产生了很多的不同解法。C++支持编程风格的多样性,因此,C++叫做“多范式  multi-paradigm”会更合适一些,而不是一个面向对象语言。

我个人在看过这些言论后,我先不管“面向对象是不是一个骗局”,不过从某种角度上来看的确是有些问题的,C++、OO、XML、SOA、网格计算等等诸如此类的东西的确被挂上了神圣的光坏。这些东西出来的时候总是只有一种赞美的声音。无论好坏,只有一种声音总是令人恐怖的,无论好坏,有不同的声音总是好的,每当这个社会或是我们的IT界大张旗鼓地鼓吹或是信仰某些东西,却没有任何一点不同意见的时候,我就会感到一种莫名的恐慌。我知道,这是我们从小受到的那种“非黑即白”的价值观教育所致,事物要么全是好的,要么全是不好的。其实任何事物都是有好有不好的,C++,敏捷开发,CMMi,OO,设计模式,重构,等等等等,他们都有好的也有不好的,关键看你怎么来使用。这个世界只有适合不适合的东西,不会出现放之四海皆准的东西,也不可能出现一种可以解决所有问题的东西,如果有,那么这种东西必然是一种宗教性质的用来洗脑的东西。

所以,每当在我身边看到或听到那些只有一种声音有如“电视购物”或是“新闻联播”之类的宣传或是鼓动的时候,我就感到很一种莫名的反感…… 不多说了,还是交给大家来评价吧。我仅以此篇文章献给那些OO-Oriented,Design Pattern-Oriented,Agile-Oriented,Process-Oriented,等等有着宗教信仰一般的人和事。

 

原文链接:http://coolshell.cn/articles/3036.html#more-3036

c/c++中关于struct内存对齐问题

716次浏览

题:

struct st1{                                       struct st2{
          int i;                                                 char c;
          char c;                                             int i;
          short s;                                            short s;
       };                                                   };

上述两结构体在内存中占用字节是多少,即sizeof(struct st1)=?, sizeof(struct st2)=?
解:8, 12

思考:这是struct结构的内存对齐问题,结构体的内存布局依赖于CPU、操作系统、编译器及编译时的对齐选项。其主要有:

1)结构体内部成员对齐
对于结构体内部成员,通常会有这样的规定:各成员变量存放的起始地址相对于结构的起始地址的偏移量必须为该变量的类型所占用的字节数的倍数。但是也可以看到,有时候某些字段如果严格按照大小紧密排列,根本无法达到这样的目的,因此有时候必须进行padding。各成员变量在存放的时候根据在结构中出现的顺序依次申请空间,同时按照上面的对齐方式调 整位置,空缺的字节编译器会自动填充也就是padding。如下图所示:

662029145240471388

图中st1,第一个为int型,占用4个字节,第二个为char型,其偏移量为4,char所占的字节数为1,则偏移量是其占用字节数的倍数,则仅列其后,第三个为short型,占用字节数为2,前面已有字节为5,不是2的倍数,所以char后面padding一个字节,使得short的其实地址为6,所以对齐后,共占用8个字节。同理可得str2占用12个字节

2)结构体之间对齐(此并不是考虑结构体内部的对齐,而是一组结构体的对齐,在计算单个结构体占用字节时并不考虑)

考虑整个结构体的对齐需求。ANSI C标准规定结构体类型的对齐要求不能比它所有字段中要求最严格的那个宽松,可以更严格。实际上要求结构体至少是其中的那个最大的元素大小的整数倍。因为有时候我们使用的是结构体数组,所以结构体的大小还得保证结构体数组中各个结构体满足对齐要求,同时独立的结构体与结构体数组中单个结构体的大小应当是一致的。


每个特定平台上的编译器都有自己的默认“对齐系数”(也叫对齐模数32位机上是8)。程序员可以通过预编
译命令#pragma pack(n),n=1,2,4,8,16 来改变这一系数,其中的n 就是你要指定的“对齐系数”。

指定对齐:
一般的,可以通过下面的方法来改变缺省的对齐条件:
使用伪指令#pragma pack(n),编译器将按照n个字节对齐;
使用伪指令#pragma pack(),取消自定义的字节对齐方式;
注意:如果#pragma pack(n)中指定的n大于结构体中最大的成员的size,则其不起作用,结构体仍然按照size最大的成员进行对齐。

c/c++ struct按位分配成员

204次浏览

一个普通的c/c++中的struct如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct A
{
int d1;
int d2;
int d3;
int d4;
bool b1;
bool b2;
bool b3;
bool b4;
bool b5;
bool b6;
bool b7;
bool b8;
};

如果这里有一个大数组,如100万个A结构数组,肯定会想节省内存的。数据结构在固定的时候,还有什么办法吗?当然有了,就是按位分配成员。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct A
{
int d1;
int d2;
int d3;
int d4;
bool b1:1;
bool b2:1;
bool b3:1;
bool b4:1;
bool b5:1;
bool b6:1;
bool b7:1;
bool b8:1;
};

在成员名后面添加冒号和数字,就代表这个成员占用多少bit位。这样原来的一个bool是占一个字节8bit位的,现在就只占了1bit位了,而8个bool才占一个字节,是不是节省了N多内存了呢。
bool完全可以这么做的原因,是因为bool只有两个值true(1)、false(0),一个bit位完全可以表示并不失真。当然,任意类型在语法上都是可以这么指定位的。如d1、d2你明显知道取值是在0-1000以内,用short都太浪费,用char又不足。那么就可以分配11个bit位(高位符号位,也可以用unsigned d1:10),以次类推。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct A
{
int d1:11;
int d2:11;
int d3;
int d4;
bool b1:1;
bool b2:1;
bool b3:1;
bool b4:1;
bool b5:1;
bool b6:1;
bool b7:1;
bool b8:1;
};

这么做优点很明显了,节省内存,尤其是在大数组的情况下,可以节省很多。唯一不确定的是,大家都知道有一个struct内存对齐的东东。

c/c++结构相同但名字不同的的struct如何转换

258次浏览

比如一个struct1和struct2,里面的成员都是一样的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct struct1
{
int a;
int b;
float c;
};
 
struct struct2
{
int a;
int b;
float c;
};
 
struct1 T1;
struct2 T2;

现在如何把T2转换为struct1类型的?

试试

1
struct1 t1 = *(struct1*)&T2

不过这实际调用了struct1的copy构造函数,T2并未实际变为struct1类型,而是做为参数构造出一个全新的t1

c++形参int与const int等价

236次浏览

在c++形参中 int 等价于 const int。

因为如果你的函数的参数是int类型变量,那么实参传给形参的是值,所以函数内不管如何去改变该参数都不会影响实参。

如果你在这个形参前加以const修饰,那么形参值在你函数内将不能被改变。

不过由于函数参数的类型限定总是针对调用者而言的,因此对于int参数前面加不加const对于调用者而言都一样。所以加const反而会显得有些冗余。

64af63cd83ee47e2b07aacf096b7de54