Qisong's profileMephisPhotosBlogLists Tools Help

Blog


    C/C++ - C语言中操作字符串的一些函数源代码

    『 ۞рēn-Ρlāм ™ 』 C/C++ - C语言中操作字符串的一些函数源代码
    C/C++ - C语言中操作字符串的一些函数源代码程 
     
    C/C++ - C语言中操作字符串的一些函数源代码
     
    1. strlen(),计算字符串长度
    int strlen(const char string)
    {
    int i=0;
    while(string[i]) i++;
    return i;
    }
    2. strcpy(), 字符串拷贝.
    char *strcpy(char *destination, const char *source)
    {
    while(*destinaton++=*source++);
    return (destination-1);
    }
    3. strcat(), 字符串的连接.
    char *strcat(char *target,const char *source)
    {
    char *original=target;
    while(*target) target++;     // Find the end of the string
    while(*target++=*source++);
    return(original);
    }
    4. streql(), 判断两个字符串是否相等.
    int streql(char *str1,char *str2)
    {
    while((*str1==*str2)&&(*str1))
    {
    str1++;
    str2++;
    }
    return((*str1==NULL)&&(*str2==NULL));
    }
    5. strchr(), 在字符串中查找某个字符.
    char *strchr(const char *string,int letter)
    {
    while((*string!=letter)&(*string))
    string++;
    return (string);
    }
    6. chrcnt(), 计算某个字符在字符串中出现的次数.
    int chrcnt(const char *string,int letter)
    {
    int count=0;
    while(*string)
    if(*string==letter)count++;
    return count;
    }
    7. strcmp(), 判断两个字符串是否相等.
    int strcmp(const char *str1,const char *str2)
    {
    while((*str1==*str2)&&(*str1))
    {
    str1++;
    str2++;
    }
    if((*str1==*str2)&&(!*str1)) //Same strings
    return o;
    else if((*str1)&&(!*str2))  //Same but str1 longer
    return -1;
    else if((*str2)&&(!*str1)) //Same but str2 longer
    else
    return((*str1>*str2)?-1:1);
    }

     
     
    『 ۞рēn-Ρlāм ™ 』
    『 Open-Palm ™ 』
     
    『 编辑日志 』

    C/C++ - 关于 C/C++ 语言图形编程

    『 ۞рēn-Ρlāм ™ 』 C/C++ - 关于 C/C++ 语言图形编程
    C/C++ - 关于 C/C++ 语言图形编程 
     
        C/C++ 语言标准中没有图形函数。图形编程是面向系统的,需要学习 api(应用程序接口application programming interface)。例如tc中的graphics.h头文件中的图形函数是面向 dos 的。我觉得没必要学 graphics.h 里的函数,毕竟 dos已经过时了。现在是 windows的时代。如果想学图形编程,可以学windows api,这是面向 windows 的。因为这些是面向系统的编程,所以不能移植到别的系统。例如,利用 win api 或者 graphics.h 编写的程序不能移植到 linux 或者 mac os 等。
        我觉得初学者没有必要太过于深入某种面向系统的编程,先好好掌握标准 C/C++,通晓 C/C++ 语言的标准函数库才是最重要的。我们一定要分清楚哪些是标准函数库里的函数,而哪些是特定的开发工具(例如 tc 或者 vc)提供的函数。因为标准规定的东西是可以移植到任何操作系统的,除非那个操作系统没有C/C++ 编译器。如果以后我们的工作是开发 unix/linux 应用程序的话,win api 和 graphics.h 毫无用处。当然,学习 win api 还是很有必要的。毕竟我们以后开发 windows 程序可能性很大。但是,graphics.h 我觉得没必要学。
        顺便说一句,我们经常使用的 getch 函数,其实不是标准函数库里的函数。虽然我们常用的编译器都有这个函数,但是并非所有的编译器都要提供这个函数,因为它不是标准函数库里的函数
     
     
    『 ۞рēn-Ρlāм ™ 』
    『 Open-Palm ™ 』
     
    『 编辑日志 』

    C/C++ - C/C++的常见误区

    『 ۞рēn-Ρlāм ™ 』 C/C++ - C++的常见误区
    C/C++的常见误区
     
    1. C++虽然主要是以C的基础发展起来的一门新语言,但她不是C的替代品,不是C的升级,C++和C是兄弟关系。没有谁比谁先进的说法,更重要的一点是C和C++各自的标准委员会是独立的,最新的C++标准是C++98,最新的C标准是C99。因此也没有先学C再说C++的说法,也不再(注意这个"不再")有C++语法是C语法的超集的说法。
     
    2. C++/CLI 和 C# 是微软的,它们与C和C++没有任何关系,虽然部分语法相似。但哪两种语言不相似呢?都是abc这26个字母。
     
    3. 不要使用TC/TC++/BC/CB等古老的编译器来学习C/C++,因为它们太古老了,不支持新的C/C++标准。不要使用CBX/VC++6.0/VC2005等对C/C++标准支持不好的编译器,虽然这些编译器适合工作,但不适合学习,因为它们中的语法陷阱很多。记住唯一适合学习的编译器是gcc/mingw。[antigloss注:Dev-C++ 使用的编译器就是gcc & g++]
     
    4. 不要用""代替<>来包含系统头文件,虽然有些编译器允许你这样做,但它不符合C/C++标准。
    错误的示例:#include "stdio.h",#include "iostream"。[antigloss注:<> 用于包含标准头文件和系统头文件,"" 用于包含自定义头文件。标准似乎没有明确规定不准用 "" 包含标准头文件和系统头文件。使用 "" 包含标准头文件或者系统头文件只能说是一种不良风格。]
     
    5. 不要将main函数的返回类型定义为void,虽然有些编译器允许你这样做,但它不符合C/C++标准。不要将函数的int返回类型省略不写,在C++中要求编译器至少给一个警告。错误的示例:void main() {},main() {} [antigloss注:C99和C++98都要求编译器对省略int至少发出一个警告]
     
    6. 不要把VC++中的 #include "stdafx.h" 贴出来,它是预编译头文件。如同上菜时不要把厨师也放到托盘中。
     
    7. [C++]不要#include <iostream.h>,不要#include <string.h>,因为它们已经被C++标准明确的废弃了,请改为 #include <iostream> 和 #include <cstring>。规则就是:
        a. 如果这个头文件是旧C++特有的,那么去掉.h后缀,并放入std名字空间,
            比如 iostream.h 变为 iostream。
        b. 如果这个头文件是C也有的,那么去掉.h后缀,增加一个c前缀,比如 string.h
            变为 cstring;stdio.h 变为 cstdio, 等等。
    BTW:不要把string、cstring、string.h三个头文件搞混淆
    BTW:windows.h不是C/C++的标准文件,因此它的命名C/C++不管。
     
    8. 不要再写 char* p = "XXX" 这种语句,要写成 const char* p = "XXX",编译器之所以让前者通过编译是为了兼容以前的大量的旧代码。
    BTW:const TYPE* p 和 TYPE const* p 是一样的,风格不同而已。
    BTW:C语言中也有const关键字。
     
    9. 不要在同一条语句中包含一个变量的多个++/--,因为它们的解析在C/C++标准中没有规定,完全取决于编译器的个人行为。
     
    10. C/C++ 是平台无关性语言,因此系统相关的 process/GUI 等不在标准 C/C++ 库中。比如 graphics.h 和 windows.h 等是由某个编译器提供的,而不是由C/C++ 提供的。
     
    11. C/C++只是语言,而且是平台无关性语言。论坛上有部分人甚至认为C就是dos,C++就是windows,那么请问linux是什么?
     
    12.[C++]面向对象曾经是设计C with class(C++的前身)的主要目的,但C++不是,C++是一个多典范语言。主要支持过程调用、基于对象、面向对象、泛式编程这四种编程典范。当然还支持functional, generative,metaprogramming等典范。
     
    13. 语法学家不是文学家,所以当你学会了一门计算机语言时,你还需要学习数据机构和算法,还需要掌握工具和平台API的用法。
     
    14. C/C++ 是通用语言,因此语法很复杂,你应当裁减成适合你自己的语法集合,比如裁减成 better C 和 ADT。
     
    15. C/C++是通用语言,因此只含通用的库,你应该丰富自己需要的库,比如汽车工业协会有自己的C/C++函数/类/模板库。
     
    『 ۞рēn-Ρlāм ™ 』
    『 Open-Palm ™ 』
     
    『 编辑日志 』

    C/C++ - C++的特点

    『 ۞рēn-Ρlāм ™ 』 C/C++ - C++的特点
    C++的特点
    C++语言是在C语言的基础是扩展而成的.所以两种语言的基本语法和语义是相同。C++中加入了面向对程序设计(OOP)的特征。
    下面的三个主要性质刻划OOP语言的特点:
    封装性:把一个数据结构同操作的函数(行为或方法)组合在一起。封装性是借助于一种新的结构和数据类型机制——类实现的。
     
    继承性:建立一个新的派生类,它从一个或多个先前定义的基类中继承函数和数据,而且可能重新定义或加进新的数据行为,这样就建立了类的层次。
     
    多态性:给行为取一个名字或符号,它共享一个类的层次,在这个层次中的每个类都以适合自己的方式实现这个行为。
     
    什么是API
    API就是Windows应用程序设计接口的意思。API是一个程序内(或一组相关程序内)的一组函数调用,程序员用它创建其他程序。不必知道函内部,只要知道函数原型及返回值。将一组函数转入API的问题实质是此函数提供每个人可使用的技术规范资料。Windows API大概是今天世界上最著名的API了。现在API以发展到了Win32 API。在它的核心中,依靠三个主要组件提供Windows的大部分函数。这三个组件分别是USER32.DLL,GDI32.DLL,KERNEL32.DLL。
     
    什么是MFC
    MFC(Microsoft基本类)库封装SDK(软件开发工具包)结构、功能及应用程序框架内部技术,该应用程序框架隐藏过去Windows程序员不得不处理的许多重复性工作。
     
    ActiveX,OLE
    ActiveX和OLE已成了同义词。人们以前所说的OLE控件(OCXs)现在已被称作ActiveX控件,OLE DocObjects现在称为ActiveX文档。在一些情形下,有关如何实现OLE技术的文档已被全部更新为ActiveX技术,并且仅仅是更换了OLE一词,它目前被称为ActiveX。
     
    ActiveX组件包括如下几类:
    1  自动化服务器:可以由其他应用程序编程驱动的组件。自动化服务器至少包括一个,也许是多个供其他应用程序生成和连接的基于IDispatch的接口。自动化服务器可以含有也可以没有用户界面(UI),这取决于服务器的特性和功能。
    2  自动化控制器:那些使用和操纵自动化服务器的应用程序。
    3  控件:ActiveX控件等价于以前的OLE控件或OCX。一个典型的控件包括设计时和运行时的用户界面,唯一的IDispatch接口定义控件的方法和属性,唯一的IConnectionPoint接口用于控件可引发的事件。
    4  文档:ActiveX文档,即以前所说的DocObect,表示一种不仅仅是简单控件或自动化服务器的对象。ActiveX文档在结构上是对OLE链接和模型的扩展,并对其所在的容器具有更多控制权。一个最显著的变化是菜单的显示方式。一个典型的OLE文档的菜单会与容器菜单合并成一个新的集合,而ActiveX文档将替换整个菜单系统,只表现出文档的特性而不是文档与容器共同的特性。
    5  容器:ActiveX容器是一个可以作为自动化服务器、控件和文档宿主的应用程序。

     

    『 ۞рēn-Ρlāм ™ 』
    『 Open-Palm ™ 』
     
    『 编辑日志 』

    C/C++ - VC Studio 使用技巧大全

    『 ۞рēn-Ρlāм ™ 』 C/C++ - VC Studio 使用技巧大全
    VC Studio 使用技巧大全
     
    1.检测程序中的括号是否匹配
      把光标移动到需要检测的括号(如大括号{}、方括号[]、圆括号()和尖括号<>)前面,键入快捷键“Ctrl+]”。如果括号匹配正确,光标就跳到匹配的括号处,否则光标不移动,并且机箱喇叭还会发出一声警告声。
     
    2.查看一个宏(或变量、函数)的宏定义
      把光标移动到你想知道的一个宏上,就比如说最常见的DECLARE_MAP_MESSAGE上按一下F12(或右键菜单中的Go To Defition Of …),如果没有建立Browse files,会出现提示对话框,确定,然后就会跳到定义那些东西的地方。
     
    3.格式化一段乱七八糟的源代码
      选中那段源代码,按ATL+F8。
     
    4.在编辑状态下发现成员变量或函数不能显示
      删除该项目扩展名为.ncb文件,重新打开该项目。
     
    5.如何整理ClassView视图中大量的类
      可以在classview 视图中右键新建文件夹(new folder),再把具有相近性质的类拖到对应的文件夹中,使整个视图看上去清晰明了
    .
     
    6.定位预处理指定
    在源文件中定位光标到对称的#if, #endif,使用Ctrl+K.
     
    7.如何添加系统中Lib到当前项目
      在Project | Settings | Link | Object/library modules:输入Lib名称,不同的Lib之间用空格格开.
     
    8.如何添加系统中的头文件(.h)到当前项目.
      #include <FileName.h>,告诉编译到VC系统目录去找;使用#include "FileName.h",告诉编译在当前目录找.
     
    9.如何在Studio使用汇编调试
      在WorkBench的Debugger状态下按CTRL+F7.
     
    10.怎样处理ClassZiard找不到的系统消息
      如果要在ClassWizard中处理WM_NCHITTEST等系统消息,请在ClassWizard中Class Info页中将Message filter改为Window就有了.
     
    11.如何干净的删除一个类
      先从Workspace中的FileView中删除对应的.h和.cpp文件,再关闭项目,从实际的文件夹中删除对应的.h和.cpp文件与.clw文件。
     
    12.如果让控制台应用程序支持mfc类库
      可以在控制台应用程序中include 来引入mfc库,但是控制台应用程序缺省是单线程的,mfc是多线程的,为解决该矛盾,在project setting->c/c++ 选项,选择code generation,在use run-time library 下拉框中选择debug multithread。
     
    13.如何汉化只有可执行代码的.exe 文件
      在nt 下利用vc open file 以resources方式打开*.exe 文件,直接修改资源文件,然后保存即可。
     
    附:VC项目文件说明
    .opt 工程关于开发环境的参数文件。如工具条位置等信息;
    .aps (AppStudio File),资源辅助文件,二进制格式,一般不用去管他.
    .clw ClassWizard信息文件,实际上是INI文件的格式,有兴趣可以研究一下.有时候ClassWizard出问题,手工修改CLW文件可以解决.如果此文件不存在的话,每次用ClassWizard的时候绘提示你是否重建.
    .dsp (DeveloperStudio Project):项目文件,文本格式,不过不熟悉的话不要手工修改.DSW(DeveloperStudio Workspace)是工作区文件,其他特点和DSP差不多.
    .plg 是编译信息文件,编译时的error和warning信息文件(实际上是一个html文件),一般用处不大.在Tools->Options里面有个选项可以控制这个文件的生成.
    .hpj (Help Project)是生成帮助文件的工程,用microsfot  Help Compiler可以处理.
    .mdp (Microsoft DevStudio Project)是旧版本的项目文件,如果要打开此文件的话,会提示你是否转换成新的DSP格式.
    .bsc 是用于浏览项目信息的,如果用Source Brower的话就必须有这个文件.如果不用这个功能的话,可以在Project Options里面去掉Generate Browse Info File,可以加快编译速度.
    .map 是执行文件的映像信息纪录文件,除非对系统底层非常熟悉,这个文件一般用不着.
    .pch (Pre-Compiled File)是预编译文件,可以加快编译速度,但是文件非常大.
    .pdb (Program Database)记录了程序有关的一些数据和调试信息,在调试的时候可能有用.
    .exp 只有在编译DLL的时候才会生成,记录了DLL文件中的一些信息.一般也没什么用.
    .ncb 无编译浏览文件(no compile browser)。当自动完成功能出问题时可以删除此文件。build后会自动生成。
     
    『 ۞рēn-Ρlāм ™ 』
    『 Open-Palm ™ 』
     
    『 编辑日志 』

    C/C++ - AfxBeginThread函数初探

    『 ۞рēn-Ρlāм ™ 』 C/C++ - AfxBeginThread函数初探

    进行多线程程序设计的时候,我们经常用到AfxBeginThread函数来启动一条线程
    该函数使用起来非常的简单方便,其定义如下
    CWinThread* AfxBeginThread(
       AFX_THREADPROC pfnThreadProc,//线程函数地址
       LPVOID pParam,//线程参数
       int nPriority = THREAD_PRIORITY_NORMAL,//线程优先级
       UINT nStackSize = 0,//线程堆栈大小,默认为1M
       DWORD dwCreateFlags = 0,//
       LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL
    );

    CWinThread* AfxBeginThread(
       CRuntimeClass* pThreadClass,
       int nPriority = THREAD_PRIORITY_NORMAL,
       UINT nStackSize = 0,
       DWORD dwCreateFlags = 0,
       LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL
    );

    参数说明:
    pfnThreadProc:线程函数的地址,该参数不能设置为NULL,线程函数必须定义成全局函数或者类的静态成员函数
    例如:
    UINT myThreadFunc(LPVOID lparam)
    或者
    class A
    {
    public:
            static UINT __stdcall myThreadFunc(LPVOID lparam);
    }
    之所以要定义成类的静态成员函数,是因为类的静态成员函数不属于某个类对象,这样在调用函数
    的时候就不用传递一个额外的this指针.

    pThreadClass:指向从CWinThread派生的子类对象的RUNTIME_CLASS

    pParam:要传递给线程函数的参数

    nPriority:要启动的线程的优先级,默认优先级为THREAD_PRIORITY_NORMAL(普通优先级),关于线程
     优先级的详细说明请参考Platform SDK SetThreadPriority函数说明

    nStackSize:新线程的堆栈大小,如果设置为0,则使用默认大小,在应用程序中一般情况下线程的默认堆栈大小
     为1M

    dwCreateFlags:线程创建标志,该参数可以指定为下列标志
     CREATE_SUSPENDED:以挂起方式启动线程,如果你在线程启动之前想初始化一些CWinThread类中的一些成员变量
     比如:m_bAutoDelete或者你的派生类中的成员变量,当初始化完成之后,你可以使用CWinThread类的ResumeThread
     成员函数来恢复线程的运行
     如果把该标志设置为0,则表示立即启动线程
    lpSecurityAttrs:指向安全描述符的指针,如果使用默认的安全级别只要讲该参数设置为NULL就可以了!

    上面就是AfxBeginThread函数的简单说明,我们在使用的时候一般情况下只要指定前两个参数,其他
    参数使用默认值就可以.嗯,的确,使用起来是很简单,只要这个函数一被调用,就创建了一个线程.
    但是大家有没有想过,AfxBeginThread函数究竟是如何启动的线程呢?它的内部是如何实现的呢?

    下面我们就来看一下AfxBeginThread函数的内部实现

    //启动worker线程
    CWinThread* AFXAPI AfxBeginThread(AFX_THREADPROC pfnThreadProc, LPVOID pParam,
     int nPriority, UINT nStackSize, DWORD dwCreateFlags,
     LPSECURITY_ATTRIBUTES lpSecurityAttrs)
    {
    #ifndef _MT
             pfnThreadProc;
             pParam;
             nPriority;
             nStackSize;
             dwCreateFlags;
             lpSecurityAttrs;

             return NULL;
    #else
             ASSERT(pfnThreadProc != NULL);

             CWinThread* pThread = DEBUG_NEW CWinThread(pfnThreadProc, pParam);
             ASSERT_VALID(pThread);

             if (!pThread->CreateThread(dwCreateFlags|CREATE_SUSPENDED, nStackSize,
                      lpSecurityAttrs))
             {
                      pThread->Delete();
                      return NULL;
             }
             VERIFY(pThread->SetThreadPriority(nPriority));
             if (!(dwCreateFlags & CREATE_SUSPENDED))
                      VERIFY(pThread->ResumeThread() != (DWORD)-1);

             return pThread;
    #endif //!_MT)
    }

    //启动UI线程
    CWinThread* AFXAPI AfxBeginThread(CRuntimeClass* pThreadClass,
     int nPriority, UINT nStackSize, DWORD dwCreateFlags,
     LPSECURITY_ATTRIBUTES lpSecurityAttrs)
    {
    #ifndef _MT
            pThreadClass;
            nPriority;
            nStackSize;
            dwCreateFlags;
            lpSecurityAttrs;

            return NULL;
    #else
            ASSERT(pThreadClass != NULL);
            ASSERT(pThreadClass->IsDerivedFrom(RUNTIME_CLASS(CWinThread)));

            CWinThread* pThread = (CWinThread*)pThreadClass->CreateObject();
            if (pThread == NULL)
                    AfxThrowMemoryException();
            ASSERT_VALID(pThread);

            pThread->m_pThreadParams = NULL;
            if (!pThread->CreateThread(dwCreateFlags|CREATE_SUSPENDED, nStackSize,
                    lpSecurityAttrs))
            {
                    pThread->Delete();
                    return NULL;
            }
            VERIFY(pThread->SetThreadPriority(nPriority));
            if (!(dwCreateFlags & CREATE_SUSPENDED))
                    VERIFY(pThread->ResumeThread() != (DWORD)-1);

            return pThread;
    #endif //!_MT
    }

    从上面的代码中可以看出AfxBeginThread所做的事情主要有以下几点:

    1.在heap中配置一个新的CWinThread对象(worker线程)
    代码如:CWinThread* pThread = DEBUG_NEW CWinThread(pfnThreadProc, pParam);
    调用CRuntimeClass结构中的CreateObject函数创建CWinThread对象
    CWinThread* pThread = (CWinThread*)pThreadClass->CreateObject();
    CRuntimeClass以及MFC相关类的内部实现,详情请参考
    《深入浅出MFC》侯捷著

    2.调用CWinThread::CreateThread()并设定属性,使线程以挂起状态产生
    pThread->CreateThread(dwCreateFlags|CREATE_SUSPENDED, nStackSize,lpSecurityAttrs);

    3.设定线程的优先权
    pThread->SetThreadPriority(nPriority);

    4.调用CWinThread::ResumeThread
    pThread->ResumeThread();

    『 ۞рēn-Ρlāм ™ 』
    『 Open-Palm ™ 』
     
    『 编辑日志 』

    C/C++ - C/C++ 头文件一览

    『 ۞рēn-Ρlāм ™ 』 C/C++ - C/C++头文件一览

    C、传统 C++

    #include <assert.h> //设定插入点
    #include <ctype.h> //字符处理
    #include <errno.h> //定义错误码
    #include <float.h>  //浮点数处理
    #include <fstream.h> //文件输入/输出
    #include <iomanip.h> //参数化输入/输出
    #include <iostream.h> //数据流输入/输出
    #include <limits.h> //定义各种数据类型最值常量
    #include <locale.h> //定义本地化函数
    #include <math.h>  //定义数学函数
    #include <stdio.h>  //定义输入/输出函数
    #include <stdlib.h> //定义杂项函数及内存分配函数
    #include <string.h> //字符串处理
    #include <strstrea.h> //基于数组的输入/输出
    #include <time.h>  //定义关于时间的函数
    #include <wchar.h> //宽字符处理及输入/输出
    #include <wctype.h> //宽字符分类

    //////////////////////////////////////////////////////////////////////////

    标准 C++ (同上的不再注释)

    #include <algorithm> //STL 通用算法
    #include <bitset>  //STL 位集容器
    #include <cctype>
    #include <cerrno>
    #include <clocale>
    #include <cmath>
    #include <complex> //复数类
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <ctime>
    #include <deque>  //STL 双端队列容器
    #include <exception> //异常处理类
    #include <fstream>
    #include <functional> //STL 定义运算函数(代替运算符)
    #include <limits>
    #include <list>  //STL 线性列表容器
    #include <map>  //STL 映射容器
    #include <iomanip>
    #include <ios>  //基本输入/输出支持
    #include <iosfwd>  //输入/输出系统使用的前置声明
    #include <iostream>
    #include <istream> //基本输入流
    #include <ostream> //基本输出流
    #include <queue>  //STL 队列容器
    #include <set>  //STL 集合容器
    #include <sstream> //基于字符串的流
    #include <stack>  //STL 堆栈容器    
    #include <stdexcept> //标准异常类
    #include <streambuf> //底层输入/输出支持
    #include <string>  //字符串类
    #include <utility>  //STL 通用模板类
    #include <vector>  //STL 动态数组容器
    #include <cwchar>
    #include <cwctype>

    //////////////////////////////////////////////////////////////////////////

    『 ۞рēn-Ρlāм ™ 』
    『 Open-Palm ™ 』
     
    『 编辑日志 』

    C/C++ - 指针

    『 ۞рēn-Ρlāм ™ 』 C/C++ - 指针

    针优点:
    1。为函数提供修改调用变元的手段;
    2。支持C++动态分配子程序
    3。可以改善某些子程序的效率
    4。为动态数据结构(如二叉树、链表)提供支持
    注:指针为程序引入了一层间接性,可以操控指针而不直接操控对象。
    1。可操控指针内含的地址也可操控指针所指的对象
    2。指针可能并不指向任何对象,写*pi时,可能会使程序在执行期错误,如寻址到某个对象,则提领操作,不指向任何对象,会出错,所以在提领前先确定它的确指向某对象.

    一个未指向任何对象的指针,内含地址为0,有时称为null指针,assert (p != 0)可检测是否分配成功。也可用if (pi),只有在pi含非零值时,才为true.

    一、定义:
    为存放内存地址的变量。
    诠释:
    指针为一数据类型也有自己的地址。占用四个字节的存储空间
    int * p: &p返回的是指针p的地址,而不是所指变量的地址
    地址:一般指内存中另一变量的位置

    二、指针变量:
    type * name 声明时必须确保它的类型与要指向的对象类型兼容
    const 是“最靠近”为原则
    指向整数常量的指针:const int * p;它所指向的值只读不能被修改 *p = 4(错误), p = 5(正确)
    指向一个整数的常量指针:int * const p;不允许修改指针变量的值,*p = 5 (正确), p = 5 (错误)

    三、指针操作符:
    &(取址运算符):一元操作符,只作用于一个操作数,返回操作数的地址
    *(提领操作):一元操作符,是&的补操作,返回其操作数所指变量的值

    四、指针赋值及转换:
    同类型直接赋值,异类型要进行转换。

    强制转换:可以把表达式结果硬性转换为指定类型
    char * p;(int *)p 把p强制转换为int型,记住转换过程中要注意两个类型的大小,大转小时可能会有数据丢失(如int到double)

    涉及void *的:
    c 中void *类型可赋值给任何类型的指针,反之亦然
    c++ 中都需要强制转换
    void * 可似为无穷大能接纳任何类型赋值,反之不行int * p =9;void * t= p(正确);p=t(错误)

    不涉及void *的都要强制转换

    五、指针的算术操作
    和整数的加法,减法,自身的增量、减量指针增量后指向下一个与指针基类同型的元素,增减单位是所指类型的长度。

    六、其他说明:
    1。指针和数组:
    不带下标的数组名返回数组的起始地址,即数组首元素的地址,所以对数组的访问可有两种方式:数组下标和指针算术

    2。函数指针:
    函数具有可赋给指针的物理内存地址,一个函数地址也为该函数的进入点,也是调用函数的地址

    3。多级指针地址 **p

    七、动态内存分配
    定义:是程序在运行中取得内存的方法。是从堆(heap)--系统的自由内存区-取得内存

    运算符:
    new(c中的malloc):自动建立一个具有合适大小的对象,返回具有正确类型的指针,如分配不成功,返回一个空指针0,且可自动调用构造函数。
    char * p = new char('t');
    delete(c中的free):delect p;
    释放数组对象时要使用方括号delete [] p;

    八、与引用的区别
    &引用运算符:
    1。引用只是变量的别名,而不是指向变量的指针(区别于取址运算符"&")不占内存空间,对变量引用的改变其相应的变量也会改变。

    2。不能对引用使用指针间接运算符“*”进行复引用操作

    3。引用必须在声明时初始化 int &c = count;(c是count的别名)

    九、注意:
    在每次使用指针前,都应该初始化。以防止指针指向空对象。

    应用举例(pointer.cpp)
    编译环境:Window2000 Vc6.0

    #include <string>
    #include <iostream>
    using namespace std;
    void main()
    &leftsign;
    //int * p =1, 不对,整型常量不能转换为整型指针,char * t =0 可以
    //指针运算符&返回操作数的地址,此处&p,&q是p,q的地址
    //要返回得到指向的地址要么正接用p,q 要么用&(*p),&(*q),指针也是
    //是一种数据类型也有自己的内存地址为4个字节,8位
    int * q , * p;
    int x =1 ,y = 2;
    q = &x;
    p = &y;
    cout << "p" << &p << " "<< &(*p)<< " " << p <<" " << *p << endl;
    cout << "q" << &q << " " << &(*q)<< " "<< q<<" "<< *q<< endl;
    //指针赋值,整个指针包含的地址、指向的对象都改变了
    int * t;
    t = q;
    q = p;
    p = t;
    cout << "p" << p << " "<< *p << endl;
    cout << "q" << q << " " << *q << endl;
    //指针所指对象的赋值操作,地址不变
    q = &x; //1
    p = &y; //2
    cout << "p" << p << " "<< *p << endl;
    cout << "q" << q << " " << *q << endl;
    //强制类型转换
    //double *l;
    //l = (double*)*q; // q的值赋给临时变量 *t=1
    *t = *q; // q的值赋给临时变量 *t=1
    cout << *t <<endl;
    *q = *p; // q的值给q,*q=2
    cout << *q <<endl;
    //??*p = *t; //为什么此处*p值没有改变
    *p = *t;
    cout << *t <<endl;
    cout << "p" << p << " "<< *p << endl;
    cout << "q" << q << " " << *q << endl;
    //引用的使用
    int count = 1;
    int &c = count; //声明c为count的引用,c只是count的别名,不占实际内存空间
    cout << "引用";
    cout << c << count << endl;
    //引用变量在声明时要初始化
    //int &t; (错误)
    //t = count ;
    //不能用指针间接运算符复引用一引用,引用只是一变量的别名
    //它不占地址空间
    //cout << *c << endl;
    int iF = 10;
    const int * ciS = 0; //指向整数常量的指针,指针最好都进行初始化
    int * iT = &iF;
    //*ciS = 100;(错误),*ciS为常量
    ciS = iT;
    cout << ciS << " "<< *ciS <<endl;
    int * const icS = &iF; //指向整数的常量指针
    *icS = 10;
    icS = iT;
    cout << icS << " " << *icS << endl;
    &rightsign;

     

    『 ۞рēn-Ρlāм ™ 』
    『 Open-Palm ™ 』
     
    『 编辑日志 』

    C/C++ - 类和对象 II

    『 ۞рēn-Ρlāм ™ 』 C/C++ - 类和对象 II

    II
    人(类)->父母(对象)->子女(继承、多重继承)->子孙(多态性)---哇,My God!多么完美的曲线!
    面向对象程序设计总结:向对象发送消息。可以减少函数传递过程中的参数建立的类要有用,实用为准!

    从此部分开始笔记中加入声明功能,并在每一定义部分后加入小程序(伪代码)用来讲解,方便多了。

     

    ****声明****
    1. const对象和const函数
    2. 友元(friend)类和友元函数
    3. 静态(static)数据成员、静态数据函数及静态对象
    4. 模块(template)类
    5. 其他杂述(复合、包容器类、递取类......)
    * 信息隐藏:对客户隐藏实现细节
    类定义了抽象数据类型(ADT):表示了数据及对数据的操作
    C++强调了数据的重要性  
    * char * temp = new char [stlen (fristname) + 1];
    assert (temp != 0); //确认内存分配是否成功
    6. 应用举例
    a. 堆栈模块类 (TStack.cpp)
    b. 遍历包容器类的递取类(Queue类)
    c. List类
    7. 问题:
     this指针不理解
     为什么不能用const声明静态成员函数
    ****定义****

    1. const对象和const函数
    * const对象不能调用非const函数、不能修改const对象
    const对象和const变量是不能赋值的,必须要初始化。在构造函数中可以用成员初始值进行初始化
    * const成员函数不能调用非const成员函数、不能修改对象的数据成员非const成员函数可重载为const成员函数
    * 特例:当声明一个const对象时与构造函数和析构函数肯定会修改对象矛盾,此处这两个函数都不要声 明关键字const,为了能正确初始化对象,允许这两个函数修改对象
    * 切记:当一个类包括const对象时,必须给构造函数提供成员初始化值 

    class Time
    {
     Time(int = 0, int = 0);
     void setTime(int = 0,int = 0,int = 0); //默认构造函数
     int getHour() const { return hour; } //const成员函数
     private:
     int hour;
     int time const; //const数据成员,要进行成员赋值初始化
    }
    Time::Time(int s, int m)
    :time(m) //成员初始化值
    {
     hour = i;
     time = m; //企图赋值,是错误的
     //数据初始化
    }
    void main()
    {
     const Time T (12, 0, 0); // const对象不能赋值必须初始化
     T.getHour(); // 正确,只有const成员函数才能访问const对象
     T.setTime(); // 错误,非const函数
    }

    2.友元类和友元函数
    友元函数是在类作用域外定义的,但它有权访问该类的私有成员和受保护成员在类定义中,在函数原型或类前加入friend,它可放入类中任何地方,一般在类定义开始处是一种“给予”关系,即B是A的友元,B可访问A中的数据成员及成员函数

    class count
    {
     friend void setX(count & ,int); //声明友元类
     public:
     private:
     int x; //私有数据成员
    }
    //看到此处了没有,没有用类的类作用域符号,它是类外的一个函数,区别处
    void setX(count &c, int val)
    {
     c.x = val; //友元函数所以可以修改私有数据
    }
    void main()
    {
     count c;
     setX(c, 8); //用友元函数设置x的值,此处也没用类作用域符,它是类作用外的一个函数
    }

    3.静态数据成员、函数及对象
    * 类的对象通常都有该类的所有数据成员的单独拷贝,为了让对象共享一份拷贝引入静态(static)数据成员.可节省内存。好像全局变量但只有类作用域
    * 公有的静态数据成员,可以用双目作用域符通过类名访问Time::count(最好在不存在对象时用),也可用类对象访问 T.count,静态成员函数只能用第二种方式
    * 私有的和受保护的只能用公有静态成员函数访问。一个成员函数不要访问类的非静态成员时,声明为静态函数有利于节省内存
    * 即使不存在类对象,静态数据成员、函数也存在并可使用。

    public:
    static int getCount(); //静态函数
    private:
    static int count; //静态成员
    int Time::getCount(return count;)

    4.模板类(template )
    模块类需要一种或多种类型参数,所以模板类也叫带参数的数据类型声明:template <typename elemType>,模板类外的成员函数也都要以这个形式开头

    template <typename elemType>
    int Stack<elemType>::pop() { }
    类名:Stack <elemType>,
    实例化:Stack <float> floatStack ,stack为假设的一个模板类对象
    多个参数:template <typename felemType,typename selemtype>

    5.复合、包容器类、递取类及其他
    * 复合: 一个类把另一个作为自己的成员,成员对象是在包括它们的对象之前建立的成员对象不一定要提供成员初始化值的,其构造函数会自动调用,但如也没默认的构造函数,会出错
    * 成员初始化值可避免对成员对象初始化两次(调用默认构造函数及用"set"函数)

    Time::Time(int hour, int minute, int second)
    :Minute (minute),
    Second (second)
    { }

    6.应用举例:
    编译环境:window2000 Vc6.0
    a. Stack.cpp 堆栈类
    #ifndef TSTACK_H
    #define TSTACK_H
    template<typename elemType>
    class Stack
    {
      public:
     Stack (int = 10); //构造函数,栈默认大小为10
     ~Stack (); //析构函数
     int pop (elemType&); //入栈
     int push (const elemType &); //出栈
     int isEmpty () const { return _top == -1; } //栈空,返回-1
     int isFull () const { return _top == _size - 1; } //栈滿,返回栈顶大小
      private:
     int _size; //堆栈能容纳元素的数目
     int _top; //栈顶元素的数目
     elemType * _stackPtr; //指向栈的指针
    };
    template<typename elemType>
    Stack<elemType>::Stack (int s)
    {
     _size = s;
     _top = -1; //空栈
     _stackPtr = new elemType [_size];
    }
    template<typename elemType>
    Stack<elemType>::~Stack ()
    {
     delete [] _stackPtr;
    }
    //压入一元素,如成功返回1,失败返回0
    template<typename elemType>
    int Stack<elemType>::push (const elemType &item)
    {
     if (! isFull ())
     {
      _stackPtr [++_top] = item; //如栈没滿则栈顶赋值后加1
      return 1; //成功
     }
     return 0; //入栈失败
    }
    //出栈
    template<typename elemType>
    int Stack<elemType>::pop (elemType &popValue)
    {
     if (! isEmpty ())
     {
      popValue = _stackPtr [_top--]; //如栈不为空则赋值后减1
      return 1;
     }
     return 0;
    }
    #endif
    #include <iostream.h>
    #include <stdlib.h>
    void main ()
    {
     Stack<int> floatStack (5); //模板实例化
     int fS = 1;
     cout << "Pushing \n";
     while (floatStack.push (fS))
     {
      cout << fS << ' ';
      fS += 1;
     }
     cout << "Full is:" << fS << '\n' << "Poping \n";
     while (floatStack.pop (fS))
     {
     cout << fS << ' ';
     }
     cout << endl;
     Stack<char> charStack (10);
     char cS = 'a';
     cout << "Pushing \n";
     while (charStack.push (cS))
     {
      cout << cS << ' ';
      cS += 1; //字符串可进行运算
     }
     cout << "Full is:" << cS << '\n' << "Poping \n";
     while (charStack.pop (cS))
     {
      cout << cS << ' ';
     }
     cout << endl;
    }

    『 ۞рēn-Ρlāм ™ 』
    『 Open-Palm ™ 』
     
    『 编辑日志 』

    C/C++ - 类和对象 I

    『 ۞рēn-Ρlāм ™ 』 C/C++ - 类和对象 I

    I
    类,多么富有艺术性的词!想一想能把真实世界中的一切在它中模拟实现,就让人兴奋不已。掌握了它也就掌握了程序艺术的真谛、也就掌握了打通了现实与虚幻的时空门!

    请您记住下面惊人的相似规律:
    人(类)->父母(对象)->子女(继承、多重继承)->子孙(多态性)---哇,My God!多么完美的曲线!

    面向对象程序设计总结:向对象发送消息。
    可以减少函数传递过程中的参数
    建立的类要有用,实用为准!

    一、类杂谈
    1。定义:
    类:对实体的属性和行为的一般描述。 注:太完美的概念是定义不来的是程序设计中的基本单位。可以说是一种新的数据类型,所以有这么多的面向对象程序设计语言。太灵活了类的数据部分:数据成员(data member) ,其初始化不能在定义体中声明它们的地方初始化,而应该用类的构造函数初始化或给它们设置值的函数赋值。最好保持类的所有数据成员都是私有的,提供函数来操作这些数据,可隐藏类的实现、减少错误,提高可修改性。
    外部不能访问类的私有成员,只能利用成员函数。记住main也是一个类的外部

    2。类的函数部分:
    成员函数(member function),在外部访问类公有成员函数,也要用来成员访问运算符
    对象:由类产生的大量的项称为对象也即“存储的一个区域”
    对象是类的最终实例化。
    对象初始化 Time T(23)在对象名右,分号前的圆括号中

    类是C中结构的自然延伸。

    3。类的成员访问运算符:
    圆点:通过对象名或对对象的引用访问 p.t箭头:通过指向对象的指针访问 p->t

    4。构造函数与析构函数
    a.与类同名的成员函数叫“构造函数(constructor)”,用来初始化类的对象的数据成员。

    构造函数:没有返回类型--所以没有返回值,可重载
    给构造函数提供默认参数值即使调用构造函数时没有提供参数值,也会确保按默认参数初始化。所有的参数都是默认参数的也是默认构造函数。
    不要在构造函数中调用其它函数(虽然这是允许的)但在初始化正确地完成之前使用数据成员可能会导致错误

    b. 与类同名但加了“~”(按位取反运算符)的成员函数叫“析构函数(destructor)”,系统回收内存前 做清理工作析构函数:没有返回类型--所以没有返回值,不能重载--所以在类中只有一个

    c.在对象的建立和撤销时分别自动调用相应的构造和析构函数,两者的调用顺序正好相反根据对象的不同:全局作用域(程序终止)、局部对象(声明对象的程序块)、静态static局部对象(程序终止,但在全局前)

    5。 接口和实现
    类的定义:包括数据成员和成员函数的声明,引处的成员函数声明也即函数的原型。要用分号结束类的定义

    接口:类的公有函数(访问说明符public中的函数)提供的操作该类的数据成员的方法,
    此种函数也叫接口把类的声明放入某个头文件中构成类的公有接口
    实现:类的成员函数的定义部分,在外部定义的要用双目运算符“::”使用成员函数,把成员函数的定义放入某个源文件中,从而构成类的实现--信息隐藏用户可以访问类的接口,便不能访问类的实现好处:只要接口没变,实现改变了只要使用类的代码重新编译而不需改动。

    **注软件工程的基本原则:
    最低访问权原则:(当然相对于用户了--类的使用者)除了很小的函数外,所有的成员函数都应在类定义体外定义,有利于接口和实现分离。能鼓励独立软件销售商(ISV)类库作为商品 ---Skyala:全部行业有公用类,统一规化软件类库,多么有趣的一件事

    6。一个成员函数中类的内部定义的会自动成为内联函数(inline),外部的要加inline。

    7。防止多次包含相同的头文件time.h为头文件名,TIME为假设的类,"_"代替".",此处运用了预处理指令

    #ifndef TIME_H
    #define TIME_H
    .
    .
    #endif

    8。公有(Public)、私有(private)、受保护(protected) 私有数据成员只能被本类的成员函数或类的友元访问类的默认情况下是私有访问类型

    9。访问函数及工具函数 set 设置函数,可进行合法性检查。get 获取函数的地位。不仅有助于保护数据的完整性,而且也使得数据成员的实现方式对客户隐藏起来

    10。不要让公有成员函数返回对私有数据成员的非常量引用(或指针)
    如下:
    public:
    int &badset();
    int Time::badset(inthh)
    {
     return hour;
    }
    T.badset(12) = 74; //返回的引用函数调用可作左值。

    11。“=”可以将一个类赋值给同类型的另一个类,通过逐个成员拷贝的默认赋值方式实现的
    应用举例:LeapYear.h、LeapYear.cpp
    注:这是个闰年的计算,不过本人不满意,有点儿乱。
    编译环境:Window2000 Vc6.0

    #ifndef LEAPYEAR_H //预处理指令,防止包含多个同名头文件
    #define LEAPYEAR_H //"-"代替"."

    class LeapYear
    {
      public:
     //可以给构造函数提供默认参数,无返回值无返回类型
     LeapYear (int, int, int);
     ~LeapYear ();
     int getYear ();
     void setMonth (int);
     int getDay ();
     void print () const; //const成员函数
      private:
     int iYear;
     int iMonth;
     //int iday; 要注意大小不要输入错误
     int iDay;
     int checkDay (int); //工具函数
    }; //类定义结束

    #endif
    #include <iostream>
    #include <string>
    #include "LeapYear.h"
    using namespace std;

    //成员初始化
    LeapYear::LeapYear (int iY, int iM ,int iD)
    {
     iMonth = iM;
     iYear = iY;
     //iDay = checkDay (int iD); //函数调用格式错误
     iDay = checkDay (iD);
     cout << "constructor sucessed!" << endl;
    }
    LeapYear::~LeapYear()
    {
     cout << "对象内存释放成功!" << endl;
    }
    int LeapYear::getYear ()
    {
     return iYear;
    }
    void LeapYear::setMonth (int iM)
    {
     iMonth = (iM < 12 && iM > 1)?iM: 1;
    }
    int LeapYear::getDay ()
    {
     return iDay;
    }
    int LeapYear::checkDay (int testDay)
    {
     static int dayPerMonth [13] ={ 0, 31, 28, 31,30,31, 30, 31, 31,30, 31, 30, 31 };
     if (iMonth != 2)
     {
      if (testDay > 0 && testDay <= dayPerMonth [iMonth])
       return dayPerMonth [iMonth];
     }
     else
     {
      //闰年计算公式
      int days = (iYear % 400 == 0 || (iYear % 4 == 0 && iYear % 100 != 0)?29: 28);
      if (testDay > 0 && testDay <= days)
       return days;
     }
     cout << "今天Day" << testDay << "invalid set to day \n";
     return 1;
    }
    void LeapYear::print () const
    {
     cout << iYear << '/' << iMonth << '/' << iDay;
    }
    void main()
    {
     LeapYear lyDay (1994, 2, 4);
     // lyDay.setDay (4);
     lyDay.print ();
     cout << "年份" << lyDay.getYear () << endl;
     cout << "月日" << lyDay.getDay () << endl;
    }

    『 ۞рēn-Ρlāм ™ 』
    『 Open-Palm ™ 』
     
    『 编辑日志 』