存档在 ‘C++’ 分类

预编译头编译链接过程

2014年8月29日
  • 预编译头文件
  • 在编写程序的时候,有一些头文件被许多文件用到。例如:windows.h这样的系统头文件。如果不采用预编译头文件,那么每个包含windows.h的文件都需要编译这个文件。这将会增加编译时间。正是为了解决这个问题,Microsoft Visual C++提出了预编译头文件的解决方法。

    所谓预编译头文件,即将一些指定的头文件预先编译成二进制文件,为了与其他二进制文件区别开来。预编译的头文件生成的二进制文件以.PCH结尾。在Visual C++中默认使用预编译头文件,预编译头文件默认为stdafx.cpp,将那些常用的头文件都放进stdafx.h中去即可。而stdafx.cpp则只包含stdafx.h一个文件。其他所有文件都默认为调用预编译头文件,也即每个.cpp文件都需要在第一行包括#include “stdafx.h”这样的文件。

    当然我们也可以自己定义预编译头文件。例如创建这样两个文件:MyStdafx.h,MyStdafx.cpp。然后将需要提前编译的头文件包含在MyStdafx.h中去,并将MyStdafx.h包含到MyStdafx.cpp中去。将通过Properties\C/C++\Precompiled Header中选择Create Precomliled Header即可。

    Visual C++的其他文件都是默认使用预编译头文件的,并且默认指定了使用的预编译头文件stdafx.h,所以我们在使用了预编译头文件的.cpp文件中必须首行包含stdafx.h。当然我们如果不想使用stdafx.h的话,可以通过Properties\C/C++\Precompiled Header中设置Not Using Precompiled Headers。如此设置,即可以不包含stdafx.h文件了。

    当设定好了预编译头文件时,我们来看一下它是如何工作的。编译都是以.cpp文件为单位开始的,当编译器发现有包括预编译头文件stdafx.h时,并不是将这个文件通过预编译包含进来,而是代替以相就的.pch文件。这样将大大节约编译时间。

  • 编译链接过程
  • 当一段代码写好之后进行Build时,主要完成以下三个步骤。

    • 1. 预处理阶段:即由预处理器对预处理指令(#include、#define和#if)进行处理,在内存中输出翻译单元(一种临时文件)。还有预编译头文件并生成pch文件。
    • 2. 编译阶段:编译器将内存中的预处理单元,及.pch文件,并编译.cpp文件成生对应的二进制.obj文件(里面包含的是程序的相对地址)。
    • 3. 链接阶段:链接器将所有obj文件以及所用到的标准库链接成可执行文件.exe。
    • 6597533760563904363

Code分享

2014年5月20日

1299

#include <stdio.h>
#include <stdlib.h>

int BSearchUpperBound(int array[], int low, int high, int target)
{
    if(low > high || target >= array[high]) return -1;

    int mid = (low + high) / 2;
    while (high > low)
    {
        if (array[mid] > target)
            high = mid;
        else
            low = mid + 1;

        mid = (low + high) / 2;
    }

    return mid;
}


int main()
{
    int n,m;
    int a[100];
    scanf("%d %d",&n,&m);
    for(int i = 0; i <n; ++i)
        scanf("%d",&a[i]);
    printf("%d\n",a[BSearchUpperBound(a,0, n-1,m)]);
    return 0;

}

1298

#include <stdio.h>
#include <stdlib.h>
#define min(a,b) ((a) >(b)?(b):(a))
int coin[]= {2,3,5};
int fun(int n)
{
    if(n == 1)
        return 1;
    else if(n==3)
        return 1;
    else if(n==5)
        return 1;
    else
    {
        int m = 0xffff;
        for(int i = 0; i < 3; i++)
        {
            if(n >= coin[i])
                m = min(m,fun(n-coin[i]) + 1);
        }
        return m;
    }
}

VS2012-2005版本.sln文件头部标志信息

2014年5月20日

高版本的VS能够对低版本的VS解决方案进行转换后打开,而低版本的VS则不能直接打开高版本的VS解决方案,其实就解决方案中某个工程来说,各个版本的编译器并没有差别。
所以在需要低向高转换的时候,可以尝试一下修改.sln解决方案文件,或许问题就解决了。
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2012

Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010

Microsoft Visual Studio Solution File, Format Version 10.00
# Visual Studio 2008

Microsoft Visual Studio Solution File, Format Version 9.00
# Visual Studio 2005

一个循环队列帧完整性匹配算法

2014年4月27日

今天早上思考了下关于循环队列中帧匹配的问题,以前在其他项目中,都是用了两个线程的单生产者-单消费者模式,但是有一定的弊端,那就是效率相对来说不是太高,而且线程同步也相对复杂。以下是今天早上思考的结论,算法时间复杂度O(n),而且上锁,解锁的次数大幅度降低。

看似一段简单的代码,个人觉得还是比较有技巧的,重点在于每次读取两个字节,而只处理一个字节。以帧头FB,FD,帧尾为FC,FE(都为16进制)为例,通过调整参数door,越大则及时性不足,越小则消耗更多时间。




const int FRAME_LENGTH = 11;
const char HEAD_FIRST = 0xfb;
const char HEAD_SECOND = 0xfd;
const char TAIL_FIRST = 0xfc;
const char TAIL_SECOND = 0xfe;
const int DOOR = FRAME_LENGTH ;
void run()
{
	bool bstart;
	int cnt;
	char tmp[9];
	char buff[2];
	while(true)
	{
		mutex.lock();
		while(cir.size() <= DOOR )
			con.wait(&mutex);

		if(!bstart)
		{
			while(true)
			{
				if(cir.size() < 2) continue;
				cir.get(buff, 2);
				if(!(buff[0] ^ HEAD_FIRST) && !(buff[1] ^ HEAD_SECOND))
				{
					bstart = true;
					cnt = 0;
					cir.remove(2);
					break;
				}
				else					
					cir.remove(1);
			}
		}
		else
		{
			while(true)
			{
				if(cir.size() < 2) continue;
				cir.get(buff,2);
				if(!(buff[0] ^ TAIL_FIRST) && !(buff[1] ^ TAIL_SECOND))
				{
					/*至此,检索到一个完整的有效数据序列,并保存在tmp中*/
					bstart = false;
					cnt = 0;
					cir.remove(2);
					break;
				}
				else
				{
					cir.remove(1);
					tmp[cnt++] = buff[0];
				}

			}
		}
		mutex.unlock();
				
	}


}

为shared_ptr中对象增加安全的强有力技术

2014年4月16日

shared_ptr<>是在堆中对象的wraper。有时候,由于历史原因或者库接口原因,不支持智能指针参数,我们需要从shard_ptr中提取出原始指针,但是原始指针就会增加不安全因素,因为说不定就在某个函数内被delete,然而其他地方仍旧使用该内存区,导致内存泄露。

#include <boost/shared_ptr.hpp>
#include <iostream>

class A
{
private:
	class deleter
	{
	public:
		void operator()(A* p)
		{
			std::cout<<"delete A"<<std::endl;
			delete p;
		}
	};
public:
	static boost::shared_ptr<A> creatA()
	{
		boost::shared_ptr<A> p1(new A(),A::deleter());
		return p1;
	}
protected:
	 virtual ~A(){std::cout<<"析构 A"<<std::endl;}
};

int _tmain(int argc, _TCHAR* argv[])
{
	{
		boost::shared_ptr<A> p = A::creatA();
#if 0
		A* a = p.get();
		delete a;
#endif 
#if 0
		A* a = new A();
		delete a;
#endif
	}
	system("pause");;
	return 0;
}

在上面中,让析构函数为保护(protected)权限,这样可以有效避免直接析构原始指针。为了能够有效清理对象,需要定义一个删除器仿函数,如代码中的deleter.

		A* a = p.get();
		delete a;

当使用智能指针获取原始指针并delete时,将会出错。

		A* a = new A();
		delete a;

甚至,你不能在栈上定义A的对象,更不能delete A的原始指针。

这当然对shared_ptr使用来说,更安全。但是意味着使用也更受限。

using namespace ***与 using ***区别

2014年4月15日
以boost为例子,使用代码来展示区别
在使用using ***时
using boost::asio::ip::tcp;
则使用tcp下的命名空间,就编程时可以简化使用
tcp::resolver resolver(io_service);
tcp::resolver::query query(argv[1], "daytime");

 

  如果使用using namespace ***的话
using namespace  boost::asio::ip;
   在编程中使用时,可以直接使用ip下的其他类或者全局对象
 tcp::resolver resolver(io_service);
 tcp::resolver::query query(argv[1], "daytime");

综上得出:using ***仅仅为了简化限定域,比如using boost::asio::ip::tcp,并没有将tcp中的所有类型或者全局对象导入。

using namespace ***则做更多的工作,using namespace std,则会把std中的所有类型和全局对象都导入。

另外在头文件中尽量少用using namespace ***,可以在头文件中使用完全限定域,在实现文件中再使用。

error C4996解决办法

2014年4月14日

错误 1 error C4996: ‘ctime’: This function or variable may be unsafe. Consider using ctime_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.

1.原因

原因是Visual C++ 2012 使用了更加安全的 run-time library routines 。新的Security CRT functions(就是那些带有“_s”后缀的函数)。

2.解决方案:

方法一:将原来的旧函数替换成新的 Security CRT functions。

方法二:用以下方法屏蔽这个警告:

1. 在预编译头文件stdafx.h里(注意:一定要在没有include任何头文件之前)定义下面的宏:

#define _CRT_SECURE_NO_DEPRECATE

2. 或声明

#param warning(disable:4996)

3. 更改预处理定义:

项目->属性->配置属性->C/C++ -> 预处理器 -> 预处理器定义,增加:

 _CRT_SECURE_NO_DEPRECATE

方法三:自动 替换为安全的 CRT 函数,简便方法为:

在预编译头文件 stdafx.h 里(同样要在没有include任何头文件之前)定义下面的宏,在链接的时候便会自动将旧函数替换成 Security CRT functions 。

#define _CRT_SECURE_NO_DEPRECATE

#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1

3.错误原因解释:

这种微软的警告,主要因为那些C库的函数,很多函数内部是不进行参数检测的(包括越界类的),微软担心使用这些会造成内存异常,所以就改写了同样功能的函数,改写了的函数进行了参数的检测,使用这些新的函数会更安全和便捷。关于这些改写的函数你不用专门去记忆,因为编译器对于每个函数在给出警告时,都会告诉你相应的安全函数,查看警告信息就可以获知,在使用时也再查看一下MSDN详细了解。

双缓冲循环队列DLL

2014年4月7日

周末无事,写了个循环队列的dll,一方面考虑到后面项目中需要这个数据结构,另外很久没写程序了,就当练下手。

这个循环队列采用的是双缓冲,按块复制,相对来说效率还是比较高,先给出头文件:

#pragma once
#ifndef CIRCULAR_QUEUE
#define CIRCULAR_QUEUE

#ifdef DLL_IMPLEMENT
	#define DLL_API __declspec(dllexport)
#else
	#define DLL_API __declspec(dllimport)
#endif

#include <string>

class DLL_API CircularQueue
{
public:
	CircularQueue(size_t size);
	~CircularQueue();

	/** 写入p开始的n字节,返回true正确,false失败 */
	bool enqueue(const char *p, size_t n);
	/** 读取n字节到dest,返回true正确,false失败 */
	bool dequeue(char *dest, size_t n);
	/** 删除n字节 ,返回true正确,false失败*/
	bool remove(size_t n);
	/** n个字节以字符串形式返回,返回true正确,false失败*/
	std::string readString(size_t n);

	/** 循环队列是否空 */
	bool isEmpty() const;
	/** 返回当前有效的数据字节数  */
	size_t getLength() const;
	/** 返回循环队列容量 */
	size_t getCapacity() const;
	/** 返回循环队列空闲字节数 */
	size_t space() const;
	/** 写入到缓冲区的总字节数 */
	unsigned long bytesCounter(bool clear = false);

	/** 返回循环队有效区头地址 */
	const char *getStart() const;

	/**打印循环队列中有效数据*/
	void printQueueOnlyUsed(bool displayDir) const;
	/**打印循环队列所有数据*/
	void printQueueAll(bool displayDir) const;
private:
	CircularQueue(const CircularQueue& ) {}
	CircularQueue& operator=(const CircularQueue& ) { return *this; }
	char *buf;
	size_t m_max_size;
	size_t m_used;
	size_t m_head_index;
	size_t m_tail_index;
	unsigned long m_enqueue_count;
};
#undef DLL_IMPLEMENT
#endif

另外提供头文件和dll的下载地址:

  双缓冲循环队列 (unknown, 1,016 hits)