『壹』 VC++ 【数据库 查询记录】
境 创建一个标准的MFC AppWizard(exe)应用程序,然后在使用ADO数据库的InitInstance函数中初始化OLE/COM库(因为ADO库是一个COM DLL库)。本例为: BOOL CAdotestDlg::OnInitDialog() { ::CoInitialize(NULL); //初始化OLE/COM库环境 } 程序最后要调用 ::CoUninitialize();//释放程序占用的COM 资源。另外: m_pRecordset->Close(); 注意!!!不要多次关闭!!!!!!!!!!!!m_pConnection->Close();m_pRecordset = NULL;m_pConnection = NULL; 2. 引入ADO库文件 使用ADO前必须在工程的stdafx.h文件最后用直接引入符号#import引入ADO库文件,以使编译器能正确编译。代码如下:#import "C:\Program Files\common files\system\ado\msado15.dll" no_namespace rename("EOF","adoEOF") ADO类的定义是作为一种资源存储在ADO DLL(msado15.dll)中,在其内部称为类型库。类型库描述了自治接口,以及C++使用的COM vtable接口。当使用#import指令时,在运行时Visual C++需要从ADO DLL中读取这个类型库,并以此创建一组C++头文件。这些头文件具有.tli 和.tlh扩展名,读者可以在项目的目录下找到这两个文件。在C++程序代码中调用的ADO类要在这些文件中定义。 程序的第三行指示ADO对象不使用名称空间。在有些应用程序中,由于应用程序中的对象与ADO中的对象之间可能会出现命名冲突,所以有必要使用名称空间。如果要使用名称空间,则可把第三行程序修改为: rename_namespace("AdoNS")。第四行代码将ADO中的EOF(文件结束)更名为adoEOF,以避免与定义了自己的EOF的其他库冲突。 3.利用智能指针进行数据库操作 在CaboutDlg头文件中定义两个ADO智能指针类实例,并在对话框中加入一个ListCtrl。 class CAdotestDlg : public CDialog{ _ConnectionPtr m_pConnection; _RecordsetPtr m_pRecordset; ClistCtrl m_List; ……} ADO库包含三个智能指针:_ConnectionPtr、_CommandPtr和_RecordsetPtr。_ConnectionPtr通常被用来创建一个数据连接或执行一条不返回任何结果的SQL语句,如一个存储过程。_CommandPtr返回一个记录集。它提供了一种简单的方法来执行返回记录集的存储过程和SQL语句。在使用_CommandPtr接口时,可以利用全局_ConnectionPtr接口,也可以在_CommandPtr接口里直接使用连接串。_RecordsetPtr是一个记录集对象。与以上两种对象相比,它对记录集提供了更多的控制功能,如记录锁定、游标控制等。 在使用ADO程序的事件响应中OnButton1加入以下代码: void CAdotestDlg::OnButton1() { m_List.ResetContent(); m_pConnection.CreateInstance(_uuidof(Connection)); //初始化Connection指针 m_pRecordset.CreateInstance(_uuidof(Recordset));//初始化Recordset指针 try { m_pConnection->Open("DSN=ADOTest","","",0); //连接叫作ADOTest的ODBC数据源 //注意:这是连接不需要用户ID或密码的open 函数 // 否则形式为 ->Open("DSN=test;uid=sa;pwd=123;","","",0); // 执行SQL语句得到一个记录集把其指针赋值给m_pRecordset CString strSql="select * from middle"; BSTR bstrSQL = strSql.AllocSysString(); m_pRecordset->Open(bstrSQL,(IDispatch*)m_pConnection,adOpenDynamic,adLockOptimistic,adCmdText); //adOpenDynamic:动态 adLockOptimistic乐观封锁法 adCmdText:文本查询语句 while(!m_pRecordset->adoEOF)//遍历所有记录 { //取纪录字段值方式之一 _variant_t TheValue; //VARIANT数据类型 TheValue = m_pRecordset->GetCollect("BIG_NAME");//得到字段BIG_NAME的值 if(TheValue.vt!=VT_NULL) m_List.AddString((char*)_bstr_t(TheValue)); //将该值加入到列表控件中 //取纪录字段值方式之二 // _bstr_t TheValue1=m_pRecordset->Fields->GetItem("BIG_NAME")->Value; // CString temp=TheValue1.(); // m_List.AddString(temp); //数据类型转换 _variant_t vUsername,vBirthday,vID,vOld; TRACE("id:%d,姓名:%s,年龄:%d,生日:%s\r\n", vID.lVal,(LPCTSTR)(_bstr_t)vUsername,vOld.lVal,(LPCTSTR)(_bstr_t)vBirthday); m_pRecordset->MoveNext();//转到下一条纪录 } m_pRecordset->Close(); m_pConnection->Close(); } catch (_com_error e)//异常处理 { AfxMessageBox(e.ErrorMessage()); } m_pRecordset->Close(); //注意!!!不要多次关闭!!!!否则会出错 m_pConnection->Close(); m_pRecordset = NULL; m_pConnection = NULL; } 程序中通过_variant_t和_bstr_t转换COM对象和C++类型的数据, _variant_t类封装了OLE自治VARIANT数据类型。在C++中使用_variant_t类要比直接使用VARIANT数据类型容易得多。 好,编译后该程序就能运行了,但记住运行前要创建一个叫ADOTest的ODBC数据源。该程序将把表middle中的BIG_NAME字段值显示在列表控件中。4.执行SQL命令并取得结果记录集 为了取得结果记录集,我们定义一个指向Recordset对象的指针:_RecordsetPtr m_pRecordset;并为其创建Recordset对象的实例: m_pRecordset.CreateInstance("ADODB.Recordset");SQL命令的执行可以采用多种形式,下面我们一进行阐述。(1)利用Connection对象的Execute方法执行SQL命令Execute方法的原型如下所示: _RecordsetPtr Connection15::Execute ( _bstr_t CommandText, VARIANT * RecordsAffected, long Options ) 其中CommandText是命令字串,通常是SQL命令。 参数RecordsAffected是操作完成后所影响的行数, 参数Options表示CommandText中内容的类型,Options可以取如下值之一: adCmdText:表明CommandText是文本命令 adCmdTable:表明CommandText是一个表名 adCmdProc:表明CommandText是一个存储过程 adCmdUnknown:未知 Execute执行完后返回一个指向记录集的指针,下面我们给出具体代码并作说明。 _variant_t RecordsAffected; ///执行SQL命令:CREATE TABLE创建表格users,users包含四个字段:整形ID,字符串username,整形old,日期型birthday m_pConnection->Execute("CREATE TABLE users(ID INTEGER,username TEXT,old INTEGER,birthday DATETIME)", &RecordsAffected, adCmdText); ///往表格里面添加记录 m_pConnection->Execute("INSERT INTO users(ID,username,old,birthday) VALUES (1, ''''Washington'''',25,''''1970/1/1'''')",&RecordsAffected,adCmdText); ///将所有记录old字段的值加一 m_pConnection->Execute("UPDATE users SET old = old+1",&RecordsAffected,adCmdText); ///执行SQL统计命令得到包含记录条数的记录集 m_pRecordset = m_pConnection->Execute("SELECT COUNT(*) FROM users",&RecordsAffected,adCmdText); _variant_t vIndex = (long)0; _variant_t vCount = m_pRecordset->GetCollect(vIndex);///取得第一个字段的值放入vCount变量 上两句可以写成— _variant_t vCount = m_pRecordset->GetCollect((_variant_t)((long)0)); m_pRecordset->Close();///关闭记录集 CString message; message.Format("共有%d条记录",vCount.lVal); AfxMessageBox(message);///显示当前记录条数 (2)利用Command对象来执行SQL命令 _CommandPtr m_pCommand;m_pCommand.CreateInstance("ADODB.Command");_variant_t vNULL;vNULL.vt = VT_ERROR;vNULL.scode = DISP_E_PARAMNOTFOUND;///定义为无参数m_pCommand->ActiveConnection = m_pConnection;///非常关键的一句,将建立的连接赋值给它m_pCommand->CommandText = "SELECT * FROM users";///命令字串m_pRecordset = m_pCommand->Execute(&vNULL,&vNULL,adCmdText);///执行命令,取得记录集 在这段代码中我们只是用Command对象来执行了SELECT查询语句,Command对象在进行存储过程的调用中能真正体现它的作用。下次我们将详细介绍。 (3)直接用Recordset对象进行查询取得记录集 实例—— void CGmsaDlg::OnDBSelect() { // TODO: Add your control notification handler code here _RecordsetPtr Rs1; //定义Recordset对象 _bstr_t Connect("DSN=GMS;UID=sa;PWD=;");//定义连接字符串 _bstr_t Source ("SELECT count(*) FROM buaa.mdb010"); //要执行的SQL语句 ::CoInitialize(NULL); //初始化Rs1对象 HRESUL hr = Rs1.CreateInstance( __uuidof( Recordset ) ); //省略对返回值hr的判断 Rs1->Open( Source, Connect, adOpenForwardOnly, adLockReadOnly, -1 ); _variant_t temp=Rs1->GetCollect(_variant_t((long)0)); CString strTemp=(char* )(_bstr_t)temp; MessageBox("OK!"+strTemp);}例如 m_pRecordset->Open("SELECT * FROM users", _variant_t((IDispatch *)m_pConnection,true), adOpenStatic, adLockOptimistic, adCmdText);Open方法的原型是这样的:HRESULT Recordset15::Open ( const _variant_t & Source, const _variant_t & ActiveConnection, enum CursorTypeEnum CursorType, enum LockTypeEnum LockType, long Options ) 其中:①Source是数据查询字符串②ActiveConnection是已经建立好的连接(我们需要用Connection对象指针来构造一个_variant_t对象) ③CursorType光标类型,它可以是以下值之一,请看这个枚举结构:enum CursorTypeEnum{ adOpenUnspecified = -1,///不作特别指定 adOpenForwardOnly = 0,///前滚静态光标。这种光标只能向前浏览记录集,比如用MoveNext向前滚动,这种方式可以提高浏览速度。但诸如BookMark,RecordCount,AbsolutePosition,AbsolutePage都不能使用 adOpenKeyset = 1,///采用这种光标的记录集看不到其它用户的新增、删除操作,但对于更新原有记录的操作对你是可见的。 adOpenDynamic = 2,///动态光标。所有数据库的操作都会立即在各用户记录集上反应出来。 adOpenStatic = 3///静态光标。它为你的记录集产生一个静态备份,但其它用户的新增、删除、更新操作对你的记录集来说是不可见的。};④LockType锁定类型,它可以是以下值之一,请看如下枚举结构:enum LockTypeEnum{ adLockUnspecified = -1,///未指定 adLockReadOnly = 1,///只读记录集 adLockPessimistic = 2,悲观锁定方式。数据在更新时锁定其它所有动作,这是最安全的锁定机制 adLockOptimistic = 3,乐观锁定方式。只有在你调用Update方法时才锁定记录。在此之前仍然可以做数据的更新、插入、删除等动作 adLockBatchOptimistic = 4,乐观分批更新。编辑时记录不会锁定,更改、插入及删除是在批处理模式下完成。}; ⑤Options可以取如下值之一: adCmdText:表明CommandText是文本命令 adCmdTable:表明CommandText是一个表名 adCmdProc:表明CommandText是一个存储过程 adCmdUnknown:未知 5. 记录集的遍历、更新 根据我们刚才通过执行SQL命令建立好的users表,它包含四个字段:ID,username,old,birthday以下的代码实现:打开记录集,遍历所有记录,删除第一条记录,添加三条记录,移动光标到第二条记录,更改其年龄,保存到数据库。 _variant_t vUsername,vBirthday,vID,vOld;_RecordsetPtr m_pRecordset;m_pRecordset.CreateInstance("ADODB.Recordset");m_pRecordset->Open("SELECT * FROM users", _variant_t((IDispatch*)m_pConnection,true), adOpenStatic, adLockOptimistic, adCmdText);while(!m_pRecordset->adoEOF){ vID = m_pRecordset->GetCollect(_variant_t((long)0));///取得第1列的值,从0开始计数, ///你也可以直接给出列的名称,如下一行 vUsername = m_pRecordset->GetCollect("username");///取得username字段的值 vOld = m_pRecordset->GetCollect("old"); vBirthday = m_pRecordset->GetCollect("birthday"); ///在DEBUG方式下的OUTPUT窗口输出记录集中的记录 if(vID.vt != VT_NULL && vUsername.vt != VT_NULL && vOld.vt != VT_NULL && vBirthday.vt != VT_NULL) TRACE("id:%d,姓名:%s,年龄:%d,生日:%s\r\n", vID.lVal, (LPCTSTR)(_bstr_t)vUsername, vOld.lVal, (LPCTSTR)(_bstr_t)vBirthday); m_pRecordset->MoveNext();///移到下一条记录}m_pRecordset->MoveFirst();///移到首条记录m_pRecordset->Delete(adAffectCurrent);///删除当前记录///添加三条新记录并赋值for(int i=0;i<3;i++){ m_pRecordset->AddNew();///添加新记录 m_pRecordset->PutCollect("ID",_variant_t((long)(i+10))); m_pRecordset->PutCollect("username",_variant_t("叶利钦")); m_pRecordset->PutCollect("old",_variant_t((long)71)); m_pRecordset->PutCollect("birthday",_variant_t("1930-3-15"));}m_pRecordset->Move(1,_variant_t((long)adBookmarkFirst));///从第一条记录往下移动一条记录,即移动到第二条记录处m_pRecordset->PutCollect(_variant_t("old"),_variant_t((long)45));///修改其年龄m_pRecordset->Update();///保存到库中 备注:多次查询可把查询过程做成一个函数ExecuteSQL让m_pRecordset获得连接指针m_pConnection查询结果 void ExecuteSQL(_ConnectionPtr m_pConnection, _RecordsetPtr m_pRecordset,CString strSql){ //执行Select 语句 BSTR bstrSQL = strSql.AllocSysString(); try { m_pRecordset->Open(bstrSQL,(IDispatch*)m_pConnection,adOpenDynamic,adLockOptimistic,adCmdText); //adOpenDynamic:动态 adLockOptimistic乐观封锁法 adCmdText:文本查询语句 } catch(_com_error error) { CString errorMessage; errorMessage.Format("%s",(LPTSTR)error.Description()); AfxMessageBox(errorMessage); }} //出错处理:3127——没有找到目标表3092——目标表已经存在例如:catch(const _com_error e){ AfxMessageBox(e.Description()); long errorCode=e.WCode(); if(3127==errorCode) AfxMessageBox("表不存在"); if(3092==errorCode) AfxMessageBox("表已经存在"); return FALSE;}
『贰』 VC中如何调用BCG的类大虾求解
网上找的,来源http://thisisxingmeng.blog.163.com/blog/static/4211528620087231197613/BCG中有很多控件,其中属性配置对话框功能很强大,为了单独使用BCG 的属性配置对话框,需要对代码有一些修改,下面是具体步骤如下:1、 在Stdafx.h中增加BCG的头文件,添加以后在项目使用BCG的任何部分就不在需要增加头文件,代码如下: #include <BCGCBProInc.h>2、 修改项目从CWinApp的继承关系,增加从CBCGPWorkspace的继承,如果不增加,在使用BCG的过程中会有一些问题,代码如下: class CTestControlConfigApp : public CWinApp, public CBCGPWorkspace3、 在CWinApp的子类的InitInstance函数中增加如下语句: AfxOleInit(); // 进行BCG使用控件的初始化4、 最后为了防止BCG资源泄露,需要在CWinApp的子类中的ExitInstance函数中增加如下语句: BCGCBProCleanUp ();5、最后,就可以使用属性配置类了第一步,声明一个变量CBCGPPropList m_wndPropList;第二步,在OnCreate或者OnInitDialog函数中初始化,例子代码如下:CRect rect(159,29,468,376); if (!m_wndPropList.Create (WS_VISIBLE | WS_CHILD, rect, this, 2)) { TRACE0("Failed to create Properies Grid \n"); return -1; // fail to create } m_wndPropList.EnableHeaderCtrl (FALSE); m_wndPropList.EnableDesciptionArea (); m_wndPropList.SetVSDotNetLook (); m_wndPropList.MarkModifiedProperties (); CBCGPProp* pGroup1 = new CBCGPProp (_T("Appearance")); pGroup1->AddSubItem (new CBCGPProp (_T("3D Look"), (_variant_t) false, _T("Specifies the dialog's font will be nonbold and controls will have a 3D border"))); CBCGPProp* pProp = new CBCGPProp (_T("Border"), _T("Dialog Frame"), _T("One of: None, Thin, Resizable, or Dialog Frame")); pProp->AddOption (_T("None")); pProp->AddOption (_T("Thin")); pProp->AddOption (_T("Resizable")); pProp->AddOption (_T("Dialog Frame")); pProp->AllowEdit (FALSE); pGroup1->AddSubItem (pProp); pGroup1->AddSubItem (new CBCGPProp (_T("Caption"), (_variant_t) _T("About BCGProTest"), _T("Specifies the text that will be displayed in the dialog's title bar"))); m_wndPropList.AddProperty (pGroup1); CBCGPProp* pSize = new CBCGPProp (_T("Window Size"), 0, TRUE); pProp = new CBCGPProp (_T("Height"), (_variant_t) 250l, _T("Specifies the dialog's height")); pProp->EnableSpinControl (TRUE, 0, 1000); pSize->AddSubItem (pProp); pProp = new CBCGPProp ( _T("Width"), (_variant_t) 150l, _T("Specifies the dialog's width")); pProp->EnableSpinControl (); pSize->AddSubItem (pProp); m_wndPropList.AddProperty (pSize); CBCGPProp* pGroup2 = new CBCGPProp (_T("Font")); LOGFONT lf; CFont* font = CFont::FromHandle ((HFONT) GetStockObject (DEFAULT_GUI_FONT)); font->GetLogFont (&lf); lstrcpy (lf.lfFaceName, _T("Arial")); pGroup2->AddSubItem (new CBCGPFontProp (_T("Font"), lf, CF_EFFECTS | CF_SCREENFONTS, _T("Specifies the default font for the dialog"))); pGroup2->AddSubItem (new CBCGPProp (_T("Use System Font"), (_variant_t) true, _T("Specifies that the dialog uses MS Shell Dlg font"))); m_wndPropList.AddProperty (pGroup2); CBCGPProp* pGroup3 = new CBCGPProp (_T("Misc")); pProp = new CBCGPProp (_T("(Name)"), _T("IDD_ABOUT_BOX (dialog)")); pProp->Enable (FALSE); pGroup3->AddSubItem (pProp); CBCGPColorProp* pColorProp = new CBCGPColorProp (_T("Window Color"), RGB (210, 192, 254), NULL, _T("Specifies the default dialog color")); pColorProp->EnableOtherButton (_T("Other…")); pColorProp->EnableAutomaticButton (_T("Default"), ::GetSysColor (COLOR_3DFACE)); pGroup3->AddSubItem (pColorProp); static TCHAR BASED_CODE szFilter[] = _T("Icon Files (*.ico)|*.ico|All Files (*.*)|*.*||"); pGroup3->AddSubItem (new CBCGPFileProp (_T("Icon"), TRUE, _T(""), _T("ico"), 0, szFilter, _T("Specifies the dialog icon"))); pGroup3->AddSubItem (new CBCGPFileProp (_T("Folder"), _T("c:\\"))); COleDateTime date = COleDateTime::GetCurrentTime (); pGroup3->AddSubItem (new CBCGPDateTimeProp (_T("Date"), date, _T("Set a date"), 0, CBCGPDateTimeCtrl::DTM_DATE)); pGroup3->AddSubItem (new CBCGPDateTimeProp (_T("Time"), date, _T("Set a time"), 0, CBCGPDateTimeCtrl::DTM_TIME)); m_wndPropList.AddProperty (pGroup3); CBCGPProp* pGroup4 = new CBCGPProp (_T("Hierarchy")); CBCGPProp* pGroup41 = new CBCGPProp (_T("First sub-level")); pGroup4->AddSubItem (pGroup41); CBCGPProp* pGroup411 = new CBCGPProp (_T("Second sub-level")); pGroup41->AddSubItem (pGroup411); pGroup411->AddSubItem (new CBCGPProp (_T("Item 1"), (_variant_t) _T("Value 1"), _T("This is a description"))); pGroup411->AddSubItem (new CBCGPProp (_T("Item 2"), (_variant_t) _T("Value 2"), _T("This is a description"))); pGroup411->AddSubItem (new CBCGPProp (_T("Item 3"), (_variant_t) _T("Value 3"), _T("This is a description"))); pGroup4->Expand (FALSE); m_wndPropList.AddProperty (pGroup4); m_wndPropList.ShowWindow(SW_SHOW);
『叁』 vc++中ParseDateTime()属于什么头文件
#include <ATLComTime.h>
『肆』 vc 16进制字符串转换成日期时间
这是通过FILETIME保存的时间 FILETIME The FILETIME structure is a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601 (UTC). typedef struct _FILETIME { DWORD dwLowDateTime; DWORD dwHighDateTime; } FILETIME, *PFILETIME; 代码:"40 E3 A8 F7 OE 81 B4 E8" FILETIME ft; ft.dwLowDateTime = 0xF7A8E340; ft.dwHighDateTime = 0xE8B481OE; CTime time(ft); cout << (LPCTSTR)time.Format("%Y-%m-%d %H:%M:%S") << endl; 8字节数据中前4字节为dwLowDateTime,后4字节为dwHighDateTime
『伍』 C++ 有什么时间函数,能做加减演算
一、在C++ builder中,使用前请使用以下语句包含头文件:
#include <DateUtils.hpp>
有:
IncYear、IncMonth、IncWeek、IncDay、IncHour、IncMinute、IncSecond、IncMilliSecond
例:
TDateTimeLS1;LS1=IncDay(Now(),1);Edit1->Text=LS1.DateString();
二、VC中稍麻烦一些:
COleDateTimedt;//定义时间变量dt=COleDateTime::GetCurrentTime();//得到当前时间dt=dt+COleDateTimeSpan(1,0,0,0);//当前时间加一天,请查COleDateTimeSpan函数
三、至于两个时间的时间差,想告诉你的是,C里都是把时间当数值,相对麻烦。VC和C++Builder里的函数不同,实现方式也不同。
『陆』 Visual C++6里创建源文件和头文件有什么区别
头文件和源文件在功能上,用法上,有很大不同头文件最大的特点就是,各种程序都可以包含它,可以起到代码的重用,提高效率,使得要写很多代码的程序直接用饱含头文件的方法就能实现。头文件可以想象成代码的等价替换,就像宏一样,如://在my.hint i;——//在my.cpp#include my.hmain(){…}这样编译后,就可以看成//int i;main(){…}另外,头文件不能有入口函数,比如main winmain。一般在头文件中常做一些宏定义,类型定义,相关声明等,像代码实现,全局变量定义,入口函数等都会在cpp文件中。比如://in file my.htypedef int BOOL //类型定义#define TRUE 1 //宏定义#define FALSE 0 //宏定义typedef struct Student{char* name;int age;}Student,* pStudent;//结构体定义class Teacher{public:char* name;public:vitual Display();//只声明有这个函数,却不实现它,它的实现在cpp中}//类定义
『柒』 c++中怎么样测一段程序执行的时间
不行啊试过了,没用——————–Configuration: Cpp1 – Win32 Debug——————–Compiling…Cpp1.cppc:\program files\microsoft visual studio\vc98\include\winbase.h(216) : error C2146: syntax error : missing ';' before identifier 'Internal'c:\program files\microsoft visual studio\vc98\include\winbase.h(216) : error C2501: 'DWORD' : missing storage-class or type specifiersc:\program files\microsoft visual studio\vc98\include\winbase.h(216) : error C2501: 'Internal' : missing storage-class or type specifiersc:\program files\microsoft visual studio\vc98\include\winbase.h(217) : error C2146: syntax error : missing ';' before identifier 'InternalHigh'c:\program files\microsoft visual studio\vc98\include\winbase.h(217) : error C2501: 'DWORD' : missing storage-class or type specifiersc:\program files\microsoft visual studio\vc98\include\winbase.h(217) : error C2501: 'InternalHigh' : missing storage-class or type specifiersc:\program files\microsoft visual studio\vc98\include\winbase.h(218) : error C2146: syntax error : missing ';' before identifier 'Offset'c:\program files\microsoft visual studio\vc98\include\winbase.h(218) : error C2501: 'DWORD' : missing storage-class or type specifiersc:\program files\microsoft visual studio\vc98\include\winbase.h(218) : error C2501: 'Offset' : missing storage-class or type specifiersc:\program files\microsoft visual studio\vc98\include\winbase.h(219) : error C2146: syntax error : missing ';' before identifier 'OffsetHigh'c:\program files\microsoft visual studio\vc98\include\winbase.h(219) : error C2501: 'DWORD' : missing storage-class or type specifiersc:\program files\microsoft visual studio\vc98\include\winbase.h(219) : error C2501: 'OffsetHigh' : missing storage-class or type specifiersc:\program files\microsoft visual studio\vc98\include\winbase.h(220) : error C2146: syntax error : missing ';' before identifier 'hEvent'c:\program files\microsoft visual studio\vc98\include\winbase.h(220) : error C2501: 'HANDLE' : missing storage-class or type specifiersc:\program files\microsoft visual studio\vc98\include\winbase.h(220) : error C2501: 'hEvent' : missing storage-class or type specifiersc:\program files\microsoft visual studio\vc98\include\winbase.h(224) : error C2146: syntax error : missing ';' before identifier 'nLength'c:\program files\microsoft visual studio\vc98\include\winbase.h(224) : error C2501: 'DWORD' : missing storage-class or type specifiersc:\program files\microsoft visual studio\vc98\include\winbase.h(224) : error C2501: 'nLength' : missing storage-class or type specifiersc:\program files\microsoft visual studio\vc98\include\winbase.h(225) : error C2146: syntax error : missing ';' before identifier 'lpSecurityDescriptor'c:\program files\microsoft visual studio\vc98\include\winbase.h(225) : error C2501: 'LPVOID' : missing storage-class or type specifiersc:\program files\microsoft visual studio\vc98\include\winbase.h(225) : error C2501: 'lpSecurityDescriptor' : missing storage-class or type specifiersc:\program files\microsoft visual studio\vc98\include\winbase.h(226) : error C2146: syntax error : missing ';' before identifier 'bInheritHandle'c:\program files\microsoft visual studio\vc98\include\winbase.h(226) : error C2501: 'BOOL' : missing storage-class or type specifiersc:\program files\microsoft visual studio\vc98\include\winbase.h(226) : error C2501: 'bInheritHandle' : missing storage-class or type specifiersc:\program files\microsoft visual studio\vc98\include\winbase.h(230) : error C2146: syntax error : missing ';' before identifier 'hProcess'c:\program files\microsoft visual studio\vc98\include\winbase.h(230) : error C2501: 'HANDLE' : missing storage-class or type specifiersc:\program files\microsoft visual studio\vc98\include\winbase.h(230) : error C2501: 'hProcess' : missing storage-class or type specifiersc:\program files\microsoft visual studio\vc98\include\winbase.h(231) : error C2146: syntax error : missing ';' before identifier 'hThread'c:\program files\microsoft visual studio\vc98\include\winbase.h(231) : error C2501: 'HANDLE' : missing storage-class or type specifiersc:\program files\microsoft visual studio\vc98\include\winbase.h(231) : error C2501: 'hThread' : missing storage-class or type specifiersc:\program files\microsoft visual studio\vc98\include\winbase.h(232) : error C2146: syntax error : missing ';' before identifier 'dwProcessId'c:\program files\microsoft visual studio\vc98\include\winbase.h(232) : error C2501: 'DWORD' : missing storage-class or type specifiersc:\program files\microsoft visual studio\vc98\include\winbase.h(232) : error C2501: 'dwProcessId' : missing storage-class or type specifiersc:\program files\microsoft visual studio\vc98\include\winbase.h(233) : error C2146: syntax error : missing ';' before identifier 'dwThreadId'c:\program files\microsoft visual studio\vc98\include\winbase.h(233) : error C2501: 'DWORD' : missing storage-class or type specifiersc:\program files\microsoft visual studio\vc98\include\winbase.h(233) : error C2501: 'dwThreadId' : missing storage-class or type specifiersc:\program files\microsoft visual studio\vc98\include\winbase.h(241) : error C2146: syntax error : missing ';' before identifier 'dwLowDateTime'c:\program files\microsoft visual studio\vc98\include\winbase.h(241) : error C2501: 'DWORD' : missing storage-class or type specifiersc:\program files\microsoft visual studio\vc98\include\winbase.h(241) : error C2501: 'dwLowDateTime' : missing storage-class or type specifiersc:\program files\microsoft visual studio\vc98\include\winbase.h(242) : error C2146: syntax error : missing ';' before identifier 'dwHighDateTime'c:\program files\microsoft visual studio\vc98\include\winbase.h(242) : error C2501: 'DWORD' : missing storage-class or type specifiersc:\program files\microsoft visual studio\vc98\include\winbase.h(242) : error C2501: 'dwHighDateTime' : missing storage-class or type specifiersc:\program files\microsoft visual studio\vc98\include\winbase.h(250) : error C2146: syntax error : missing ';' before identifier 'wYear'c:\program files\microsoft visual studio\vc98\include\winbase.h(250) : error C2501: 'WORD' : missing storage-class or type specifiersc:\program files\microsoft visual studio\vc98\include\winbase.h(250) : error C2501: 'wYear' : missing storage-class or type specifiersc:\program files\microsoft visual studio\vc98\include\winbase.h(251) : error C2146: syntax error : missing ';' before identifier 'wMonth'c:\program files\microsoft visual studio\vc98\include\winbase.h(251) : error C2501: 'WORD' : missing storage-class or type specifiersc:\program files\microsoft visual studio\vc98\include\winbase.h(251) : error C2501: 'wMonth' : missing storage-class or type specifiersc:\program files\microsoft visual studio\vc98\include\winbase.h(252) : error C2146: syntax error : missing ';' before identifier 'wDayOfWeek'c:\program files\microsoft visual studio\vc98\include\winbase.h(252) : error C2501: 'WORD' : missing storage-class or type specifiersc:\program files\microsoft visual studio\vc98\include\winbase.h(252) : error C2501: 'wDayOfWeek' : missing storage-class or type specifiersc:\program files\microsoft visual studio\vc98\include\winbase.h(253) : error C2146: syntax error : missing ';' before identifier 'wDay'c:\program files\microsoft visual studio\vc98\include\winbase.h(253) : error C2501: 'WORD' : missing storage-class or type specifiersc:\program files\microsoft visual studio\vc98\include\winbase.h(253) : error C2501: 'wDay' : missing storage-class or type specifiersc:\program files\microsoft visual studio\vc98\include\winbase.h(254) : error C2146: syntax error : missing ';' before identifier 'wHour'c:\program files\microsoft visual studio\vc98\include\winbase.h(254) : error C2501: 'WORD' : missing storage-class or type specifiersc:\program files\microsoft visual studio\vc98\include\winbase.h(254) : error C2501: 'wHour' : missing storage-class or type specifiersc:\program files\microsoft visual studio\vc98\include\winbase.h(255) : error C2146: syntax error : missing ';' before identifier 'wMinute'c:\program files\microsoft visual studio\vc98\include\winbase.h(255) : error C2501: 'WORD' : missing storage-class or type specifiersc:\program files\microsoft visual studio\vc98\include\winbase.h(255) : error C2501: 'wMinute' : missing storage-class or type specifiersc:\program files\microsoft visual studio\vc98\include\winbase.h(256) : error C2146: syntax error : missing ';' before identifier 'wSecond'c:\program files\microsoft visual studio\vc98\include\winbase.h(256) : error C2501: 'WORD' : missing storage-class or type specifiersc:\program files\microsoft visual studio\vc98\include\winbase.h(256) : error C2501: 'wSecond' : missing storage-class or type specifiersc:\program files\microsoft visual studio\vc98\include\winbase.h(257) : error C2146: syntax error : missing ';' before identifier 'wMilliseconds'c:\program files\microsoft visual studio\vc98\include\winbase.h(257) : error C2501: 'WORD' : missing storage-class or type specifiersc:\program files\microsoft visual studio\vc98\include\winbase.h(257) : error C2501: 'wMilliseconds' : missing storage-class or type specifiersc:\program files\microsoft visual studio\vc98\include\winbase.h(260) : error C2065: 'WINAPI' : undeclared identifierc:\program files\microsoft visual studio\vc98\include\winbase.h(260) : error C2065: 'PTHREAD_START_ROUTINE' : undeclared identifierc:\program files\microsoft visual studio\vc98\include\winbase.h(260) : error C2501: 'DWORD' : missing storage-class or type specifiersc:\program files\microsoft visual studio\vc98\include\winbase.h(260) : error C2143: syntax error : missing ';' before '('c:\program files\microsoft visual studio\vc98\include\winbase.h(262) : error C2146: syntax error : missing ')' before identifier 'lpThreadParameter'c:\program files\microsoft visual studio\vc98\include\winbase.h(262) : error C2146: syntax error : missing ';' before identifier 'lpThreadParameter'c:\program files\microsoft visual studio\vc98\include\winbase.h(262) : error C2501: 'LPVOID' : missing storage-class or type specifiersc:\program files\microsoft visual studio\vc98\include\winbase.h(262) : fatal error C1004: unexpected end of file foundError executing cl.exe.Cpp1.exe – 74 error(s), 0 warning(s)
『捌』 系统负载过高
网站的问题,也就是你要登陆的网站的服务器问题,和你的网速有一定关系(你的网速快,相对而言对方给你的服务越及时,你的访问优先级越高)不过这一切都是天生的,因为你选择服务商的时候(上网的方式,例如,小区宽带,adsl,拨号)已经决定了显示“连接数据库失败! 由于系统负载过高,请稍后再刷新。”分两种情况,一.你是他们的家人(一员),你可以跟老大说,哎,老大,我们的数据服务器,该更新了,伟大的网民,无法联系到我们,欣赏我们美丽的主页.你还可以跟数据库编程人员说,让他们注意代码的优化.二、不是一的情况,你可以通过EAMIL,或者其他他们提供的可联系信息(找不到,可以上网,或者电话(114),黄页找到他们公司的相关联系信息)跟他们沟通(前期,你愿意的情况下)
『玖』 C++中如何表示从2到10
综合自网络
C语言在嵌入式学习中是必备的知识,审核大部分操作都要围绕C语言进行,而其中有三块“难啃的硬骨头”几乎是公认级别的。
指针公认最难理解的概念,也是让很多初学者选择放弃的直接原因。
指针之所以难理解,因为指针本身就是一个变量,是一个非常特殊的变量,专门存放地址的变量,这个地址需要给申请空间才能装东西,而且因为是个变量可以中间赋值,这么一倒腾很多人就开始犯晕了,绕不开弯了。C语言之所以被很多高手所喜欢,就是指针的魅力,中间可以灵活的切换,执行效率超高,这点也是让小白晕菜的地方。
指针是学习绕不过去的知识点,而且学完C语言,下一步紧接着切换到数据结构和算法,指针是切换的重点,指针搞不定下一步进行起来就很难,会让很多人放弃继续学习的勇气。
指针直接对接内存结构,常见的C语言里面的指针乱指,数组越界根本原因就是内存问题。在指针这个点有无穷无尽的发挥空间。很多编程的技巧都在此集结。
指针还涉及如何申请释放内存,如果释放不及时就会出现内存泄露的情况,指针是高效好用,但不彻底搞明白对于有些人来说简直就是噩梦。
在概念方面问题可以参见此前推文《对于C语言指针最详尽的讲解》,那么在指针方面可以参见一下大神的经验:
复杂类型说明
要了解指针,多多少少会出现一些比较复杂的类型。所以先介绍一下如何完全理解一个复杂类型。
要理解复杂类型其实很简单,一个类型里会出现很多运算符,他们也像普通的表达式一样,有优先级,其优先级和运算优先级一样。
所以笔者总结了一下其原则:从变量名处起,根据运算符优先级结合,一步一步分析。
下面让我们先从简单的类型开始慢慢分析吧。
int p;
这是一个普通的整型变量
int p;
首先从P处开始,先与结合,所以说明P是一个指针。然后再与int结合,说明指针所指向的内容的类型为int型,所以P是一个返回整型数据的指针
int p[3];
首先从P处开始,先与[]结合,说明P是一个数组。然后与int结合,说明数组里的元素是整型的,所以P是一个由整型数据组成的数组。
int *p[3];
首先从P处开始,先与[]结合,因为其优先级比高,所以P是一个数组。然后再与结合,说明数组里的元素是指针类型。之后再与int结合,说明指针所指向的内容的类型是整型的,所以P是一个由返回整型数据的指针所组成的数组。
int (*p)[3];
首先从P处开始,先与结合,说明P是一个指针。然后再与[]结合(与"()"这步可以忽略,只是为了改变优先级),说明指针所指向的内容是一个数组。之后再与int结合,说明数组里的元素是整型的。所以P是一个指向由整型数据组成3个整数的指针。
int **p;
首先从P开始,先与结合,说明P是一个指针。然后再与结合,说明指针所指向的元素是指针。之后再与int结合,说明该指针所指向的元素是整型数据。由于二级指针以及更高级的指针极少用在复杂的类型中,所以后面更复杂的类型我们就不考虑多级指针了,最多只考虑一级指针。
int p(int);
从P处起,先与()结合,说明P是一个函数。然后进入()里分析,说明该函数有一个整型变量的参数,之后再与外面的int结合,说明函数的返回值是一个整型数据。
int (*p)(int);
从P处开始,先与指针结合,说明P是一个指针。然后与()结合,说明指针指向的是一个函数。之后再与()里的int结合,说明函数有一个int型的参数,再与最外层的int结合,说明函数的返回类型是整型,所以P是一个指向有一个整型参数且返回类型为整型的函数的指针。
int (p(int))[3];
可以先跳过,不看这个类型,过于复杂。从P开始,先与()结合,说明P是一个函数。然后进入()里面,与int结合,说明函数有一个整型变量参数。然后再与外面的结合,说明函数返回的是一个指针。之后到最外面一层,先与[]结合,说明返回的指针指向的是一个数组。接着再与结合,说明数组里的元素是指针,最后再与int结合,说明指针指向的内容是整型数据。所以P是一个参数为一个整数据且返回一个指向由整型指针变量组成的数组的指针变量的函数。
说到这里也就差不多了。理解了这几个类型,其它的类型对我们来说也是小菜了。不过一般不会用太复杂的类型,那样会大大减小程序的可读性,请慎用。这上面的几种类型已经足够我们用了。
细说指针
指针是一个特殊的变量,它里面存储的数值被解释成为内存里的一个地址。
要搞清一个指针需要搞清指针的四方面的内容:指针的类型、指针所指向的类型、指针的值或者叫指针所指向的内存区、指针本身所占据的内存区。让我们分别说明。
先声明几个指针放着做例子:
(1)int*ptr;
(2)char*ptr;
(3)int**ptr;
(4)int(*ptr)[3];
(5)int*(*ptr)[4];指针的类型
从语法的角度看,小伙伴们只要把指针声明语句里的指针名字去掉,剩下的部分就是这个指针的类型。这是指针本身所具有的类型。
让我们看看上述例子中各个指针的类型:
(1)intptr;//指针的类型是int
(2)charptr;//指针的类型是char
(3)intptr;//指针的类型是int
(4)int(ptr)[3];//指针的类型是int()[3]
(5)int*(ptr)[4];//指针的类型是int(*)[4]
怎么样?找出指针的类型的方法是不是很简单?
指针所指向的类型
当通过指针来访问指针所指向的内存区时,指针所指向的类型决定了编译器将把那片内存区里的内容当做什么来看待。
从语法上看,小伙伴们只需把指针声明语句中的指针名字和名字左边的指针声明符*去掉,剩下的就是指针所指向的类型。
上述例子中各个指针所指向的类型:
(1)intptr; //指针所指向的类型是int
(2)char*ptr; //指针所指向的的类型是char*
(3)int*ptr; //指针所指向的的类型是int*
(4)int(*ptr)[3]; //指针所指向的的类型是int(*)[3]
(5)int*(*ptr)[4]; //指针所指向的的类型是int*(*)[4]
在指针的算术运算中,指针所指向的类型有很大的作用。
指针的类型(即指针本身的类型)和指针所指向的类型是两个概念。当小伙伴们对C 越来越熟悉时,就会发现,把与指针搅和在一起的"类型"这个概念分成"指针的类型"和"指针所指向的类型"两个概念,是精通指针的关键点之一。
笔者看了不少书,发现有些写得差的书中,就把指针的这两个概念搅在一起了,所以看起书来前后矛盾,越看越糊涂。
指针的值
即指针所指向的内存区或地址。
指针的值是指针本身存储的数值,这个值将被编译器当作一个地址,而不是一个一般的数值。
在32位程序里,所有类型的指针的值都是一个32位整数,因为32位程序里内存地址全都是32位长。指针所指向的内存区就是从指针的值所代表的那个内存地址开始,长度为si zeof(指针所指向的类型)的一片内存区。
以后,我们说一个指针的值是XX,就相当于说该指针指向了以XX为首地址的一片内存区域;我们说一个指针指向了某块内存区域,就相当于说该指针的值是这块内存区域的首地址。
指针所指向的内存区和指针所指向的类型是两个完全不同的概念。在例一中,指针所指向的类型已经有了,但由于指针还未初始化,所以它所指向的内存区是不存在的,或者说是无意义的。
以后,每遇到一个指针,都应该问问:这个指针的类型是什么?指针指的类型是什么?该指针指向了哪里?
指针本身所占据的内存区
指针本身占了多大的内存?只要用函数sizeof(指针的类型)测一下就知道了。在32位平台里,指针本身占据4个字节的长度。指针本身占据的内存这个概念在判断一个指针表达式是否是左值时很有用。
0x02 函数
面向过程对象模块的基本单位,以及对应各种组合,函数指针,指针函数
一个函数就是一个业务逻辑块,是面向过程,单元模块的最小单元,而且在函数的执行过程中,形参,实参如何交换数据,如何将数据传递出去,如何设计一个合理的函数,不单单是解决一个功能,还要看是不是能够复用,避免重复造轮子。
函数指针和指针函数,表面是两个字面意思的互换实际上含义截然不同,指针函数比较好理解,就是返回指针的一个函数,函数指针这个主要用在回调函数,很多人觉得函数都没还搞明白,回调函数更晕菜了。其实可以通俗的理解指向函数的指针,本身是一个指针变量,只不过在初始化的时候指向了函数,这又回到了指针层面。没搞明白指针再次深入的向前走特别难。
C语言的开发者们为后来的开发者做了一些省力气的事情,他们编写了大量代码,将常见的基本功能都完成了,可以让别人直接拿来使用。但是那么多代码,如何从中找到自己需要的呢?将所有代码都拿来显然是不太现实。
但是这些代码,早已被早期的开发者们分门别类地放在了不同的文件中,并且每一段代码都有唯一的名字。所以其实学习C语言并没有那么难,尤其是可以在动手锻炼做项目中进行。使用代码时,只要在对应的名字后面加上( )就可以。这样的一段代码就是函数,函数能够独立地完成某个功能,一次编写完成后可以多次使用。
很多初学者可能都会把C语言中的函数和数学中的函数概念搞混淆。其实真相并没有那么复杂,C语言中的函数是有规律可循迹的,只要搞清楚了概念你会发现还挺有意思的。函数的英文名称是 Function,对应翻译过来的中文还有“功能”的意思。C语言中的函数也跟功能有着密切的关系。
我们来看一小段C语言代码:
#includeint main()puts("Hello World");return 0;
把目光放在第4行代码上,这行代码会在显示器上输出“Hello World”。前面我们已经讲过,puts 后面要带(),字符串也要放在()中。
在C语言中,有的语句使用时不能带括号,有的语句必须带括号。带括号的就是函数(Function)。
C语言提供了很多功能,我们只需要一句简单的代码就能够使用。但是这些功能的底层都比较复杂,通常是软件和硬件的结合,还要要考虑很多细节和边界,如果将这些功能都交给程序员去完成,那将极大增加程序员的学习成本,降低编程效率。
有了函数之后,C语言的编程效率就好像有了神器一样,开发者们只需要随时调用就可以了,像进程函数、操作函数、时间日期函数等都可以帮助我们直接实现C语言本身的功能。
C语言函数是可以重复使用的。
函数的一个明显特征就是使用时必须带括号(),必要的话,括号中还可以包含待处理的数据。例如puts("果果小师弟")就使用了一段具有输出功能的代码,这段代码的名字是 puts,"尚观科技" 是要交给这段代码处理的数据。使用函数在编程中有专业的称呼,叫做函数调用(Function Call)。
如果函数需要处理多个数据,那么它们之间使用逗号,分隔,例如:
pow(10, 2);
该函数用来求10的2次方。
好了,看到这里你有没有觉得其实C语言函数还是比较有意思的,而且并没有那么复杂困难。以后再遇到菜鸟小白的时候,你一口一个C语言的函数,说不定就能当场引来无数膜拜的目光。
0x03 结构体、递归
很多在大学学习C语言的,很多课程都没学完,结构体都没学到,因为从章节的安排来看好像,结构体学习放在教材的后半部分了,弄得很多学生觉得结构体不重要,如果只是应付学校的考试,或者就是为了混个毕业证,的确学的意义不大。
如果想从事编程这个行业,对这个概念还不了解,基本上无法构造数据模型,没有一个业务体是完全使用原生数据类型来完成的,很多高手在设计数据模型的时候,一般先把头文件中的结构体数据整理出来。然后设计好功能函数的参数,以及名字,然后才真正开始写c源码。
如果从节省空间考虑结构体里面的数据放的顺序不一样在内存中占用的空间也不一样,结构体与结构体之间赋值,结构体存在指针那么赋值要特别注意,需要进行深度的赋值。
递归一般用于从头到位统计或者罗列一些数据,在使用的时候很多初学者都觉得别扭,怎么还能自己调用自己?而且在使用的时候,一定设置好跳出的条件,不然无休止的进行下去,真就成无线死循环了。
对于结构体方面的知识,可以参见此前推送的文章《C语言结构体(struct)最全的讲解(万字干货)》。具体也可以参见大佬的经验:
相信大家对于结构体都不陌生。在此,分享出本人对C语言结构体的研究和学习的总结。如果你发现这个总结中有你以前所未掌握的,那本文也算是有点价值了。当然,水平有限,若发现不足之处恳请指出。代码文件test.c我放在下面。在此,我会围绕以下2个问题来分析和应用C语言结构体:
C语言中的结构体有何作用
结构体成员变量内存对齐有何讲究(重点)
对于一些概念的说明,我就不把C语言教材上的定义搬上来。我们坐下来慢慢聊吧。
1. 结构体有何作用
三个月前,教研室里一个学长在华为南京研究院的面试中就遇到这个问题。当然,这只是面试中最基础的问题。如果问你你怎么回答?我的理解是这样的,C语言中结构体至少有以下三个作用:
(1) 有机地组织了对象的属性。
比如,在STM32的RTC开发中,我们需要数据来表示日期和时间,这些数据通常是年、月、日、时、分、秒。如果我们不用结构体,那么就需要定义6个变量来表示。这样的话程序的数据结构是松散的,我们的数据结构最好是“高内聚,低耦合”的。所以,用一个结构体来表示更好,无论是从程序的可读性还是可移植性还是可维护性皆是:
typedef struct //公历日期和时间结构体vu16 year;vu8 month;vu8 date;vu8 hour;vu8 min;vu8 sec;}_calendar_obj;_calendar_obj calendar; //定义结构体变量
(2) 以修改结构体成员变量的方法代替了函数(入口参数)的重新定义。
如果说结构体有机地组织了对象的属性表示结构体“中看”,那么以修改结构体成员变量的方法代替函数(入口参数)的重新定义就表示了结构体“中用”。继续以上面的结构体为例子,我们来分析。假如现在我有如下函数来显示日期和时间:
void DsipDateTime( _calendar_obj DateTimeVal)
那么我们只要将一个_calendar_obj这个结构体类型的变量作为实参调用DsipDateTime()即可,DsipDateTime()通过DateTimeVal的成变量来实现内容的显示。如果不用结构体,我们很可能需要写这样的一个函数:
void DsipDateTime( vu16 year,vu8 month,vu8 date,vu8 hour,vu8 min,vu8 sec)
显然这样的形参很不可观,数据结构管理起来也很繁琐。如果某个函数的返回值得是一个表示日期和时间的数据,那就更复杂了。这只是一方面。
另一方面,如果用户需要表示日期和时间的数据中还要包含星期(周),这个时候,如果之前没有用机构体,那么应该在DsipDateTime()函数中在增加一个形参vu8 week:
void DsipDateTime( vu16 year,vu8 month,vu8 date,vu8 week,vu8 hour,vu8 min,vu8 sec)
可见这种方法来传递参数非常繁琐。所以以结构体作为函数的入口参数的好处之一就是函数的声明void DsipDateTime(_calendar_obj DateTimeVal)不需要改变,只需要增加结构体的成员变量,然后在函数的内部实现上对calendar.week作相应的处理即可。这样,在程序的修改、维护方面作用显著。
typedef struct //公历日期和时间结构体vu16 year;vu8 month;vu8 date;vu8 week;vu8 hour;vu8 min;vu8 sec;}_calendar_obj;_calendar_obj calendar; //定义结构体变量
(3) 结构体的内存对齐原则可以提高CPU对内存的访问速度(以空间换取时间)。
并且,结构体成员变量的地址可以根据基地址(以偏移量offset)计算。我们先来看看下面的一段简单的程序,对于此程序的分析会在第2部分结构体成员变量内存对齐中详细说明。
#include
int main(){struct //声明结构体char_short_long{char c;short s;long l;}char_short_long;
struct //声明结构体long_short_char{long l;short s;char c;}long_short_char;
struct //声明结构体char_long_short{char c;long l;short s;}char_long_short;
printf("
");printf(" Size of char = %d bytes
",sizeof(char));printf(" Size of shrot = %d bytes
",sizeof(short));printf(" Size of long = %d bytes
",sizeof(long));printf("
"); //char_short_longprintf(" Size of char_short_long = %d bytes
",sizeof(char_short_long));printf(" Addr of char_short_long.c = 0x%p (10进制:%d)
",&char_short_long.c,&char_short_long.c);printf(" Addr of char_short_long.s = 0x%p (10进制:%d)
",&char_short_long.s,&char_short_long.s);printf(" Addr of char_short_long.l = 0x%p (10进制:%d)
",&char_short_long.l,&char_short_long.l);printf("
");
printf("
"); //long_short_charprintf(" Size of long_short_char = %d bytes
",sizeof(long_short_char));printf(" Addr of long_short_char.l = 0x%p (10进制:%d)
",&long_short_char.l,&long_short_char.l);printf(" Addr of long_short_char.s = 0x%p (10进制:%d)
",&long_short_char.s,&long_short_char.s);printf(" Addr of long_short_char.c = 0x%p (10进制:%d)
",&long_short_char.c,&long_short_char.c);printf("
");
printf("
"); //char_long_shortprintf(" Size of char_long_short = %d bytes
",sizeof(char_long_short));printf(" Addr of char_long_short.c = 0x%p (10进制:%d)
",&char_long_short.c,&char_long_short.c);printf(" Addr of char_long_short.l = 0x%p (10进制:%d)
",&char_long_short.l,&char_long_short.l);printf(" Addr of char_long_short.s = 0x%p (10进制:%d)
",&char_long_short.s,&char_long_short.s);printf("
");return 0;}
程序的运行结果如下(注意:括号内的数据是成员变量的地址的十进制形式):
首先,我们来分析一下上面程序的运行结果。前三行说明在我的程序中,char型占1个字节,short型占2个字节,long型占4个字节。char_short_long、long_short_char和char_long_short是三个结构体成员相同但是成员变量的排列顺序不同。并且从程序的运行结果来看,
Size of char_short_long = 8 bytesSize of long_short_char = 8 bytesSize of char_long_short = 12 bytes //比前两种情况大4 byte !
并且,还要注意到,1 byte (char)+ 2 byte (short)+ 4 byte (long) = 7 byte,而不是8 byte。
所以,结构体成员变量的放置顺序影响着结构体所占的内存空间的大小。一个结构体变量所占内存的大小不一定等于其成员变量所占空间之和。如果一个用户程序或者操作系统(比如uC/OS-II)中存在大量结构体变量时,这种内存占用必须要进行优化,也就是说,结构体内部成员变量的排列次序是有讲究的。
结构体成员变量到底是如何存放的呢?
在这里,我就不卖关子了,直接给出如下结论,在没有#pragma pack宏的情况下:
原则1 结构(struct或联合union)的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储)。
原则2 结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补齐。
原则3 结构体作为成员时,结构体成员要从其内部最大元素大小的整数倍地址开始存储。(struct a里存有struct b,b里有char,int,double等元素时,那么b应该从8的整数倍地址处开始存储,因为sizeof(double) = 8 bytes)
这里,我们结合上面的程序来分析(暂时不讨论原则3)。
先看看char_short_long和long_short_char这两个结构体,从它们的成员变量的地址可以看出来,这两个结构体符合原则1和原则2。注意,在 char_short_long的成员变量的地址中,char_short_long.s的地址是1244994,也就是说,1244993是“空的”,只是被“占位”了!
成员变量 成员变量十六进制地址 成员变量十进制地址 char_long_short.c 0x0012FF2C 1244972 char_long_short.l 0x0012FF30 1244976 char_long_short.s 0x0012FF34 1244980
可见,其内存分布图如下,共12 bytes:
首先,1244972能被1整除,所以char_long_short.c放在1244972处没有问题(其实,就char型成员变量自身来说,其放在任何地址单元处都没有问题),根据原则1,在之后的1244973~1244975中都没有能被4(因为sizeof(long)=4bytes)整除的,1244976能被4整除,所以char_long_short.l应该放在1244976处,那么同理,最后一个.s(sizeof(short)=2 bytes)是应该放在1244980处。
是不是这样就结束了?不是,还有原则2。根据原则2的要求,char_long_short这个结构体所占的空间大小应该是其占内存空间最大的成员变量的大小的整数倍。如果我们到此就结束了,那么char_long_short所占的内存空间是1244972~1244981共计10bytes,不符合原则2,所以,必须在最后补齐2个 bytes(1244982~1244983)。
至此,一个结构体的内存布局完成了。
下面我们按照上述原则,来验证这样的分析是不是正确。按上面的分析,地址单元1244973、1244974、1244975以及1244982、1244983都是空的(至少char_long_short未用到,只是“占位”了)。如果我们的分析是正确的,那么,定义这样一个结构体,其所占内存也应该是12 bytes:
struct //声明结构体char_long_short_newchar c;char add1; //补齐空间char add2; //补齐空间char add3; //补齐空间long l;short s;char add4; //补齐空间char add5; //补齐空间}char_long_short_new;
可见,我们的分析是正确的。至于原则3,大家可以自己编程验证,这里就不再讨论了。
所以,无论你是在VC6.0还是Keil C51,还是Keil MDK中,当你需要定义一个结构体时,只要你稍微留心结构体成员变量内存对齐这一现象,就可以在很大程度上节约MCU的RAM。这一点不仅仅应用于实际编程,在很多大型公司,比如IBM、微软、网络、华为的笔试和面试中,也是常见的。
这三大块硬骨头是学习C语言的绊脚石,下功夫拿掉基本上C语言的大动脉就打通了,那么再去学习别的内容就相对比较简单了。编程学习过程中越是痛苦的时候,学到的东西就会越多,克服过去就会自己的技能,放弃了前面的付出的时间都将清零。越是难学的语言在入门之后,在入门之后越觉得过瘾,而且还容易上瘾。你上瘾了没?还是放弃了?
— EOF —
『拾』 VC++ mfc 高手来看 VC++中自带的日期控件有些大 想弄成弹出式的 该怎么办呀
以下办法请参考,看看是否可行:
创建一个工程项目,名为:XXX
1、添加一个对话框CDlgMyDate,添加一个CDateTimeCtrl和一个OK按钮
在cpp文件写入:#include"PictureDlg.h"
voidCDlgMyDate::OnOK()
{
//TODO:Addextravalidationhere
CStringstr;
this->m_dateTime.GetWindowText(str);
CPictureDlg*parent=(CPictureDlg*)GetParent();
parent->m_myEdit.SetWindowText(str);
CDialog::OnOK();
}
2、添加一个CEdit派生类CMyEdit,为其添加一个双击的消息响应函数OnLButtonDblClk,意思是双击时弹出CDlgMyDate对话框。
voidCMyEdit::OnLButtonDblClk(UINTnFlags,CPointpoint)
{
//TODO:/orcalldefault
CDlgMyDatedlg;
dlg.DoModal();//根据需要,可修改对话框显示的位置
CEdit::OnLButtonDblClk(nFlags,point);
}
3、在工程对话框CXXXDlg头文件写入#include"MyEdit.h"
在对话框拖一个CEdit控件,将其修改为派生于CMyEdit。
运行结果如图: