2018-11-25 21:41:37 qq_34917728 阅读数 1748

利用OpenCV实现C语言中二维数组的图像显示

在利用C语言处理数组时,我们经常需要将图片的二维数组进行可视化;所以本文分享了一种利用OpenCV读取和生成图片的方法。

1. 利用OpenCV读取图片

前提条件:

  • 本文的程序是在visual Studio2012中执行的;
  • vs已经配置好调用OpenCV需要的设置,如包含库目录,库文件等;
  • 在当前文件夹下有一幅名为“1”的.jpg格式的图片;

在满足上述条件的情况下,执行如下程序,可读取1.jpg这幅图片。

如何读取不同的图片,如何变通?

关键的命令是:

“imread(filename,option)”

变通只要将filename变换为对应的“文件名”或者“路径+文件名”即可,注意程序里的双引号要用英文格式的。通过选择option可用不同的方式读取图片,本文是灰度读取“IMREAD_GRAYSCALE”。

程序:

#include "opencv.hpp"
#include "highgui.hpp"
using namespace cv;
int main()
{
    Mat image=imread("1.jpg",IMREAD_GRAYSCALE);
    cvNamedWindow("source image");
    imshow("source image",image);
    waitKey();
}

运行结果如下:

完整的结果:

2. 利用OpenCV实现C语言中二维数组的可视化

仔细理解第一部分的程序,你就可以知道OpenCV显示图像是通过这一命令实现的:

“imshow("source image",image)

其中"source image"是打开图片窗口的名称,image就是你的Mat类型的变量。

    对于C语言的二维数组,它一般为数值类型(整形、浮点型、字符串等等),所以无法直接进行可视化,需要将二维数组的类型赋值给Mat类型的变量。

给Mat类型的变量(假设为B)进行赋值需要执行命令:

“B.data[i] = 255;”

这里涉及到对象(结构体)的知识,你不理解也没关系,只要认为B.data就是Mat类型变量的数组就可以,如B.data[0],就是访问数组的第一个元素;B.data是一个一维数组,所以二维数组进行赋值时需要将元素对应到一维数组上。

实现程序如下:

#include "opencv.hpp"
#include "highgui.hpp"
#include "math.h"
using namespace cv;		

int main()
{
    const int M = 300,N = 400;	//图片的行、列像素
    int data[M][N] = {0};		//创建一个200×200的数组
    //下面循环产生一个沿对角线变大的数组
    for(int i=0;i<M;i++)
        for(int j=0;j<N;j++)
            data[i][j] = (int)(255*sqrt(i*i+j*j)/500);	
    Mat B = Mat(M,N,CV_8UC1);	//创建一个M×N大小的变量
    for(int i=0;i<M;i++)
    {
    for(int j=0;j<N;j++)
        {
            B.data[i*N+j]=data[i][j];
        }
    }
    cvNamedWindow("source image1");
    imshow("source image1",B);
    waitKey();
    return 0;
}

执行结果如下:

完整的结果:

3. 结论

利用Mat类型的.data性质进行变量访问,就可以实现二维数组赋值给Mat类型变量了,进而利用OpenCV的imshow命令将结果可视化。

2018-03-08 23:12:00 MobiuX 阅读数 2012

用C语言实现的科学计算器,支持2种常量,10种基本函数,Ans寄存器。相对来说拓展性应该是不错的,思路是首先化简复杂名称的函数名和常量名,然后把表达式转换成前缀表达式,再直接处理前缀表达式即可。因此对运算符和括号优先级的处理比较容易完美实现,执行效率也比较高,且无论输入表达式有多么复杂,只要确保输入缓冲区(定义在Analytic.h中)够大后面的都好说。

Analytic.c 运算库源文件,考虑到了移植方便的问题:
//Create Date: 2018-3-4  
//Author: Charming  
//File: Analytic.c  
//Version: 1.7.2  
//Last edit Date: 2018-3-13  
//Directions: 表达式解析库源文件  
//  v1.0.0: 基本前缀表达式转换  
//  v1.1.0: 支持区分正负号与加减运算符的功能  
//  v1.2.0: 新增额外的多字节函数化简,并支持转换相应的前缀表达式  
//  v1.3.0: 新增支持符号常量参与表达式转换  
//  v1.3.5: 区分正负号与加减运算符功能的完善  
//  v1.4.0: 增加计算功能  
//  v1.5.0: 增加函数计算功能  
//  v1.6.0: 增加常数项功能  
//  v1.6.2: 增强表达式错误辨别能力  
//  v1.6.5: 提高常数精度  
//  v1.7.0: 新增三角函数的预期计算结果趋近于0或者不存在时的单独处理功能  
//  v1.7.2: 优化常数项遇到负号时的计算问题  
//  v1.7.7: 解决单目运算符计算顺序的Bug

#include "Analytic.h"  
#include <string.h>  
#include <math.h>  

char *Expression = 0;//中缀表达式指针  
a_size_t E_Ptr = 0;//中缀表达式操作位置标记  

char *Polish_Notation = 0;//前缀表达式指针  
a_size_t P_Ptr = 0;//前缀表达式操作位置标记  

double Calc_ANS = 0;//答案寄存器  

					/*基本常数项(数值)Start*/
const_mode double _const_Euler_ = 2.718281828459045;//自然常数e  
const_mode double _const_Pai_ = 3.141592653589793;//圆周率π  
												  /*基本常数项(数值)End*/

												  /*基本常数项(符号)Start*/
const_mode char Ans[] = "Ans";//上一运算答案  
#define S_Ans 'A'  
const_mode char Pai[] = "Pai";//圆周率π的多字节符号  
#define S_Pai 'P'  
const_mode char Euler[] = "_e_";//自然常数,一般都使用e而不是_e_,  
								//复杂名的写法仅为避免名称重复陷入死循环,  
								//实际使用时直接用e即可  
#define S_Euler 'e'  
								/*基本常数项(符号)End*/

								/*基本函数项Start*/
const_mode char Ln[] = "ln";//自然对数  
#define S_In 'I'  
const_mode char Exp[] = "Exp";//自然指数  
#define S_Exp 'E'  
const_mode char Log[] = "log";//以10为底的对数  
#define S_Log 'L'  
const_mode char Sin[] = "Sin";//正弦函数  
#define S_Sin 's'  
const_mode char Cos[] = "Cos";//余弦函数  
#define S_Cos 'c'  
const_mode char Tan[] = "Tan";//正切函数  
#define S_Tan 't'  
const_mode char Sqrt[] = "Sqrt";//开方函数  
#define S_Sqrt 'Q'  
const_mode char ArcSin[] = "Arcsin";//反正弦函数  
#define S_ArcSin 'S'  
const_mode char ArcCos[] = "Arccos";//反余弦函数  
#define S_ArcCos 'C'  
const_mode char ArcTan[] = "Arctan";//反正切函数  
#define S_ArcTan 'T'  
									/*基本函数项End*/

const_mode char *ComplexOperators[] = {//多字节函数名列表,顺序从长到短,有利于逻辑实别  
	ArcSin, ArcCos, ArcTan, Exp, Log, Sin, Cos, Tan, Sqrt, Ln
};

const_mode char SimpleOperators[] = {//单字节函数名列表,与多字节顺序需一一对应  
	S_ArcSin, S_ArcCos, S_ArcTan, S_Exp, S_Log, S_Sin, S_Cos, S_Tan,S_Sqrt, S_In
};

const_mode char *ComplexConstants[] = {//多字节常量名列表,顺序从长到短,有利于逻辑实别  
	Ans, Pai, Euler
};

const_mode char SimpleConstants[] = {//单字节常量名列表,与多字节顺序需一一对应  
	S_Ans, S_Pai, S_Euler
};

struct {
	char data[SymbolMax];
	w_size_t top;
}Symbol_Stack;//运算符栈  

struct {
	double data[NumberMax];
	w_size_t top;
}Number_Stack;//运算符栈  

typedef enum
{
	Symbol, Number
}Stack_type;//栈类型  

typedef enum
{
	isNumber,
	isNumberOrDot,
	isBrackets,
	isBracketLeft,
	isBracketRight,
	isOperatorsLevel3,
	isOperatorsLevel2,
	isOperatorsLevel1,
	isOperatorsLevel0,
	isConstants
}Symbol_type;//符号类型  

void Init_Stack(Stack_type type)//堆栈初始化  
{
	switch (type)
	{
	case Symbol:
		Symbol_Stack.top = -1;
		break;
	case Number:
		Number_Stack.top = -1;
		break;
	default:
		break;
	}
}

Analytic_type Symbol_Push(void)//中缀表达式中的当前字符入栈  
{
	if (Symbol_Stack.top < SymbolMax - 1)
		Symbol_Stack.data[++Symbol_Stack.top] = Expression[E_Ptr--];
	else
		return Conv_OverFlow;
	return Conv_Ok;
}

Analytic_type Symbol_Pop(void)//从运算符栈出栈到前缀表达式  
{
	if (Symbol_Stack.top >= 0)
		Polish_Notation[++P_Ptr] = Symbol_Stack.data[Symbol_Stack.top--];
	else
		return Conv_Exception;
	return Conv_Ok;
}

w_size_t Get_StringLen(char *str, const w_size_t Maxlen)//计算表达式长度  
{
#if USESTRINGLIB == 0  
	w_size_t len = 0;
	while (str[len] != '\0' && len < Maxlen)
	{
		len++;
	}
	return len;
#else  
	return (w_size_t)strnlen(str, Maxlen);
#endif  
}

#if STRINGLIB_standard == 1  
char* my_strrev(char* s)
{
	char* h = s;
	char* t = s;
	char ch;

	while (*t++) {};
	t -= 2;//与t++抵消、回跳过结束符'\0'  

	while (h < t)//当h和t未重合时,交换它们所指向的字符  
	{
		ch = *h;
		*h++ = *t;    /* h向尾部移动 */
		*t-- = ch;    /* t向头部移动 */
	}
	return s;
}
#endif  

void Make_String_Reverse(char *str, const w_size_t Maxlen)//字符串反转  
{
	w_size_t len;
	len = Get_StringLen(str, Maxlen);
	if (str[len - 1] == ' ')
	{
		str[len - 1] = 0;
	}
#if STRINGLIB_standard == 1  
	my_strrev(str);
#else  
	strrev(str);
#endif  
}

unsigned char Check(char *chr, Symbol_type type)//判断字符或字符串的类型  
{
	a_size_t i;
	switch (type)
	{
	case isNumber:
		if (*chr >= '0' && *chr <= '9')
			return 1;
		break;
	case isNumberOrDot:
		if (*chr >= '0' && *chr <= '9' || *chr == '.')
			return 1;
		break;
	case isBrackets:
		if (*chr == '(' || *chr == ')')
			return 1;
		break;
	case isBracketLeft:
		if (*chr == '(')
			return 1;
		break;
	case isBracketRight:
		if (*chr == ')')
			return 1;
		break;
	case isOperatorsLevel0:
		for (i = 0; i < sizeof(SimpleOperators); i++)
			if (*chr == SimpleOperators[i])
				return 1;
		break;
	case isOperatorsLevel1:
		if (*chr == '^')
			return 1;
		break;
	case isOperatorsLevel2:
		if (*chr == '*' || *chr == '/')
			return 1;
		break;
	case isOperatorsLevel3:
		if (*chr == '+' || *chr == '-')
			return 1;
		break;
	case isConstants:
		for (i = 0; i < sizeof(SimpleConstants); i++)
			if (*chr == SimpleConstants[i])
				return 1;
		break;
	default:
		break;
	}
	return 0;
}

Analytic_type Convert_to_Polish(void)//中缀表达式到前缀表达式的转换程序  
{
	Analytic_type AnserReg;
	unsigned char tempflag;
	P_Ptr = -1;

	Init_Stack(Symbol);
	E_Ptr = (a_size_t)Get_StringLen(Expression, Exprlen);
	while (E_Ptr >= 0)//逆序处理  
	{
		if (Check(&Expression[E_Ptr], isNumber))//处理数值情况  
		{
			tempflag = 0;//这里tempflag用于处理一个特殊情况(见下面代码),  
						 //使用goto容易造成复杂的意外情况,因此改用单独设立的标志位和死循环配合实现  
			while (1)
			{
				while (tempflag == 1 || (E_Ptr >= 0) && Check(&Expression[E_Ptr], isNumberOrDot))
				{
					tempflag = 0;
					Polish_Notation[++P_Ptr] = Expression[E_Ptr--];//将数值或小数点转入前缀表达式  
				}
				if ((E_Ptr >= 0) && //中缀表达式中还有没处理的  
					((E_Ptr == 0 && Check(&Expression[E_Ptr], isOperatorsLevel3)) ||
						Check(&Expression[E_Ptr], isOperatorsLevel3) &&
						!(Check(&Expression[E_Ptr - 1], isNumber) || Check(&Expression[E_Ptr - 1], isConstants)) &&
						!Check(&Expression[E_Ptr - 1], isBracketRight)))
					//由于三级运算符('+' '-')可做正负号使用,因此对单独存在的三级运算符当做数的一部分处理,  
					//即如果当前字符是最后一个字符,且是三级运算符,或者这并不是最后一个字符,且下一个字符  
					//不是右括号、不是数字或常量的情况下,这个三级运算符需要当作数处理。  
					tempflag = 1;
				else
					break;
			}
			Polish_Notation[++P_Ptr] = ' ';//前缀表达式添加空格分隔符  
		}
		else if (Check(&Expression[E_Ptr], isConstants))//待处理的是常量  
		{
			Polish_Notation[++P_Ptr] = Expression[E_Ptr--];//将常量符号转入前缀表达式  
			if ((E_Ptr >= 0) && //中缀表达式中还有没处理的  
				((E_Ptr == 0 && Check(&Expression[E_Ptr], isOperatorsLevel3)) ||
					Check(&Expression[E_Ptr], isOperatorsLevel3) &&
					!(Check(&Expression[E_Ptr - 1], isNumber) || Check(&Expression[E_Ptr - 1], isConstants)) &&
					!Check(&Expression[E_Ptr - 1], isBracketRight)))
				//对符号单独处理,原理类似于常数的处理方法  
				Polish_Notation[++P_Ptr] = Expression[E_Ptr--];//将常量符号转入前缀表达式  
			Polish_Notation[++P_Ptr] = ' ';//前缀表达式添加空格分隔符  
		}
		else if (Check(&Expression[E_Ptr], isBracketRight))//待处理的字符是右括号  
		{
			if ((AnserReg = Symbol_Push()) != Conv_Ok)//压入运算符栈,如果异常,则返回异常情况,压栈过程同时E_Ptr也移动,不需要重复操作  
				return AnserReg;
		}
		else if (Check(&Expression[E_Ptr], isBracketLeft))//待处理的字符是左括号  
		{
			while (!Check(&Symbol_Stack.data[Symbol_Stack.top], isBracketRight))//如果运算符栈栈顶不是右括号  
			{
				if ((AnserReg = Symbol_Pop()) != Conv_Ok)//运算符出栈  
					return AnserReg;
				Polish_Notation[++P_Ptr] = ' ';//出一次加一个空格  
			}
			Symbol_Stack.data[Symbol_Stack.top--] = '\0';//封栈,删除栈顶外内容  
			E_Ptr--;//处理下一位  
		}
		else if (Check(&Expression[E_Ptr], isOperatorsLevel3))//待处理字符是'+'或'-'  
		{
			while (Symbol_Stack.top >= 0 &&
				!Check(&Symbol_Stack.data[Symbol_Stack.top], isOperatorsLevel3) &&
				!Check(&Symbol_Stack.data[Symbol_Stack.top], isBracketRight))
				//运算符栈非空,且栈顶不是')' '+' '-'的时候要先出栈,实际上这里是比较优先级,'*' '/' '^'的优先级更高,所以要先出栈  
			{
				if ((AnserReg = Symbol_Pop()) != Conv_Ok)//运算符出栈到前缀表达式  
					return AnserReg;
				Polish_Notation[++P_Ptr] = ' ';//出一次加一个空格  
			}
			if ((AnserReg = Symbol_Push()) != Conv_Ok)//压栈  
				return AnserReg;
		}
		else if (Check(&Expression[E_Ptr], isOperatorsLevel2))//待处理字符是'*'或'/'  
		{
			while (Symbol_Stack.top >= 0 &&
				!Check(&Symbol_Stack.data[Symbol_Stack.top], isOperatorsLevel3) &&
				!Check(&Symbol_Stack.data[Symbol_Stack.top], isOperatorsLevel2) &&
				!Check(&Symbol_Stack.data[Symbol_Stack.top], isBracketRight))
				//运算符栈非空,且栈顶不是')' '+' '-' '*' '/'的时候要先出栈,原理同上  
			{
				if ((AnserReg = Symbol_Pop()) != Conv_Ok)//运算符出栈到前缀表达式  
					return AnserReg;
				Polish_Notation[++P_Ptr] = ' ';//出一次加一个空格  
			}
			if ((AnserReg = Symbol_Push()) != Conv_Ok)//压栈  
				return AnserReg;
		}
		else if (Check(&Expression[E_Ptr], isOperatorsLevel1))//待处理字符是'^',原理同上  
		{
			while (Symbol_Stack.top >= 0 &&
				!Check(&Symbol_Stack.data[Symbol_Stack.top], isOperatorsLevel3) &&
				!Check(&Symbol_Stack.data[Symbol_Stack.top], isOperatorsLevel2) &&
				!Check(&Symbol_Stack.data[Symbol_Stack.top], isOperatorsLevel1) &&
				!Check(&Symbol_Stack.data[Symbol_Stack.top], isBracketRight))
				//运算符栈非空,且栈顶不是')' '+' '-' '*' '/' '^'的时候要先出栈,原理同上  
			{
				if ((AnserReg = Symbol_Pop()) != Conv_Ok)//运算符出栈到前缀表达式  
					return AnserReg;
				Polish_Notation[++P_Ptr] = ' ';//出一次加一个空格  
			}
			if ((AnserReg = Symbol_Push()) != Conv_Ok)//压栈  
				return AnserReg;
		}
		else if (Check(&Expression[E_Ptr], isOperatorsLevel0))//若待处理的是一元运算符  
		{
			if (E_Ptr > 0 && //单目运算符前有值或括弧  
				Check(&Expression[E_Ptr - 1], isNumberOrDot) ||
				Check(&Expression[E_Ptr - 1], isBracketRight) ||
				Check(&Expression[E_Ptr - 1], isConstants))
				return Conv_MathError;
			while (Symbol_Stack.top >= 0 &&
				!Check(&Symbol_Stack.data[Symbol_Stack.top], isOperatorsLevel3) &&
				!Check(&Symbol_Stack.data[Symbol_Stack.top], isOperatorsLevel2) &&
				!Check(&Symbol_Stack.data[Symbol_Stack.top], isOperatorsLevel1) &&
				!Check(&Symbol_Stack.data[Symbol_Stack.top], isOperatorsLevel0) &&
				!Check(&Symbol_Stack.data[Symbol_Stack.top], isBracketRight))
				//原理同上  
			{
				if ((AnserReg = Symbol_Pop()) != Conv_Ok)//运算符出栈到前缀表达式  
					return AnserReg;
				Polish_Notation[++P_Ptr] = ' ';//出一次加一个空格  
			}
			if ((AnserReg = Symbol_Push()) != Conv_Ok)//压栈  
				return AnserReg;
		}
		else//其他的东西就直接都忽略掉吧  
		{
			E_Ptr--;
		}
	}
	while (Symbol_Stack.top >= 0)//操作符栈没有变空,说明有操作符没有取出来  
	{
		if ((AnserReg = Symbol_Pop()) != Conv_Ok)//运算符出栈到前缀表达式  
			return AnserReg;
		Polish_Notation[++P_Ptr] = ' ';//出一次加一个空格  
	}
	Polish_Notation[++P_Ptr] = Symbol_Stack.data[++Symbol_Stack.top] = '\0';//最后结尾加一个截至符  
	Make_String_Reverse(Polish_Notation, Polishlen);
	return Conv_Ok;
}

Init_type Analytic_Init(Init_type type, char *address)//表达式初始化  
{
	switch (type)
	{
	case Init_Expression://初始化中缀表达式  
		Expression = address;
		break;
	case Init_Polish_Notation://初始化前缀表达式  
		Polish_Notation = address;
		break;
	default:
		return Init_Error;
	}
	return Init_Ok;
}


void ReplaceStringtoChar(char *str, const char *tar, const char chr)//替换表达式中的复杂函数名  
{
	w_size_t position;
	char *tarposition;
	w_size_t tarsize;
	tarsize = (w_size_t)strlen(tar);
	while ((tarposition = strstr(str, tar)) != NULL)
	{
		position = tarposition - str;
		str[position++] = chr;
		while (str[position + tarsize - 1] != 0)
		{
			str[position] = str[position + tarsize - 1];
			position++;
		}
		str[position] = 0;
	}
}

char *Analytic_Simplification(char *str)//复杂函数名化简功能  
{
	a_size_t i;
	for (i = 0; i < sizeof(SimpleOperators); i++)//替换操作符  
		ReplaceStringtoChar(str, ComplexOperators[i], SimpleOperators[i]);
	for (i = 0; i < sizeof(SimpleConstants); i++)//替换常量名  
		ReplaceStringtoChar(str, ComplexConstants[i], SimpleConstants[i]);
	return str;
}

double Store(char *str, w_size_t *p)//转换字符到浮点数  
{
	w_size_t j = *p - 1, i;
	double n = 0, m = 0;
	switch (str[*p])
	{
	case S_Ans:
		if (str[*p - 1] == '-')
		{
			*p = *p - 1;
			return (-1 * Calc_ANS);
		}
		else
			return Calc_ANS;
	case S_Pai:
		if (str[*p - 1] == '-')
		{
			*p = *p - 1;
			return (-1 * _const_Pai_);
		}
		else
			return _const_Pai_;
	case S_Euler:
		if (str[*p - 1] == '-')
		{
			*p = *p - 1;
			return (-1 * _const_Euler_);
		}
		else
			return _const_Euler_;
	default:
		break;
	}
	while (str[j] >= '0' && str[j] <= '9')
		j--;
	if (str[j] != '.')
		for (i = j + 1; i <= *p; i++)
			n = 10 * n + (str[i] - '0');
	else
	{
		for (i = j + 1; i <= *p; i++)
			m = m + pow(0.1, i - j) * (str[i] - '0');
		if (str[j] == '.')
		{
			*p = --j;
			while (str[j] >= '0' && str[j] <= '9')
				j--;
			for (i = j + 1; i <= *p; i++)
				n = 10 * n + (str[i] - '0');
		}
	}
	*p = j;
	if (str[*p] == '-') return(-(n + m));
	return(n + m);
}

Analytic_type Number_Push(w_size_t *i)//数值入栈  
{
	if (Number_Stack.top < NumberMax - 1)
		Number_Stack.data[++Number_Stack.top] = Store(Polish_Notation, i);
	else
		return Conv_OverFlow;
	return Conv_Ok;
}

double Clear_Infinitesimal(double ans)
{
	if (ans < 1e-10 && ans > -1e-10)
		return 0;
	else
		return ans;
}

double Clear_ComplexTan(double num)
{
	double r1, r2;
	int i = (int)(num / (_const_Pai_ / 2));
	r1 = num - i * (_const_Pai_ / 2) + 1e-15;
	r2 = (i + 1) * (_const_Pai_ / 2) - num + 1e-15;
	if (i % 2 != 0 &&
		((r1 > 0 && r1 < 1e-10) ||
		(r2 > 0 && r2 < 1e-10)))
		return NAN;
	return num;
}

Analytic_type Number_Pop(w_size_t i)//数值出栈  
{
	if (Number_Stack.top >= 0)
	{
		if (Polish_Notation[i] != ' ')
			switch (Polish_Notation[i])
			{
			case '+':
				Number_Stack.data[Number_Stack.top - 1] =
					Number_Stack.data[Number_Stack.top] + Number_Stack.data[Number_Stack.top - 1];
				Number_Stack.top--;
				break;
			case '-':
				Number_Stack.data[Number_Stack.top - 1] =
					Number_Stack.data[Number_Stack.top] - Number_Stack.data[Number_Stack.top - 1];
				Number_Stack.top--;
				break;
			case '*':
				Number_Stack.data[Number_Stack.top - 1] =
					Number_Stack.data[Number_Stack.top] * Number_Stack.data[Number_Stack.top - 1];
				Number_Stack.top--;
				break;
			case '/':
				Number_Stack.data[Number_Stack.top - 1] =
					Number_Stack.data[Number_Stack.top] / Number_Stack.data[Number_Stack.top - 1];
				Number_Stack.top--;
				break;
			case '^':
				Number_Stack.data[Number_Stack.top - 1] =
					pow(Number_Stack.data[Number_Stack.top], Number_Stack.data[Number_Stack.top - 1]);
				Number_Stack.top--;
				break;
			case S_In:
				Number_Stack.data[Number_Stack.top] = log(Number_Stack.data[Number_Stack.top]);
				break;
			case S_Log:
				Number_Stack.data[Number_Stack.top] = log10(Number_Stack.data[Number_Stack.top]);
				break;
			case S_Exp:
				Number_Stack.data[Number_Stack.top] = pow(_const_Euler_, Number_Stack.data[Number_Stack.top]);
				break;
			case S_Sin:
				Number_Stack.data[Number_Stack.top] = sin(Number_Stack.data[Number_Stack.top]);
				Number_Stack.data[Number_Stack.top] = Clear_Infinitesimal(Number_Stack.data[Number_Stack.top]);
				break;
			case S_Cos:
				Number_Stack.data[Number_Stack.top] = cos(Number_Stack.data[Number_Stack.top]);
				Number_Stack.data[Number_Stack.top] = Clear_Infinitesimal(Number_Stack.data[Number_Stack.top]);
				break;
			case S_Tan:
				Number_Stack.data[Number_Stack.top] = Clear_ComplexTan(Number_Stack.data[Number_Stack.top]);
				Number_Stack.data[Number_Stack.top] = tan(Number_Stack.data[Number_Stack.top]);
				Number_Stack.data[Number_Stack.top] = Clear_Infinitesimal(Number_Stack.data[Number_Stack.top]);
				break;
			case S_Sqrt:
				Number_Stack.data[Number_Stack.top] = sqrt(Number_Stack.data[Number_Stack.top]);
				break;
			case S_ArcSin:
				Number_Stack.data[Number_Stack.top] = asin(Number_Stack.data[Number_Stack.top]);
				break;
			case S_ArcCos:
				Number_Stack.data[Number_Stack.top] = acos(Number_Stack.data[Number_Stack.top]);
				break;
			case S_ArcTan:
				Number_Stack.data[Number_Stack.top] = atan(Number_Stack.data[Number_Stack.top]);
				break;
			}
	}
	else
		return Conv_MathError;
	return Conv_Ok;
}

Analytic_type Calculation_Results(void)
{
	Analytic_type res;
	w_size_t len, i;

	Init_Stack(Number);
	len = Get_StringLen(Polish_Notation, Polishlen);
	for (i = len - 1; i >= 0; i--)
	{
		if (Check(&Polish_Notation[i], isNumber) || Check(&Polish_Notation[i], isConstants) ||
			(Check(&Polish_Notation[i], isOperatorsLevel3) && Check(&Polish_Notation[i + 1], isNumber)))
		{
			if ((res = Number_Push(&i)) != Conv_Ok)
				return res;
		}
		else
		{
			if ((res = Number_Pop(i)) != Conv_Ok)
				return res;
		}
	}
	if (Symbol_Stack.top != 0 || Number_Stack.top != 0)
		return Conv_MathError;
	return Conv_Ok;
}

Analytic_type Analytic(Analytic_type type)//表达式解析  
{
	switch (type)
	{
	case Conv_to_Polish:
		return Convert_to_Polish();
	case Calc_Results:
		return Calculation_Results();
	default:
		break;
	}
	return Conv_Exception;
}

double Get_Answer(void)
{
	return (Calc_ANS = Number_Stack.data[0]);
}

Analytic.h 头文件,另外还包括几个重要的枚举类型:
//Create Date: 2018-3-4
//Author: Charming
//File: Analytic.h
//Directions: 表达式解析库头文件
//Last edit Date: 2018-3-6

#ifndef __ANALYTIC_H_
#define __ANALYTIC_H_

#define const_mode const

#define USESTRINGLIB 1
#define STRINGLIB_standard 1

#ifndef NULL
#define NULL 0
#endif

#define Exprlen 100
#define Polishlen 180
#define SymbolMax 80
#define NumberMax 50

typedef signed char a_size_t;
typedef signed short w_size_t;

typedef enum//初始化枚举类型
{
	Init_Expression,
	Init_Polish_Notation,
	Init_Ok,
	Init_Error
}Init_type;

typedef enum
{
	Conv_to_Polish,
	Calc_Results,
	Conv_Ok,
	Conv_MathError,
	Conv_OverFlow, 
	Conv_Exception
}Analytic_type;

Init_type Analytic_Init(Init_type type, char *address);
Analytic_type Analytic(Analytic_type type);
char *Analytic_Simplification(char *str);
double Get_Answer();

#endif

另外我贴上我测试用的 main.c,也可以作为使用方法的参考:
//Create Date: 2018-3-4
//Author: Charming
//File: Analytic.c
//Version: 1.7.2
//Last edit Date: 2018-3-15
//Directions: 测试源文件

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "Analytic.h"

char E_String[Exprlen] = { 0 };
char P_String[Polishlen] = { 0 };

int main(void)
{
	Analytic_type res;
	Analytic_Init(Init_Expression, E_String);
	Analytic_Init(Init_Polish_Notation, P_String);
	while (1)
	{
		system("cls");
		printf("C语言利用前缀表达式实现复杂科学计算器\r\n");
		printf("Test Program Build : %s %s\r\n\r\n", __DATE__, __TIME__);
		printf("输入一个中缀表达式(退出请输入Exit):");
		gets_s(E_String, sizeof(E_String));
		if (strnicmp(E_String, "exit", 4) == 0)
			return 0;
		printf("化简后的中缀表达式: %s\r\n", Analytic_Simplification(E_String));
		if ((res = Analytic(Conv_to_Polish)) == Conv_Ok)
			res = Analytic(Calc_Results);
		switch (res)
		{
		case Conv_Ok:
			printf("前缀表达式: %s\r\n", P_String);
			printf("\r\n计算结果:%g\r\n\r\n", Get_Answer());
			break;
		case Conv_MathError:
			printf("\r\n数学错误!\r\n\r\n");
			break;
		case Conv_OverFlow:
			printf("\r\n堆栈溢出!\r\n\r\n");
			break;
		case Conv_Exception:
			printf("\r\n系统异常!\r\n\r\n");
			break;
		default:
			break;
		}
		system("pause");
	}
	return 0;
}

测试截图:



Mathematica验证:



2006-02-10 10:31:00 lxp916 阅读数 967

利用C#实现分布式数据库查询

随着传统的数据库、计算机网络和数字通信技术的飞速发展,以数据分布存储和分布处理为主要特征的分布式数据库系统的研究和开发越来越受到人们的关注。但由于其开发较为复杂,在一定程度上制约了它的发展。基于此,本文提出了在.Net环境下使用一种新的开发语言C#结合ADO.Net数据访问模型来开发分布式数据库系统,大大简化了开发过程。

  1 分布式数据库系统

  就其本质而言,分布式数据库系统的数据在逻辑上是统一的,而在物理上却是分散的。与集中式数据库相比它有如下主要优点:

  · 解决组织机构分散而数据需要相互联系的问题。

  · 均衡负载。负载在各处理机间分担,可避免临界瓶颈。

  · 可靠性高。数据分布在不同场地,且存有多个副本,即使个别场地发生故障,不致引起整个系统的瘫痪。

  · 可扩充性好。当需要增加新的相对自主的组织单位时,可在对当前机构影响最小的情况下进行扩充。

  分布式数据库系统虽然有诸多优点,但它同时也带来了许多新问题。如:数据一致性问题、数据远程传递的实现、通信开销的降低等,这使得分布式数据库系统的开发变得较为复杂。幸运的是,微软的.Net开发环境为我们提供了C#开发语言和ADO.Net数据访问模型,结合两者来开发分布式数据库系统能够大大简化开发工作。

  2 远程处理框架和ADO.Net

  开发分布式数据库系统需要解决的两个重要问题是:各场地间的数据通信以及对数据库的操作及管理。使用C#结合ADO.Net能够高效、可靠地解决这两方面的问题。具体表现为,在C#中通过使用.Net远程处理框架能够方便地解决数据、命令远程传递问题;C#通过ADO.Net对数据库进行操作,使分布式数据库系统中对数据库的各种操作变得高效、可靠,同时易于解决数据一致性问题。

  2.1 .Net远程处理框架

实现数据和命令的远程传递有三种方式。第一种是使用报文或消息的方式,把要传送的数据转化为流格式,再通过套接字编程用报文的形式发送到远程主机。此种方法麻烦,不易实现。第二种是使用Web Service,即各远程主机提供一个数据库查询服务的Web Service。这种方式只能对单个场地进行查询,无法实现多场地的联合查询。第三种是使用.Net远程处理框架(.Net Remoting Framework)技术,它将远程调用的技术细节隐藏起来,服务程序只需通过简单的设置就可以把本地对象变成为远程提供服务的远程对象,客户端可以像访问本地对象一样透明地访问远程对象,所有的消息、报文等都交给.Net Remoting对象处理,大大简化了开发。远程处理的一般过程如图1所示:

1 远程处理过程

  首先,服务器端创建一个服务器类的实例,远程处理系统创建一个表示该类的代理对象,并向客户端对象返回一个对该代理的引用。当客户端调用方法时,远程处理基础结构连接检查类型信息,并通过信道将该调用发送到服务器进程。侦听信道获得该请求并将其转发给服务器远程处理系统,服务器远程处理系统查找(或在必要时创建)并调用被请求的对象。然后,此过程将反向进行,服务器远程处理系统将响应捆绑成消息并由服务器信道发送到客户端信道。最后,客户端远程处理系统通过代理将调用的结果返回给客户端对象。

  2.2 ADO.Net

  ADO.NetXML为核心,是.Net数据库应用程序的解决方案。它使用离线数据结构,数据源中的数据被缓存到数据集(DataSet)对象中,用户无须锁定数据源,数据以XML格式保存。

  2.2.1 ADO.Net管理数据一致性

  在分布式数据库系统中,很可能出现多个用户同时访问和修改数据的情况,因此,对于分布式数据库系统,数据一致性是不可或缺的。ADO.Net通过使用乐观一致性方案来控制数据一致性(实际上DataSet对象被设计成支持使用乐观一致性控制机制),即数据行只有在数据库中真正被更新时才会被锁定,而在悲观一致性方案中,数据行在从被提取出来到在数据库中更新这段时间内一直被锁定。因此,使用ADO.Net能够在更少的时间内响应数量巨大的用户。

  另外,在分布式数据库系统中,还会经常遇到当用户修改自从提取出来以来已经被修改的行时,违反一致性原则。对此问题ADO.Net也作了很好地解决,即使用DataSet对象为每一条修改过的记录维护两个版本:原始版本和更新版本,在更新的记录被写回数据库之前,先要把数据集中记录的原始版本与数据库中的当前版本进行比较,如果两个版本匹配,就在数据库中更新记录;否则,就会出现违反一致性原则的错误。

  3 实例开发

  一个家用电器连锁店设有一个总部和许多分店,总部和分店以及各分店之间经常需要进行各种信息的查询(如:商品当日价目表、各店销售状况和库存信息等),对此组织机构建立分布式数据库查询系统,可实现总部和各店信息的共享,便于统一管理。

  3.1 系统设计

  3.1.1系统结构图

系统结构如图2所示:

2 系统结构图

  总部和各分店都配置了一台具有固定IP的服务器,其它电脑通过集线器与服务器相连,总部和各分店的服务器通过通信网络联接起来。
  3.1.2 系统实现步骤
  系统实现分为三个主要步骤。首先,为总部和各分店设计数据库。由于数据量较大,故采用SQL Server为每个分店创建销售和库存数据库,同时为总部创建员工数据库、整个连锁店的存货数据库、信用卡客户数据库以及供应商信息数据库等。其次,需要建立一个提供数据库服务(DbServer)的动态链接库(dll),将查询时所要用到的一些服务(如:远程对象的发布和获取等)和函数(如:本地异地数据表的查询、数据表的远程创建和删除、表间的连接和合并等)置入该dll中,各分店都需要使用这个dll,以便查询时对一些服务和函数进行调用。最后,根据实际需要开发客户端查询界面。
  3.2系统实现的关键技术
  3.2.1 远程对象的发布与获取
  系统运行后所要做的第一个工作是发布本地的远程对象并获取其它各店所发布的远程对象。发布远程对象时,首先要设置一个网络端口号,然后创建并注册一个通道,最后发布该服务器端的激活对象。其它场地的服务器根据IP地址和网络端口号即可方便地获取所发布的远程对象。实现远程对象发布和获取的关键代码如下:

  远程对象的发布:
//
创建一个通道实例,port为指定的网络端口号
TcpChannel MyChannel= new TcpChannel (Int32.Parse(port));
//
注册通道
ChannelServices.RegisterChannel(MyChannel);
//
发布该服务器端激活对象
RemotingConfiguration.RegisterWellKnownServiceType( typeof ( DbServer ), "STORE", WellKnownObjectMode.Singleton);
远程对象的获取:
//
根据IP地址和端口号获取相应的远程对象
try
{myDbServer=(DbServer)Activator.GetObject(typeof(DbServer),"tcp://"+ip+":"+p+"/STORE"); }
//
捕捉异常
catch( NullReferenceException nullExp )
{MessageBox.Show( "
指定的url地址不可到达
" + nullExp.Message );}
catch( RemotingException remExp )
{MessageBox.Show( "
指定获得的对象定义不对
" + remExp.Message );}
  3.2.2数据库的访问

  通过ADO.Net访问数据库,可以方便地连接数据库,将数据源中的数据导入DataSet对象中,在DataSet对象中可对数据表进行各种操作,而且DataSet对象本身也可远程传递。这为开发分布式数据库系统带来极大方便。实现数据库访问的关键代码如下所示:
//建立数据库的连接
string SqlConn = "Initial Catalog=Store;Data Source=Localhost;Userid=sa;Password=;";
SqlConnection Conn= new SqlConnection
(SqlConn);
Conn.Open();//
打开数据库

//
将数据源中的数据导入到数据集对象
try{
DataSet ds = new DataSet();
DataTable dt=new DataTable("Result");
SqlDataAdapter adapter=new SqlDataAdapter();
SqlCommand mySqlDataSetCmd =new SqlCommand
(CmdString,Conn);//CmdString
为要执行的命令
adapter.SelectCommand= mySqlDataSetCmd;
adapter.Fill(dt);
ds.Tables.Add(dt); }
finally
{ Conn.Close();//
关闭数据库的连接
  3.2.3 查询

  分布式数据库系统中的查询一般分为三类:本地查询、远程查询和联合查询。本地查询和集中式数据库的查询没什么区别;对于远程查询,只要获取远程对象后,调用查询函数,即可方便地实现;最复杂的是联合查询,涉及到多场地之间数据的查询、表的远程创建、传递、连接、合并等技术。下面以实例介绍联合查询的实现。
  第二连锁店要查询离其较近的第三、第四连锁店中所有北京的供应商所供应的空调的库存信息以便调货,可通过以下步骤实现。首先,获取总部以及第三、第四连锁店所发布的远程对象。接着,通过远程对象在总部创建一临时数据表t1,将查询到的所有北京的供应商信息存放在t1表中(各分店只有供应商名,并不知其所在地,只有总部才有供应商的详细信息),再将t1表保存到第三和第四连锁店。然后让t1表分别与两店的库存表作连接,找出所有北京供应商所供应的空调库存信息(如空调名称、型号、个数、价格等信息),并将连接结果t2t3数据表返回到第二连锁店。最后对t2t3两表进行合并,并使用DataGrid控件显示出来。上述实现中,包含了不同场地之间数据表的复制、传递、连接等,所用到的一些函数(如:远程创建数据表、表与表间的远程连接、合并等)都放在dll中,可以方便地调用。
  4 结束语
  利用C#.Net Remoting技术能够方便地解决各场地间数据的通信问题。另外,C#通过ADO.Net访问数据库,使得对数据库的操作及管理变得更加高效、可靠。这两种技术的使用,有效地解决了开发分布式数据库系统的主要问题,大大减轻了系统开发工作量,并且提高了系统的可靠性和安全性。

2017-11-17 00:00:00 ax8785r8C32nef593 阅读数 4884


概述

MongoDB是一个基于分布式文件存储的数据库。由C++语言编写。旨在为WEB应用提供可扩展的高性能数据存储解决方案。

MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。他支持的数据结构非常松散,是类似json的bson格式,因此可以存储比较复杂的数据类型。Mongo最大的特点是他支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。

前言

考虑这样一个场景,有个数据量有10多亿数据的设备库,里面存放了注册的设备的信息,并且设备数据还可能会递增,然后业务集群需要对指定条件的设备群发信息,那么如何才能高效的来处理这个问题那?

思考

离线分析


为了不影响在线业务,以往需要把一份数据进行多次复制来分别进行业务交易和数据分析的问题,也就是业务交易的数据是在原来的库,而数据分析是通过手段把原来库数据定时同步到另外的存储设备离线分析。上面所说消息群发设备就可以使用这种方式,首先全量把数据同步到另外的存储设备,然后在定时把原来库里面的新数据增量到存储设备。

 离线库

 阿里云产品mondb介绍

对于存储设备可以选择阿里云产品MongoDB,原因是MongoDB 分片集群(Sharded Cluster)通过将数据分散存储到多个分片(Shard)上来实现高可扩展性。实现分片集群时,MongoDB 引入 Config Server 来存储集群的元数据,引入 mongos 作为应用访问的入口,mongos 从 Config Server 读取路由信息,并将请求路由到后端对应的 Shard 上。下面看下原理图


  • 用户访问 mongos 跟访问单个 mongod 类似;

  • 所有 mongos 是对等关系,用户访问分片集群可通过任意一个或多个mongos;

  • mongos 本身是无状态的,可任意扩展,集群的服务能力为“Shard服务能力之和”与“mongos服务能力之和”的最小值;

  • 访问分片集群时,最好将应用负载均匀的分散到多个 mongos 上。

Sharding Cluster使得集合数据被分散到多个Shard存储,也就是每个shard存放整体数据的一部分

 如何正确地连接分片集群?

所有官方的 MongoDB driver 都支持以 Connection String 的方式来连接 MongoDB 分片集群。

下面就是 Connection String 包含的主要内容:

mongodb://[username:password@]host1[:port1][,host2[:port2],…[,hostN[:portN]]][/[database][?options]]

  • mongodb:// 前缀,代表这是一个Connection String;

  • username:password@ 如果启用了鉴权,需要指定用户密码;

  • hostX:portX多个 mongos 的地址列表;

  • /database鉴权时,用户帐号所属的数据库;

  • ?options 指定额外的连接选项。

 java 代码访问

通过 java 来连接的示例代码如下所示:

MongoClientURI connectionString = new MongoClientURI("mongodb://:****@s-m5e80a9241323604.mongodb.rds.aliyuncs.com:3717,s-m5e053215007f404.mongodb.rds.aliyuncs.com:3717/admin"); // ****替换为root密码

MongoClient client = new MongoClient(connectionString);

MongoDatabase database = client.getDatabase("mydb");

MongoCollection<Document> collection = database.getCollection("mycoll");

通过上述方式连接分片集群时,客户端会自动将请求分散到多个 mongos 上,以实现负载均衡;同时,当 URI 里 mongos 数量在2个及以上时,当有 mongos 故障时,客户端能自动进行 failover,将请求都分散到状态正常的 mongos 上。

好吧,言归正传,上面知道大数据最后会被分散到不同的shard上,而我们的应用系统是集群,如果能让集群每台机器分摊不同shard上的数据进行处理,那么性能肯定杠杠的,可以吗?答案是必须的。

mongb上有个config.shards,config.shards集合存储各个Shard的信息,可通过addShard、removeShard命令来动态的从Sharded cluster里增加或移除shard。

mongos> db.shards.find()

{ "_id" : "mongo-9003", "host" : "mongo-9003/10.1.72.135:9003,10.1.71.136:9003,10.1.72.137:9003" }

{ "_id" : "mongo-9004", "host" : "mongo-9004/10.1.72.135:9004,10.1.71.136:9004,10.1.72.137:9004" }

通过config.shards可以获取所以分片shard的地址然后替换上面的url中host,就可以让集群系统里不同机器直接访问shard上的数据。

通过config.shards可以获取所以分片shard的地址然后替换上面的url中host,就可以让集群系统里不同机器直接访问shard上的数据。

试用场景

MongoDB 的主要目标是在键/值存储方式(提供了高性能和高度伸缩性)和传统的RDBMS 系统(具有丰富的功能)之间架起一座桥梁,它集两者的优势于一身。根据官方网站的描述,Mongo 适用于以下场景。

● 网站数据:Mongo 非常适合实时的插入,更新与查询,并具备网站实时数据存储所需的复制及高度伸缩性。

● 缓存:由于性能很高,Mongo 也适合作为信息基础设施的缓存层。在系统重启之后,由Mongo 搭建的持久化缓存层可以避免下层的数据源过载。

● 大尺寸、低价值的数据:使用传统的关系型数据库存储一些数据时可能会比较昂贵,在此之前,很多时候程序员往往会选择传统的文件进行存储。

● 高伸缩性的场景:Mongo 非常适合由数十或数百台服务器组成的数据库,Mongo 的路线图中已经包含对MapReduce 引擎的内置支持。

● 用于对象及JSON 数据的存储:Mongo 的BSON 数据格式非常适合文档化格式的存储及查询。

MongoDB 的使用也会有一些限制,例如,它不适合于以下几个地方。

● 高度事务性的系统:例如,银行或会计系统。传统的关系型数据库目前还是更适用于需要大量原子性复杂事务的应用程序。

● 传统的商业智能应用:针对特定问题的BI 数据库会产生高度优化的查询方式。对于此类应用,数据仓库可能是更合适的选择。

● 需要SQL 的问题。

总结

使用mongdb的分片机器可以使应用集群中每台机器去访问分片机器每个shard上面的数据,从而实现并发处理大数据

大型网站架构技术

程序员修炼之道

大型web系统数据缓存设计

基于 Redis 实现分布式应用限流

Cache缓存技术全面解析

京东到家库存系统分析

Nginx 缓存引发的跨域惨案

浅谈Dubbo服务框架

数据库中间件架构 | 架构师之路

MySQL优化精髓



看完本文有收获?请转发分享给更多人


欢迎关注“畅聊架构”,我们分享最有价值的互联网技术干货文章,助力您成为有思想的全栈架构师,我们只聊互联网、只聊架构!打造最有价值的架构师圈子和社区。


长按下方的二维码可以快速关注我们


2012-11-07 20:25:24 yuchi_zhang 阅读数 419

C语言不能很好的处理泛型数据,不过下面这个泛型栈提供了一种思路,把对数据的操作看作是对一格一格内存的操作,利用Memory头文件里的函数可以做到这一点。可以通过这种思路延伸到队列等其他数据结构中,把对数据的操作抽象为对内存的操作。当然在处理过程当中要处处小心,特别是对指针操作的时候。

另外,我写了一个用这个栈解数独的程序,可参考:《穷举法解数独》更智能的解数独算法,让效率提升5倍!


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

void newStack(stack *s, size_t elemSize);
void disposeStack(stack *s, void (*remainPro)(void *elem));
void push(stack *s, void *elem);
void pop(stack *s, void *elem);
int isEmpty(stack *s);  

typedef struct
{
        void    *elem;                 // 元素起始指针
        int     elemSize;         // 单位元素大小
        int     top;              // 栈顶元素位置
        int     mallocNum;        // 已分配元素数量
}stack;

void newStack(stack *s, size_t elemSize)
{
        assert(elemSize > 0);
        s->elem = malloc(4 * elemSize);       // 初始分配4个单位长度 
        assert(s->elem != NULL);
        s->elemSize = elemSize;
        s->top = 0;
        s->mallocNum = 4;
}

void disposeStack(stack *s, void (*remainPro)(void *elem))
{
        if(s->top > 0 && remainPro != NULL){     // 栈不为空,并且定义了处理函数
                void *tmpElem;
                while(s->top > 0){
                        pop(s,&tmpElem);                   // 弹出元素,并交给处理函数处理
                        remainPro(tmpElem);
                }
        }
        free(s->elem);
}

void push(stack *s, void *elem)
{
        if (s->top == s->mallocNum){         // 如果栈满了,那么重新分配2倍大的空间 
                s->mallocNum *= 2;
                s->elem = realloc(s->elem, s->mallocNum * s->elemSize);
                assert(s->elem != NULL);
        }

        memcpy(s->elem + s->top * s->elemSize, elem, s->elemSize);      // 内存块复制实现入栈 
        s->top++;
}

void pop(stack *s, void *elem)
{
        assert(s->top > 0);
        s->top--;
        memcpy(elem, s->elem + s->top * s->elemSize, s->elemSize);      // 复制内存块出栈 
}      

int isEmpty(stack *s)
{
        return s->top == 0;
}

说明:disposeStack函数的第二个参数是一个函数指针,指向的函数原型是void remainPro(void *elem),用于在销毁栈时处理栈中残余的元素,比如当栈中的元素是malloc所分配的指针时,remainPro可以调用free处理栈中剩余的元素

测试代码:

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <memory.h>
#include <string.h>

#define LEN 5

void freeStr(void *str)
{
        printf("%s\n",str);
        free(str);
}

int main(int argc, const char *argv[])
{
        stack myStack;
        char *str[LEN];
        char *tmp;

        str[0] = strdup("abc");
        str[1] = strdup("def");
        str[2] = strdup("ghi");
        str[3] = strdup("jkl");
        str[4] = strdup("mno");
        
        newStack(&myStack,sizeof(char *));
        push(&myStack, &str[0]);
        push(&myStack, &str[1]);
        push(&myStack, &str[2]);
        push(&myStack, &str[3]);
        push(&myStack, &str[4]);
        
        pop(&myStack,&tmp);
        printf("%s\n",tmp);
        free(tmp);

        disposeStack(&myStack,freeStr);

        return 0;
}

输出结果:

mno
jkl
ghi
def
abc


没有更多推荐了,返回首页