科学网

 找回密码
  注册

tag 标签: C语言

相关帖子

版块 作者 回复/查看 最后发表

没有相关内容

相关日志

文件的操作集合,亲自编译运行
songyanru243 2011-3-24 20:12
1、新建一个单文档类型的MFC应用程序,工程取名:File。 2、为主菜单添加一个子菜单,名称:“文件操作”,然后为其添加两个菜单项,分别为其添加相应的命令相应函数,且让CFileView类接收这些菜单的命令响应。 IDM_FILE_WRITE 写入文件 OnFileWrite IDM_FILE_READ 读取文件 OnFileRead 附: 文件的打开 在C语言中,对文件的操作是利用FILE结构体进行的,具体实现时,首先需要利用fopen函数返回一个指向FILE结构体的指针,该函数的声明如下: FILE *fopen(const char* filename,const char* mode); 文件的写入 size_t fwrite(const void *buffer,size_t size,size_t count,FILE *stream); buffer:指向将要被写入文件的数据。 文件的关闭 fclose(); 例子:在File程序的CFileView类的OnFileWrite函数中添加如下代码,则可以实现文件的写入操作。 void CFileView::OnFileWrite() { // TODO: Add your command handler code here FILE *pFile=fopen("a.txt","w"); fwrite("英语词汇的奥秘!",1,strlen("英语词汇的奥秘"),pFile); fclose(pFile); } C语言对文件的操作使用了缓冲文件系统。具体地说就是系统自动为每个正在使用的文件在内存中开辟了一块缓冲区域,从内存向磁盘文件写入的数据必须先送到内存中的这个缓冲区,直到其中的缓冲区满了之后,才把其中的数据一起送到磁盘的文件中。因此,当需要在数据写入缓冲区后,立即能在磁盘文件中看到该数据,那么可以使用fclose函数关闭文件,表示对文件的写入操作已经完成,系统这时会把缓冲区中的内容写入磁盘的文件中。 如果希望每次对文件操作之后不关闭它,但仍能将缓冲区中的数据立即写入磁盘文件中,这时可以使用另一个C函数: fflush ,这个函数的作用是将缓冲区中的数据写入到磁盘文件。因此可以将上述fclose换成fflush函数。提高程序效率,减少麻烦,不断刷新缓冲区中的数据,将他们写入磁盘文件中。 文件的指针定位 int fseek(FILE *stream,long offset,int origin); 文件的读取 size_t fread(void *buffer,size_t size,size_t count,FILE *stream); 与fwrite函数一样,fread函数也有四个参数,并且各参数的含义与fwrite函数的相应参数含义相同,只是这时第一个参数:buffer是指向用来存放数据的缓冲区的指针。 例子:在File程序的CFileView类的OnFileRead函数中添加如下代码,则可以实现文件的读取操作。 void CFileView::OnFileRead() { // TODO: Add your command handler code here FILE *pFile=fopen("a.txt","r"); char ch ; fread(ch,1,100,pFile); fclose(pFile); MessageBox(ch); } Build并运行,发现消息框显示的文件中,开始部分是程序中写入的数据,但后面显示了一串乱码。 注意:在C语言中,字符串是以“\0”符号结束的。在显示字符串时,系统会寻找"\0"字符。而这里定义的100个元素的字符数组:ch,它里面的数据是随机的,可能有许多数据都是不可读的。因此我们应该在读取到的数据之后添加"\0"字符,以便作为字符串的结尾。 有多种方法可以向一个字符串添加"\0"字符。这里简单使用一种方法: 在定义字符数组之后char ch 之后,利用C语言中的memset函数将这个字符数组中的所有数据都设置为0. memset(ch,0,100);然后在程序中读取文件时,读取到多少数据就会在该字符数组中存放多少数据,字符数组中剩下的全是0.这样,在字符串显示遇到"\0",就表示该字符串结束了,因此就能正确地显示数据。 二进制文件和文本文件 一个文件写入时经常遇到的问题: 在OnFileWrite和OnFileRead这两个函数中已有代码注释起来。 在OnFileWrite中写入如下代码: FILE *pFile=fopen("2.txt","w"); char ch ; ch ='a'; ch =10; ch ='b'; fwrite(ch,1,3,pFile); fclose(pFile); 注意:在C语言中整数与字符是可以互操作的。如果将一个整数赋给字符变量,实际上就是将该整数作为ASCII码赋给这个字符变量。对于整数10来说,它实际上是换行符的ASCII码。 问题: 以文本文件写入数据的时候,文件的大小是4个字节,而以二进制方式写入数据的时候是3个字节。 实际上 :文件只是在计算机内存中以二进制表示的数据在外部存储介质上的另一种存放形式。 二进制文件 是包含在ASCII及扩展ASCII字符中编写的数据或程序指令的文件。一般是可执行程序、图形、图像、声音等文件 。文本文件(也称为ASCII文件): 它的每一个字节存放的是可表示为一个字符的ASCII代码的文件。它是以“行”为基本结构的一种信息组织和存储方式的文件,可用任何文字处理程序阅读的文本文件。 二进制文件和文本文件都是以二进制数据的方式存储的。对于文本文件来说,它只是一种特殊形式的文件,她它所存放的每一个字节都可以转换为一个可读的字符。 C++对文件操作的支持 在C++中,向文件中写入数据可以使用ofstream类来实现,其构造函数为: ofstream(const char* szName, int nMode=ios::out, int nProt=filebuf::openprot ); 利用ofstream类实现文件写入操作,代码如下: ofstream ofs("4.txt"); ofs.write("help me, help me,help me.",strlen("help me, help me,help me.")); ofs.close(); 利用ifstream类实现文件的读取,代码如下: ifstream ifs("4.txt"); char ch ; memset(ch,0,100); ifs.read(ch,100); ifs.close(); MessageBox(ch); 因为利用C++函数对文件进行操作的应用场合比较少,因此这里只是简单地介绍了ofstream和ifstream这两个类的使用。 MFC对文件操作的支持 MFC中提供的支持文件操作的基类是:CFile,该类提供了没有缓冲的二进制格式的磁盘文件输入输出功能,通过其派生类能够间接地支持文本文件和内存文件。 CFile(LPCTSTR lpszFileName,UNIT nOpenFlags ); 其中lpszFileName指定文件的名称,nOpenFlags指定文件共享和访问的方式。 文件的写入 在File程序中利用CFile类提供的方法来实现文件的写入操作。代码如下: CFile file("6.txt",CFile::modeCreate|CFile::modeWrite); file.Write("pig pig pig",strlen("pig pig pig")); file.Close(); 文件的读取 在OnFileRead函数中实现文件的读取: CFile file("6.txt",CFile::modeRead); char *pBuf; DWORD dwFileLen; dwFileLen=file.GetLength(); pBuf=new char ; pBuf =0; file.Read(pBuf,dwFileLen); file.Close(); MessageBox(pBuf); CFile类的使用非常方便,因此在使用MFC编程时,如果涉及文件操作,最好采用CFile类来完成。 CFileDialog类 可以为File程序增加一个“打开文件”对话框和“另存为”对话框的功能。 这是CFileDialog类的派生层次结构图。可以看出CFileDialog类是从CCommonDialog类派生来的,并且间接派生于CDialog类,因此它也是一个对话框类。该类的构造函数声明如下: CFileDialog( BOOL bOpenFileDialog , LPCTSTR lpszDefExt = NULL, LPCTSTR lpszFileName = NULL, DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, LPCTSTR lpszFilter = NULL, CWnd* pParentWnd = NULL ); bOpenFileDialog是BOOL类型,如果此参数设置为TRUE,那么将构建一个打开对话框;如果将此参数设置为FALSE,那么将构建一个保存为对话框。也就是说,通过 CFileDialog类,既可以创建“打开”对话框,也可以创建“保存为”对话框。 1、另存为对话框 在OnFileWrite函数中添加以下代码: CFileDialog fileDlg(FALSE); fileDlg.DoModal(); 运行File程序,弹出如下对话框。 继续做一些改进,将标题“另存为”改为“我的文件保存对话框”, fileDlg.m_ofn.lpstrTitle="我的文件保存对话框"; 如果想设置保存类型,即设置过滤器的话,可以利用m_ofn这个数据成员中另一个字段:lpstrFilter来完成,该字段是一个指向包含多个过滤字符串对的缓冲区的指针,各过滤字符串对之间,以及字符串对内部两个字符串之间都以“\0”分隔,最后的过滤器字符串必须以两个“\0”字符结尾。对一个字符串对来说,第一个字符串描述过滤器,而第二个字符串表明过滤使用的文件扩展名称,多个扩展名称可以用分号(;)分隔。因此,如果把过滤字符串设置为"Text Files(*.txt)",那么该字符串只是出现在“另存为”对话框中保存类型列表框中所看到的文字,并不具有过滤的功能。如果要想具有过滤功能,必须在其后加上“\0”字符,并随后加上“*.txt”字符串。因此,在上述代码之后在添加如下代码: fileDlg.m_ofn.lpstrFilter="Text Files(*.txt)\0\*.txt\0All Files(*.*)\0*.*\0\0"; 可以看到标题变成了:我的文件保存对话框。在保存类型下拉列表框中出现了Text Files(*.txt)和All Files(*.*)两种文件过滤类型。 接下来要实现这样的功能:当用户通过“另存为”对话框选择了某个文件并单击该对话框上的【保存】按钮后,程序应该根据用户所选择的文件名,打开相应文件,然后向其中写入数据。 再在OnFileWrite函数增加以下代码: if(IDOK==fileDlg.DoModal()) { CFile file(fileDlg.GetFileName(),CFile::modeCreate|CFile::modeWrite); file.Write("outliers,outliers",strlen("outliers,outliers")); file.Close(); 2、打开文件对话框 再利用CFileDialog类创建一个打开文件对话框。在File程序CFileView类的OnFileRead函数添加下面代码: CFileDialog fileDlg(TRUE); fileDlg.m_ofn.lpstrTitle="我的文件打开对话框"; fileDlg.m_ofn.lpstrFilter="Text Files(*.txt)\0\*.txt\0All Files(*.*)\0*.*\0\0"; if(IDOK==fileDlg.DoModal()) { CFile file(fileDlg.GetFileName(),CFile::modeRead); char *pBuf; DWORD dwFileLen; dwFileLen=file.GetLength(); pBuf=new char ; pBuf =0; file.Read(pBuf,dwFileLen); file.Close(); MessageBox(pBuf); } 运行File程序,会出现如上所示对话框。通过这个对话框,用户可以选择一个文件,并单击该对话框上的【打开】按钮,即可打开该文件,并在弹出的消息框上看到该文件的内容。 小结: 主要分析和介绍了与文件操作相关的内容,包括: C语言对文件读写的支持; 文本文件和二进制文件的区别; C++对文件读写的支持,涉及ofstream和ifstream的用法; MFC对文件读写的支持,主要介绍了CFile类和CFileDialog类的使用。 参考书:《VC++深入详解》孙鑫 余安萍 编著
个人分类: 科研交流|1333 次阅读|0 个评论
基于剖析的数据类型重构思想总结
centerplain 2011-3-21 10:20
前几天实验室的同学给我一篇国外的论文,是关于静态和动态相结合来解决数据类型恢复问题的。看完之后对其思想总结一下。 他的主要思想就是首先利用静态的方法,如SSA、DU链、UD链等来将能恢复的基本数据类型进行恢复,并将虚拟内存划分为互不相交的等价基地址区域,如果区域的大小比C语言中最大的类型所需占用的空间还大,则表示该数据类型无法被静态的方法所恢复,否则,该区域则对应一个结构或者数组,然后根据该区域中的偏移offset集合来恢复结构数据类型的框架,或者根据偏移来恢复数组。当遇到静态方法无法判断的情况将启动动态过程。他利用Valgrind框架构造动态剖析器,对所有对内存的加载以及读取进行监视,并计算被剖析数值的PC值和UC值,如果PC=1则表明被分析对象是指针,如果UC=1则表明被分析对象是无符号数。这些由剖析器得到的信息被存放在剖析信息库当中,由这些信息指导进一步的静态分析算法。 这片论文的作者做的实验是在Linux下,源代码选用的是FreeBSD4.0,用gcc4.3.2编译器-o2优化选项进行编译的,作者将正确恢复出的、没有正确恢复出的指针和基本数据类型进行了统计,还对PC和CU值进行了统计,发现在识别指针方面,大部分都可以识别,只是少数的应该是数值的在分析时得到的PC=1,而无符号数的分析则不能令人满意。另外,动态的剖析过程要比不用剖析过程慢100倍,占用的空间也要大100到200倍。
2451 次阅读|0 个评论
Fortran imsl 程序库介绍
热度 2 zheyang 2011-3-19 23:30
近来利用Fortran在编写一些程序代码,程序经过今晚的调试,终于编译通过。调试过程中遇到的最大问题就是imsl程序库的调用,通过查找资料终于找到了出错的原因。 IMSL是美国Visual Numerics公司开发的业内最全面和最可靠的数学和统计算法程序库,是开发科学数值计算必备的开发包,历经36年的历史,被全球超过50多万的科研人员所采用,已成为该领域的工业标准。 IMSL(International Mathematical and Statistical Libraries)程序库包括IMSL C语言程序库、IMSL FORTRAN程序库与提供Java语言程序开发的JMSL以及IMSL C#程序库,在过去的三十年终早已成为数值分析解决方案中的工业标准。不论您是使用 C/C++ 、FORTRAN 、Java,和C# 语言在UNIX 、Windows 或者 Linux 平台上开发,IMSL 程序库皆提供最完整与最值得信赖的程序库。 Compaq Visual Fortran的专业版自带数值计算函数库IMSL,可以方便的进行各种运算,例如,矩阵的求逆,矩阵相乘,方程组求解。许多函数,不需要自己编写,减少了工作量。 在编写的程序代码中,矩阵运算使用了imsl程序库提供的矩阵计算功能,调用语句为use imsl。今晚在调试过程中,编译始终提示一个错误,变量名命名出现冲突,一查资料发现程序中有些变量的命名与imsl程序库的一些函数名相同,所以出现错误提示。这个bug找了好久,对imsl程序库的了解还只是皮毛而已,还需要不断的积累与学习。
个人分类: 专业学习笔记|411 次阅读|4 个评论
Latex 对程序源代码编辑 总结
热度 2 dalilijiang 2011-1-6 17:02
Latex对程序源代码的编辑有多种方式。 1. 对程序源代码的直接输出。 \begin{verbatim} ⇒程序源代码 \end{verbatim} 这种方式就是对\begin{verbatim}和 \end{verbatim}中间内容的直接显示,版面效果不佳。 其效果如下: 2. 应用listings宏包对程序源代码的高级编辑(主要应用于C、C++等) 应用listings宏包可以对程序源代码的关键字、自定义符号等进行特殊显示,如变换颜色,加粗。还可以自动显示程序行数。 但是,若程序中有中文说明,应用此宏包需使用escapeinside=``,即用符号框选,以便对中文进行显示。 \usepackage{listings} %添加宏包 \lstset %定义程序代码显示版面,可自定义。 { language=c++, numbers=left, numberstyle=\ttfamily\scriptsize, backgroundcolor=\color{grey}, frame=trbl,framesep=5pt,framexleftmargin=8mm,%frameround=tttt, basicstyle=\ttfamily\small, keywordstyle=\ttfamily\bf\color{Blue}, ndkeywordstyle=\ttfamily\bf\color{Brown}, commentstyle=\color{DarkGreen}, identifierstyle=\ttfamily\color{black}\bfseries, stringstyle=\color{pink}\ttfamily,showstringspaces=false, breaklines=true, escapeinside=``} \begin{lstlisting} `⇒程序源代码` \end{lstlisting} 其效果如下 3. Fortran程序源代码的Latex编辑 (1)一般Fortran习惯用大写字母,其程序源代码的大小写切换方法在Tex工具栏内就可以实现。其方法:\Edit→\Change case。 (2)Fortran程序源代码关键字的识别和显示。 其效果去下: 对于Fortran程序源代码需要用另外的方法对其进行高级编辑。
个人分类: Latex|14045 次阅读|2 个评论
Dev C++
shuli001 2010-11-23 16:34
Dev-C++是一个C/C++程序的集成开发环境,它是一款自由软件,遵守GPL协议。它使用MinGW32/GCC编译器,遵循C/C++标准。开发环境包括多页面窗口、工程编辑器以及调试器等。在工程编辑器中集合了编辑器、编译器、连接程序和执行程序。它提供高亮语法显示以减少编辑错误,还有一定的调试功能,能够适合初学者与编程高手的不同需求,是学习C/C++的不错选择。Dev-C++完全免费,你拥有对这一工具自由使用的权利,包括取得源代码等,而这一切得益于全球狂热者所做的努力工作。 Dev-C++并不是编译器,而是一个IDE(集成开发环境),它使用的编译器是GCC/G++。而GCC是目前对ANSI C,G++是对ISO C++支持最好的编译器。 信息学竞赛 认可的是ANSI C和ISO C++,因此用Dev-C++编译出来的东西提交后可以保证不出意外的错误。 C和C++都是编译语言,也就是说,我们不能直接运行C/C++源代码。要想运行用C/C++语言编写的程序,我们必须使用编译器将C/C++源文件编译成计算机可执行的文件。从源代码到可执行文件要经历三个步骤:预处理、编译,链接。常用的编译器有:Turbo C、Turbo C++(这两个太旧了,不推荐使用。严格地说,它们都是集成开发环境)、GCC、MicroSoft Visual C++(简称VC),Microsoft Visual Studio(简称VS,严格地说,它们都是集成开发环境)、Dev-C++(严格地说,它也是集成开发环境)等等。Dev-C++虽然功能不太强大,但是它对C/C++标准的支持非常好。而且该软件体积较小(最后版本devcpp-4.9.9.2_setup.exe约9MB),便于安装,对计算机配置的要求较低。利用Dev-C++来学习C/C++是个不错的选择。下载地址: http://prdownloads.sourceforge.net/dev-cpp/devcpp-4.9.9.2_setup.exe 相比之下,Dev-C++是比较纯粹的C/C++,而VC/VS里边有很多微软自己的东西,封装了很多MFC的类库。当然,VC/VS调试功能比Dev-C++强大,在windows应用程序开发上也有非常强大的优势! ---------------------- 根据网络资料整理。
个人分类: 未分类|5826 次阅读|3 个评论
巧改putchar函数,实现单片机下多形式输出
chrujun 2010-9-29 16:13
最近看学生编的单片机C程序,发现多个地方出现类似的下面两行: len=sprintf(sdata,@:%c %02u-%02u-%02u %02u:%02u:%02u\r\n, DataBuf , (unsigned int)DataBuf +2000,(unsigned int)DataBuf ,(unsigned int)DataBuf , (unsigned int)DataBuf ,(unsigned int)DataBuf ,(unsigned int)DataBuf ); for(i=0;ilen;i++) UART0_OP(sdata ); 第一行生成一个字符串,第二行将字符串通过单片机串口1输出。UART0_OP函数如下: void UART0_OP (unsigned char opdata){ unsigned char outputdata; outputdata=opdata; ES0=0; EA=0; SBUF0=outputdata; while(TI0==0); TI0=0; ES0=1; EA=1;} 其实,可以把UART0_OP修改成putchar()函数,之后可以用printf等函数直接输出。在单片机编译器的LIB目录下,原有的putchar()函数内容如下: /* * putchar (full version): expands '\n' into CR LF and handles * XON/XOFF (Ctrl+S/Ctrl+Q) protocol */ char putchar (char c) { if (c == '\n') { if (RI) { if (SBUF == XOFF) { do { RI = 0; while (!RI); } while (SBUF != XON); RI = 0; } } while (!TI); TI = 0; SBUF = 0x0d; /* output CR */ } if (RI) { if (SBUF == XOFF) { do { RI = 0; while (!RI); } while (SBUF != XON); RI = 0; } } while (!TI); TI = 0; return (SBUF = c); } #if 0 // comment out versions below /* * putchar (basic version): expands '\n' into CR LF */ char putchar (char c) { if (c == '\n') { while (!TI); TI = 0; SBUF = 0x0d; /* output CR */ } while (!TI); TI = 0; return (SBUF = c); } /* * putchar (mini version): outputs charcter only */ char putchar (char c) { while (!TI); TI = 0; return (SBUF = c); } #endif 这个函数有3个版本,最后一个函数是最简单的,我进行了如下修改: /* * putchar (mini version): outputs charcter only */ char putchar (char c) { ES0=0; EA=0; SBUF0=c; while(TI0==0); TI0=0; ES0=1; EA=1; return c; } 然后把其它两个函数设置成不编译模式。将这个函数加入工程文件后, 前面一段程序可以作如下修改: printf(@:%c %02u-%02u-%02u %02u:%02u:%02u\r\n, DataBuf , (unsigned int)DataBuf +2000,(unsigned int)DataBuf ,(unsigned int)DataBuf , (unsigned int)DataBuf ,(unsigned int)DataBuf ,(unsigned int)DataBuf ); 也就是说,修改和加入putchar()函数后,我们可以用printf, puts等函数实现输出。 事实上,只要修改LIB下的getkey, 和putchar函数,让这两个函数实现单个字符的输入和输出,我们就可以用丰富的标准I/O函数实现字符串的输入和输出,而不用去编写专门的字符串输入和输出函数。 对学生编写的程序进行全面修改后,编译后的代码少了10%以上,对字符的输出也更加方便和灵活了,也去掉了专门的数组sdata.
个人分类: 地球物理及仪器|9446 次阅读|1 个评论
C8051单片机C语言程序设计的几个深层次问题及解决方法
chrujun 2010-9-29 11:17
当在单片机(8051)环境下编写C语言程序设计时,常常会发生一些奇怪的现象。一个变量明明赋值为500,在运行过程中却莫名其妙地变成1000。 在WINDOWS下运行完全正常的算法,移植到单片机上就会出错。 如果无法保证一个变量的值按照程序设计者的要求变化,程序出现各种乱象就不奇怪了。 这种现象出现在C语言程序比较长,子程序比较多,或是子程序的层次比较多的场合。 因此,在单片机环境下用C语言开发比较大的程序,很容易出现各种问题。 这种问题出在编译器优化上。由于单片机资源有限,编译器采取了很多优化手段来减少资源占用。 在KEIL C上,编译器优化的层次有9级。然而,仅仅是二级优化(Data overlaying),就会导致长程序出现问题。 对于比较长的程序,应该只使用一级优化,更先进的优化最好不要用,否则会出现无法预料的问题。 用单片机开发比较长的程序时,应该选择有大容量程序存储空间和数据存储空间的单片机,将编译器优化限制在第一级,其它层次优化尽量避免用。 因为二级优化的数据覆盖手段可以把变量值改变,导致程序运行出错。
个人分类: 地球物理及仪器|7785 次阅读|4 个评论
[转载]MEX文件中给一个double矩阵赋值的几种方法
w52191114 2010-8-12 10:04
MEX文件中给一个double矩阵赋值的几种方法 MEX文件中想给一个mxArray赋值,有如下几种方法: 1、直接指向mxArray元素进行赋值,如: plhs = mxCreateDoubleMatrix(1, 2, mxREAL); // 一行二列 double *p = mxGetPr(plhs ); // 指向mxArray第一个元素 *p = 123; ++p;// 指向mxArray下一个元素 *p = 321; 2、使用C函数memcpy,如: #include mex.h #include string.h // memcpy函数必须此头文件 void mexFunction(/*...*/) {  plhs = mxCreateDoubleMatrix(1, 2, mxREAL);  double *p = mxGetPr(plhs );  double data = mxCreateDoubleMatrix(1, 2, mxREAL); double *data = (double *)mxCalloc(2, sizeof(double)); data = 123; data = 321; mxFree(mxGetPr(plhs )); mxSetPr(plhs , data); 要注意的是,mxSetPr函数不会释放原本已存在的内存,所以在使用mxSetPr函数前,先使用mxFree把mxArray中内容的内存释放,否则后果严重。另外,mxSetPr函数的第二个参数必须是使用mxCalloc分配内存,注意mxCalloc函数前加上(double *),把mxCalloc返回的(void *)类型强制转换为(double *)。 详细内容参加Matlab帮助里的External Interfaces一章。
个人分类: matlab|4127 次阅读|0 个评论
[转载]Matlab调用C语言函数
w52191114 2010-8-12 09:32
Matlab调用C语言函数 如果我有一个用C语言写的函数,实现了一个功能,如一个简单的函数: double add(double x, double y) {  return x + y; } 现在我想要在Matlab中使用它,比如输入: a = add(1.1, 2.2) 3.3000 要得出以上的结果,那应该怎样做呢? 解决方法之一是要通过使用MEX文件,MEX文件使得调用C函数和调用Matlab的内置函数一样方便。MEX文件是由原C代码加上MEX文件专用的接口函数后编译而成的。 可以这样理解,MEX文件实现了一种接口,它把在Matlab中调用函数时输入的自变量通过特定的接口调入了C函数,得出的结果再通过该接口调回Matlab。该特定接口的操作,包含在mexFunction这个函数中,由使用者具体设定。 所以现在我们要写一个包含add和mexFunction的C文件,Matlab调用函数,把函数中的自变量(如上例中的1.1和2.2)传给mexFunction的一个参数,mexFunction把该值传给add,把得出的结果传回给mexFunction的另一个参数,Matlab通过该参数来给出在Matlab语句中调用函数时的输出值(如上例中的a)。 比如该C文件已写好,名为add.c。那么在Matlab中,输入: mex add.c 就能把add.c编译为MEX文件(编译器的设置使用指令mex -setup),在Windows中,MEX文件类型为mexw32,即现在我们得出add.mexw32文件。现在,我们就可以像调用M函数那样调用MEX文件,如上面说到的例子。所以,通过MEX文件,使用C函数就和使用M函数是一样的了。 我们现在来说mexFunction怎样写。 mexFunction的定义为: void mexFunction(      int nlhs,      mxArray *plhs ) { /*....................................*/ } 可以看到,mexFunction是没返回值的,它不是通过返回值把结果传回Matlab的,而是通过对参数plhs的赋值。mexFunction的四个参数皆是说明Matlab调用MEX文件时的具体信息,如这样调用函数时: b = 1.1; c = 2.2; a = add(b, c) mexFunction四个参数的意思为: nlhs = 1,说明调用语句左手面(lhs-left hand side)有一个变量,即a。 nrhs = 2,说明调用语句右手面(rhs-right hand side)有两个自变量,即b和c。 plhs是一个数组,其内容为指针,该指针指向数据类型mxArray。因为现在左手面只有一个变量,即该数组只有一个指针,plhs 指向的结果会赋值给a。 prhs和plhs类似,因为右手面有两个自变量,即该数组有两个指针,prhs 指向了b,prhs 指向了c。要注意prhs是const的指针数组,即不能改变其指向内容。 因为Matlab最基本的单元为array,无论是什么类型也好,如有double array、 cell array、 struct array所以a,b,c都是array,b = 1.1便是一个1x1的double array。而在C语言中,Matlab的array使用mxArray类型来表示。所以就不难明白为什么plhs和prhs都是指向mxArray类型的指针数组。 完整的add.c如下: // add.c #include mex.h // 使用MEX文件必须包含的头文件 // 执行具体工作的C函数 double add(double x, double y) {  return x + y; } // MEX文件接口函数 void mexFunction(  int nlhs,  mxArray *plhs ) {  double *a;  double b, c;  plhs = mxCreateDoubleMatrix(1, 1, mxREAL);  a = mxGetPr(plhs );  b = *(mxGetPr(prhs ));  c = *(mxGetPr(prhs ));  *a = add(b, c); } mexFunction的内容是什么意思呢?我们知道,如果这样调用函数时: output = add(1.1, 2.2); 在未涉及具体的计算时,output的值是未知的,是未赋值的。所以在具体的程序中,我们建立一个1x1的实double矩阵(使用mxCreateDoubleMatrix函数,其返回指向刚建立的mxArray的指针),然后令plhs 指向它。接着令指针a指向plhs 所指向的mxArray的第一个元素(使用mxGetPr函数,返回指向mxArray的首元素的指针)。同样地,我们把prhs 和prhs 所指向的元素(即1.1和2.2)取出来赋给b和c。于是我们可以把b和c作自变量传给函数add,得出给果赋给指针a所指向的mxArray中的元素。因为a是指向plhs 所指向的mxArray的元素,所以最后作输出时,plhs 所指向的mxArray赋值给output,则output便是已计算好的结果了。 上面说的一大堆指向这指向那,什么mxArray,初学者肯定都会被弄到头晕眼花了。很抱歉,要搞清楚这些乱糟糟的关系,只有多看多练。 实际上mexFunction是没有这么简单的,我们要对用户的输入自变量的个数和类型进行测试,以确保 输入正确。如在add函数的例子中,用户输入char array便是一种错误了。 从上面的讲述中我们总结出,MEX文件实现了一种接口,把C语言中的计算结果适当地返回给Matlab罢了。当我们已经有用C编写的大型程序时,大可不必在Matlab里重写,只写个接口,做成MEX文件就成了。另外,在Matlab程序中的部份计算瓶颈(如循环),可通过MEX文件用C语言实现,以提高计算速度。
个人分类: matlab|3163 次阅读|0 个评论
3. If else编程习惯
wjwqbit 2010-7-29 23:02
如果你用过 KeilC 编译器,你可能会碰到这样的错误,如下: #define LED1_ON(){PORTB|=0x01;} #define LED2_ON(){PORTB|=0x02;} int value ; value =1; if(value0) LED1_ON(); else LED2_ON(); 单独看 ifelse 语句,似乎没有任何问题。但却无法通过编译,编译提示: errorC141:syntaxerrornear'else' 。分析一下, LED1_ON() 为宏定义,表示遇到 LED1_ON() 的地方就用 {PORTB|=0x01;} 替换。于是 int value ; value =1; if(value0) {PORTB|=0x01;} ; Else {PORTB|=0x02;} ; 注意到了吧,用红色标注的分号 ; 是多余的,上述代码相当于 int value ; value =1; if(value0) {PORTB|=0x01;} ; Else {PORTB|=0x02;} ; 去掉红色 ; 代码编译通过。 分析原因, 宏定义 LED1_ON() 加了括号本意是让它看起来像函数,然后在 C 语言中函数后面一般都要加分号;所以在代码中调用时自然写成 LED1_ON(); 的形式。没想到随意加的分号 ; 却成了罪魁祸首。 为了防止这种错误出现,建议写 ifelse 语句时不管是当行还是多行都要用加大括号 {} ,这样多余的;也不会使程序出错,只是多执行了一行无关的语句;。 #define LED1_ON(){PORTB|=0x01;} #define LED2_ON(){PORTB|=0x02;} int value ; value =1; if(value0) { LED1_ON(); } else { LED2_ON(); } 当然对于单行代码的宏定义还有另外一些编程习惯,比如下面一种也是可取的。这样的好处是读者一看就知道 LED1_ON 是宏定义而不是函数的调用。 #define LED1_ONPORTB|=0x01 #define LED2_ONPORTB|=0x02 int value ; value =1; if(value0) LED1_ON; else LED2_ON;
个人分类: 嵌入式C语言编程常见错误(连载)|3891 次阅读|0 个评论
1.数组边界溢出问题(2010.07.20)
wjwqbit 2010-7-29 22:48
很高兴今天我的博客开了 嵌入式C语言编程常见错误 这个模块。这是笔者在自己实际项目中遇到的编程问题,把它们列出来,一是为了提醒自己以后别犯这些错误了,另外也为读者提供了一些编程习惯的参考和告诫。 以后还可能会遇到一些其他编程问题,笔者将会继续写下来。另外也会提出一些编程经验,仅供学习参考。 定义一维数组 ADC_Buff ,数组下标上溢出时,另一变量 TempCalibration 被奇妙的修改了。例子如下(编译环境为 ICCAVR ): int ADC_Buff ={0}; //ADC 转换缓冲区 char TempCalibration=200; unsigned char ADC_Buff_Index; #pragmainterrupt_handlerADC_Isr:iv_ADC void ADC_Isr( void ) //ADC 转换结束中断 { ADC_Buff =(ADCH8)+ADCL; Set_ADC_Read_Flag; ADC_Buff_Index++; } // 主函数 Voidmain(void) { ADC_Init(); ADC_SelChanl(0); ADC_Start; While(1) { if (ADC_Read_Flag) { Clear_ADC_Read_Flag; if ( ADC_Buff_Index1 0 ) { Set_ADC_Sampled_Flag; ADC_Buff_Index=0; } else { ADC_Start; } } } // end while(1) } // end main 主要红色字体部分,主函数启动 AD 采样后,单次 AD 采样完成后自动进入 AD 中断处理函数。第 10 次 AD 中断处理完成后, ADC_Buff_Index 是 10 。返回主函数后执行到 if ( ADC_Buff_Index1 0 ) 时, if 条件不成立,则执行 else 中内容在次启动 AD 采样 ( 实际上我需要在这里就停止采样了 ) ,再次进入 AD 中断服务函数,此时读取 ADC 采样值 ADC_Buff =(ADCH8)+ADCL; 注意数组下标 ADC_Buff_Index 是 10 ,因此是给缓存 ADC_Buff 赋值,显然数组下标越界了。那么实际上 ADC_Buff 是哪个变量呢 ? 其实, C 语言编译器对有初始化的变量的初始化是连续的,什么意思呢?看看在定义了 ADC_Buff 后,定义的是哪个变量?是 TempCalibration 。编译器在编译程序时,变量 TempCalibration 是紧跟在 ADC_Buff 之后的。内存中的位置如下: ...... ADC_Buff ADC_Buff ...... ADC_Buff TempCalibration ...... 因此在给 ADC_Buff 赋值是,实际上指针指向的是 TempCalibration ,所以 TempCalibration 被奇妙的修改了。将 if ( ADC_Buff_Index1 0 ) 改为 if ( ADC_Buff_Index 9 ) 后问题消除。
个人分类: 嵌入式C语言编程常见错误(连载)|4142 次阅读|0 个评论
c语言指针的一个有意思的小测验
hillpig 2010-7-27 04:23
大家看看下面的代码: char a ='a'; char* b=(char*)a; char* b2 = (char*)a; char* c= (char *)b; 猜猜 b=? b2=? c=? 运行程序跑一跑,就会得到: a 'a' b 0x00000061 b2 0xbfcd17ef c 0x00000061 看样子,这行代码char* b=(char*)a; 大家要小心,把a的值当成指针,然后做强制转换。 加我私人微信,交流技术。
个人分类: c语言|3695 次阅读|2 个评论
[转载]【趣闻】Google不让C语言之父提交代码
NatureXin 2010-4-22 10:09
2010-04-22 01:39 | 次阅读 | 【已有 23 条评论】 发表评论 关键词: Google | 感谢 liujiangCE 的提供 | 收藏这篇资讯 哀悼结束,生活还要继续。 说段趣闻吧。大家都知道,C语言和Unix的发明者、图灵奖得主、最具传奇性的程序员Ken Thomson加盟Google之后,与一帮高手一起捣鼓出了又一惊天之作:并发时代的系统编程语言Go。Go一经面世就闯入了编程语言排行榜前20,创造了奇迹。 可是, Gawker网站今天爆料 ,他在Google居然没有提交代码的权力!原因呢,只不过是按公司规定,所有程序员必须通过编程语言考试,而他还没有参加过这种考试,至少在《Coders at Works》一书写作前: Peter Seibel: 我知道Google有一个规定,每个新员工都要在接受编程语言测试之后,才允许提交代码。那就是说你也得考(你自己发明的)C罗? Thompson: 是啊,我还没考呢。 Seibel: 你还没考? 难道你还不能提交代码吗? Thompson: 是啊,我不能提交代码,不行我只是还没有去考试,还没觉得有必要去考。 看来Google真是一家唯算法唯规则的公司。三年前,Google曾被曝光用算法和机器人程序来给申请者提交的简历打分。此外还有很多招聘和面试程序中的古怪事情不断见诸报端。 无独有偶,昨天成为CSDN头条的文章从盖茨到扎克伯格:极客的力量中,也爆出开发Mac操作系统核心程序员之一Hertzfeld现在在Google也不快乐: 使赫兹菲尔德发生变化的不只是时间,还有他的工作环境。谷歌将工程师看作最重要的资产,认为员工必须喜欢自己从事的工作,同时支持开源软件。但赫兹菲尔德承认,谷歌是一家大公司,在产品设计方面有严格的标准和程序,因此减少了他工作中的乐趣。他说:我与工作的关系是艺术家与他的作品的关系,但在谷歌,我无法从自己的工作中获得快乐。 尽管个人的控制力降低了,但赫兹菲尔德拥有了产生更大影响的可能性。有时,谷歌的几行代码可能会影响成千上万的人,这为他的工作带来了一种激情。他说:这里的一切都是主流的。谷歌、iPhone,这些比上世纪60年代甲壳虫乐队更能影响文化,它们甚至会影响整个人类。 对了, 《Coders at Work》 一书是对15位顶级程序员(包括图灵奖得主高德纳、Erlang和JavaScript 之父、Norvig、Guy Steele等等大师)的访谈集,在同类书中是最有趣、最有料而且最精彩的一本。中文版还在翻译中,将由人民邮电出版社图灵公司出版。微软研究院的邹欣做了 不错的读书笔记1 , 2 , 3 , 4 ,大家可以去先睹为快。搞技术的,了解高手的思想有时候至关重要。
个人分类: 未分类|3239 次阅读|0 个评论
二维TDMA的C语言实现
chmaolin 2009-8-19 13:57
#include math.h #include iostream.h #include stdlib.h // #include stdafx.h double *TDMAsolver2(double *,double *,double *,double *,double *,double *,double *,int,int); void main() { //测试二维TDMA double aW ={ 0,0,0, 0,10,10, 10,10,10, 10,10,10}; double aE ={ 10,10,10, 10,10,10, 10,10,0, 0,0,0}; double aS ={ 0,10,10, 10,0,10, 10,10,0, 10,10,10}; double aN ={ 10,10,10, 0,10,10, 10,0,10, 10,10,0}; double aP ={ 20,30,30, 40,30,40, 40,50,20, 30,30,40}; double S ={ 500,500,500, 2500,0,0, 0,2000,0, 0,0,2000}; double y ={ 0,0,0, 0,0,0, 0,0,0, 0,0,0}; TDMAsolver2(aW,aE,aP,aS,aN,S,y,3,4); for(i=0;i3;i++) { for(int j=0;j4;j++) { couty ; } coutendl; } } //**********************************************************************************/ /* */ /* 本子函数为二维TDMA算法程序 */ /* */ /**********************************************************************************/ double *TDMAsolver2(double *aa,double *bb,double *cc,double *dd,double *ee,double *resres,double *xx,int x_length,int y_length) //aa为5对角系数矩阵对角下方系数 //bb为5对角系数矩阵对角次下方系数 //cc为5对角系数矩阵对角系数 //dd为5对角系数矩阵对角次上方系数 //ee为5对角系数矩阵对角上方系数 //resres为结果矩阵 //xx为方程求解结果 //为了指针传递方便,将系数矩阵等二维数组按i{j}顺序写成一维数组的形式,在子函数中再还原 //方程形式为-a *x -b *x +c *x -d *x -e *x =res //a,b,c,d,e等将在下面进行一维数组地址到二维数组变量的转化 //x_length为x方向节点数,y_length为y方向节点数 //先x方向后y方向迭代求解 { int count=0; //设置迭代次数计数器 int i,j; double error; //定义迭代中误差,用于控制迭代次数,误差error采用相对误差 error=0.0; double **a,**b,**c,**d,**e,**res,**x; //定义二维数字用于放置系数矩阵等实参 //a为aW,b为aE,c为aP,d为aS,e为aN,res为S,x为待求未知数 //由于new运算符在开辟多维数组内存空间时,第一维必须是常量, //这里采用malloc开辟内存空间,增强其通用性 a=(double **)malloc((x_length)*sizeof(double*)); b=(double **)malloc((x_length)*sizeof(double*)); c=(double **)malloc((x_length)*sizeof(double*)); d=(double **)malloc((x_length)*sizeof(double*)); e=(double **)malloc((x_length)*sizeof(double*)); res=(double **)malloc((x_length)*sizeof(double*)); x=(double **)malloc((x_length)*sizeof(double*)); for(i=0;ix_length;i++) { a =(double *)malloc((y_length)*sizeof(double)); b =(double *)malloc((y_length)*sizeof(double)); c =(double *)malloc((y_length)*sizeof(double)); d =(double *)malloc((y_length)*sizeof(double)); e =(double *)malloc((y_length)*sizeof(double)); res =(double *)malloc((y_length)*sizeof(double)); x =(double *)malloc((y_length)*sizeof(double)); } //将实参传递过来的地址内的数组放置上面定义的二维数组内 for(i=0;ix_length;i++) for(j=0;jy_length;j++) { a =*aa;aa=aa+1; b =*bb;bb=bb+1; c =*cc;cc=cc+1; d =*dd;dd=dd+1; e =*ee;ee=ee+1; res =*resres;resres=resres+1; x =*xx;xx=xx+1; } double **x_temp; //定义x_temp;存放上一个迭代步骤中的计算值,用于与本次迭代结果进行对比 x_temp=(double **)malloc((x_length)*sizeof(double*)); for(i=0;ix_length;i++) { x_temp =(double *)malloc((y_length)*sizeof(double)); } for(i=0;ix_length;i++) for(j=0;jy_length;j++) { x_temp =x ; //将x的值赋予x_temp } //double *Ax=new double ; //回代通用公式中的Aj //double *Cx=new double ; //回代通用公式中的Cj //double *Cy=new double ; //回代通用公式中的Cj //double *Ay=new double ; //回代通用公式中的Aj //采用new开辟内存空间,计算中出现莫名错误,故仍采用malloc开辟内存空间 double *Ax,*Cx,*Ay,*Cy; Ax=(double *)malloc((y_length)*sizeof(double)); for(j=0;jy_length;j++) Ax =0; Cx=(double *)malloc((y_length)*sizeof(double)); for(j=0;jy_length;j++) Cx =0; Ay=(double *)malloc((y_length)*sizeof(double)); for(j=0;jy_length;j++) Ay =0; Cy=(double *)malloc((y_length)*sizeof(double)); for(j=0;jy_length;j++) Cy =0; do{ error=0.0; //x方向做TDMA j=0; { if(c !=0) { Ax =b /c ; for(i=1;ix_length;i++) Ax =b /(c -a *Ax ); Cx =(res +e *x )/c ; for(i=1;ix_length;i++) Cx =(a *Cx +(res +e *x ))/(c -a *Ax ); x =Cx ; for(i=x_length-2;i=0;i--) x =Ax *x +Cx ; } } for(j=1;jy_length-1;j++) { if(c !=0) { Ax =b /c ; for(i=1;ix_length;i++) Ax =b /(c -a *Ax ); Cx =(res +d *x +e *x )/c ; for(i=1;ix_length;i++) Cx =(a *Cx +(res +d *x +e *x ))/(c -a *Ax ); x =Cx ; for(i=x_length-2;i=0;i--) x =Ax *x +Cx ; } } j=y_length-1; { if(c !=0) { Ax =b /c ; for(i=1;ix_length;i++) Ax =b /(c -a *Ax ); Cx =(res +d *x )/c ; for(i=1;ix_length;i++) Cx =(a *Cx +(res +d *x ))/(c -a *Ax ); x =Cx ; for(i=x_length-2;i=0;i--) x =Ax *x +Cx ; } } //y方向做TDMA i=0; { if(c !=0) { Ay =e /c ; for(j=1;jy_length;j++) Ay =e /(c -d *Ay ); Cy =(res +b *x )/c ; for(j=1;jy_length;j++) Cy =(d *Cy +(res +b *x ))/(c -d *Ay ); x =Cy ; for(j=y_length-2;j=0;j--) x =Ay *x +Cy ; } } for(i=1;ix_length-1;i++) { if(c !=0) { Ay =e /c ; for(j=1;jy_length;j++) Ay =e /(c -d *Ay ); Cy =(res +a *x +b *x )/c ; for(j=1;jy_length;j++) Cy =(d *Cy +(res +a *x +b *x ))/(c -d *Ay ); x =Cy ; for(j=y_length-2;j=0;j--) x =Ay *x +Cy ; } } i=x_length-1; { if(c !=0) { Ay =e /c ; for(j=1;jy_length;j++) Ay =e /(c -d *Ay ); Cy =(res +a *x )/c ; for(j=1;jy_length;j++) Cy =(d *Cy +(res +a *x ))/(c -d *Ay ); x =Cy ; for(j=y_length-2;j=0;j--) x =Ay *x +Cy ; } } //进行误差计算 for(i=0;ix_length;i++) for(j=0;jy_length;j++) { if(x_temp !=0) { if(fabs((x_temp -x )/x_temp )error) error=fabs((x_temp -x )/x_temp ); } else { if(fabs(x )error) error=fabs(x ); } } //将本次迭代值存入x_temp数组中,便于下次迭代结果误差计算 for(i=0;ix_length;i++) for(j=0;jy_length;j++) {x_temp =x ;} count++; //迭代计数器+1 }while(error0.0001); //由误差决定的迭代条件 cout迭代次数为:countendl; //将迭代结果赋予返回的数组xx for(i=x_length-1;i=0;i--) for(j=y_length-1;j=0;j--) { xx=xx-1; *xx=x ; } return (xx); }
个人分类: 未分类|3925 次阅读|1 个评论
一维TDMA程序(C语言)
chmaolin 2009-8-18 15:53
#include math.h #include iostream.h double *TDMAsolver1(double *,double *,double *,double *,double *,int); void main() { double a ={0,5,5,5,5}; double b ={20,15,15,15,10}; double c ={5,5,5,5,0}; double res ={1100,100,100,100,100}; double x ; TDMAsolver1(a,b,c,res,x,5); for(int i=0;i5;i++) coutx endl; } /**********************************************************************************/ /* */ /* 本子函数为一维TDMA算法程序 */ /* */ /**********************************************************************************/ double *TDMAsolver1(double *a,double *b,double *c,double *res,double *x,int length) //a为系数矩阵对角下方系数 //b为系数矩阵对角系数 //c为系数矩阵对角上方系数 //res为结果矩阵 //x为方程求解结果 //方程形式为-a *x +b *x -c *x =res //length为矩阵维数(未知数个数=网格点数) { double *AA=new double ; //回代通用公式中的Aj double *CC=new double ; //回代通用公式中的Cj //回代通用公式为x =AA *x +CC if(b !=0) { AA =c /b ; for(int i=1;ilength;i++) AA =c /(b -a *AA ); CC =res /b ; for(i=1;ilength;i++) CC =(a *CC +res )/(b -a *AA ); x =CC ; for(i=length-2;i=0;i--) x =AA *x +CC ; return (x); } else return (x); }
个人分类: 未分类|5354 次阅读|2 个评论

Archiver|手机版|科学网 ( 京ICP备07017567号-12 )

GMT+8, 2024-6-11 12:38

Powered by ScienceNet.cn

Copyright © 2007- 中国科学报社

返回顶部