⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 c++编程思想 -- 第2章 笔记(2).txt

📁 C&C++论坛精华
💻 TXT
字号:
作者:rick1126
email: rickzhang@sina.com
日期:2001-7-17 10:19:32
2.2 一个袖珍C库

   【C函数库的数据封装方式】

     . struct	用于处理一组特性
     . C库	一组struct + 一组活动在这些struct上的函数

   【一个变长数组模拟的例子】

     . 涉及操作:
       - 使用一个结构保存数据, 附加一组操作维护数据
       - C对于内存的分配和使用及其为维护
       - 动态连接库的导出

     . 相关代码

	//stash.h

	#if!defined(STASH_H)
	#define STASH_H

	typedef struct STASHtag{
		int		size;	//每一个元素的大小
		int		quantity;	//索引总长
		int		next;	//下一个索引
		unsigned char	*storage;	//数值指针, 动态分配
	}Stash;

	extern "C" {

		void  WINAPI initialize( Stash* S, int Size );
		void  WINAPI cleanup( Stash* S );
		int   WINAPI add ( Stash* S, void* element );
		void* WINAPI fetch( Stash* S, int index );
		int   WINAPI count( Stash* S );
		void  WINAPI inflate( Stash* s, int increase );

	}

	#endif//STASH_H

	; ch2_stash.def : Declares the module parameters for the DLL.

	LIBRARY      "ch2_stash"
	DESCRIPTION  'ch2_stash Windows Dynamic Link Library'

	EXPORTS
			initialize
			cleanup
			add
			fetch
			count
			inflate
	    ; Explicit exports can go here

	===>
	1) 有关结构
	   - 使用XXXTag或者tagXXX进行typedef
	   - 使用US_XXX, *PUS_XXX定义实际的结构名称和结构指针名称
	   * 当然都是从一位长期从事C编程的同事那里获得的经验 ^_^
	2) 有关导出的函数
	   - 使用extern "C" 返回类型 WINAPI 函数名( ... )进行声明
	   - 相关的函数导出方式
	     ==> 使用.DEF文件
	         
	         在声明使用 WINAPI, extern "C" 表示使用C执行方式, 考虑到兼容性
	         在DEF文件的EXPORTS段导出这些函数

	     ==> __declspec(dllexport) 导出数据, 函数, 类或者类成员函数
	         
	         函数例子:
		
		//导出函数
		__declspec(dllexport) void __cdecl Function1(void);
		
		//导出一个类中的接口--公共成员
		class __declspec(dllexport) CExampleExport : public CObject
		{ ... class definition ... };
                   
	         通常使用宏定义给出一个好用的预定义
		
		#define DllExport   __declspec( dllexport ) 

		将函数名称存储在DLL的导出列表中, 如果你要优化该表格的大小, 使用.DEF文件方式.


	//stash.cpp

	#include "stdafx.h"
	#include "stash.h"
	#include "stdlib.h"
	#include "string.h"
	#include "stdio.h"
	#include "assert.h"

	//描述: 初始化变长数组
	void WINAPI initialize( Stash* S, int Size )
	{
		S->size = Size;
		S->quantity = 0;
		S->storage = 0;
		S->next = 0;
	}
	//描述: 清空编程数组
	void WINAPI cleanup( Stash* S )
	{
		if ( S->storage ){
			puts( "free storage" );
			free( S->storage );
		}
	}
	//描述: 添加元素到变长数组
	int WINAPI add ( Stash* S, void* element )
	{
		if ( S->next >= S->quantity )
			inflate( S, 100 );
		memcpy(  &( S->storage[ S->next * S->size ] ), element, S->size );
		S->next ++;
		return ( S->next - 1 );
	}
	//描述: 获得指定索引的数组元素
	void* WINAPI fetch( Stash* S, int index )
	{
		if ( index >= S->next || index < 0 )
			return 0;
		return &( S->storage[index * S->size ] );
	}
	//描述: 变长数组计数
	int WINAPI count( Stash* S )
	{
		return S->next;
	}
	//描述: 数组变长
	void WINAPI inflate( Stash* S, int increase )
	{
		void* v=realloc( S->storage, ( S->quantity + increase ) * S->size );
		assert(v);
		S->storage = (unsigned char*)v;
		S->quantity += increase;
	}

	==>
	1) 函数实现
	   - 其实这个函数主要使用一个连续的缓冲区和定长的间隔存放指定的元素, 整个库维护一个结构
	2) 相关的概念
	   - C的内存分配(局部)函数
		函数名称		用途
		-----------------------------------------------------------------------------
		_alloca		从数据栈分配内存
		
		calloc		为数组分配缓存, 初始化每一个字节为0
		_calloc_dbg	调试版本下的 calloc 

		_expand		在不移动的前提下扩展缩小内存
		_expand_dbg	调试版本下的 _expand
		
		free		释放内存
		_free_dbg		调试版本下的 _free

		_get_sbh_threshold	返回申请上限

		_heapadd		添加内存到堆
		_heapchk		检测堆的连续性
		_heapmin		释放堆中的空闲内存
		_heapset		使用指定值填充堆内存入口
		_heapwalk		返回堆中的每一个入口的信息

		malloc		从堆分配内存
		_malloc_dbg	调试版本下的 malloc

		_msize		返回申请内存块的大小
		_msize_dbg	调试版本下的 _msize

		_query_new_handler	返回当前子函数句柄的地址, 子函数由 _set_new_handler 设置 
		_query_new_mode	_set_new_mode 的句柄模式

		realloc		重新申请内存块新的大小
		_realloc_dbg	调试版本下的 realloc

		_set_new_handler	使得错误处理机制在 new 操作失败的时候使能并且使能 STL 库
		_set_new_mode	设置 malloc 的新的句柄模式
		_set_sbh_threshold	设置内存分配上限


	// client.cpp : Defines the entry point for the console application.
	//

	#include "stdafx.h"
	#include "iostream.h"
	#include "assert.h"
	#include "windows.h"
	#include "../stash.h"

	#define BUFSIZE	80

	int main(int argc, char* argv[])
	{
		Stash intStash, stringStash;
		int i;
		FILE *file;
		char buf[BUFSIZE];
		char* cp;

		initialize( &intStash, sizeof(int) );
		for ( i=0; i<100; i++ )
			add( &intStash,&i );

		initialize( &stringStash, sizeof(char)*BUFSIZE );
		file = fopen( "..\\readme.txt", "r" );
		assert( file );
		while ( fgets( buf, BUFSIZE, file ) )
			add( &stringStash, buf );
		fclose( file );

		for ( i=0; i<count( &intStash); i++ )
			cout << (int)fetch( &intStash, i ) << endl;

		i=0;
		while ( ( cp = (char*)fetch( &stringStash, i++ ) )!=0 )
			cout << (char*)fetch( &stringStash, i-1 ) << endl;

		cleanup( &intStash );
		cleanup( &stringStash );

		return 0;
	}

	==> 客户端应用程序
	1) void* 的含义
	   通过类型转换, void* 可以转换任何标准数据类型, 由此该变长数组可以不拘一格的支持相应的数据类型, 只是类型检查和转换的工作由客户端支持

〖个人理解〗

  这里用到一个一些C里面的如何建立函数库, 使用函数库和相应的内存以及建库的常用技巧. 特别是内存管理, 有了一个堆的概念. 堆用于申请一个连续内存, 然后提供应用程序的动态内存分配函数申请堆内的内存, 同时应用程序需要维护堆内部的内存管理.

  在C++特别是VC里面, 传统的内存分配函数已经为new/delete代替, 而且也无需先申请一个堆. 不过如果开发COM还是会发现一些传统内存分配函数的影子, 毕竟从兼容性考虑, 2#标准的COM还是需要偏向或者生成C兼容的中间产物或者结果的.

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -