zoukankan      html  css  js  c++  java
  • C++/C语言探底巩固

    函数前使用extern没有意义;extern用在变量前表示变量是一个外部链接符号。(函数天然是一个外部链接符号)

    ——与此相关的,指定函数的调用风格(__cdecl, __stdcall, __fastcall)等,或者生成动态连接库时(__declspec(dllimport)、__declspec(dllexport)),或者指定编译语言类型时(C, C++)时,都只需要在 声明体(.h文件) 中说明,不需要在实现体(.c文件)中说明。

    同时,static对于函数和变量的意义也不同。

    对于C而言,源文件内、函数体外的static视为“文件域”可见,而C++中没有这样的说法,类、函数体外可见,即全局可见!

    static用在函数内部,表示这个变量对于函数的每次调用持久存在;

    static用在类成员上,表示这个变量总是采用类名::变量名形式访问,对于类的每个实例总是可见,相当于有限可见的全局变量,类似C语言的文件域。

    const用在形参上,表示函数对它不做写操作;

    const用在成员函数后,表示函数不对成员变量做写操作;

    const用在成员变量上,表示变量不允许任何函数对它做写操作,因此必须在函数体外初始化:

    构造器初始化期间——普通变量,RAM区,实例的stack内;

    或者类的静态区初始化期间——静态const变量,RAM区,全局,但是需要加类名域访问;

    或者编译建立符号表期间——整型静态const变量,ROM区

    区别于宏定义——编译后即变为多个立即数,没有存储空间,而是蕴藏在.text指令中。

    数组的特殊性:??

    非可移植的内存分配技巧:(要求平台的malloc库实现时采用连续分配,用途:概要访问,隐式存储)

    struct name {

        int    namelen;

        char namestr[1];

    };

    #include <stdlib.h>

    #include <string.h>

    struct name *makename( char *newname )

    {

        struct name *ret = malloc(sizeof(struct name)-1 + strlen(newname) + 1);

        if (ret != NULL) {

            ret->namelen = strlen(newname);

            strcpy(ret->namestr, newname);

        }

        return ret;

    }

    C99引入了许多C++中已有的特性:结构体越来越像 类 了——可以创建无名的临时 结构体变量

    结构体的sizeof值是内存占用范围,“空洞”也计算在内

    如何计算field在struct中的字节偏移量?

    非可移植方案:#define offsetof(struct, f)  (size_t)  (  (char*) (struct*)0->f - (char*) (struct*)0  )

    不要使用内建的==或!= 来比较结构体变量——编译器对齐操作可能会导致结构体的稀疏,出现空洞

    结构体中的位域如何使用?

    struct {

       BYTE    :4;  //最低4位不用(Intel little-endian,即最前面的字节)

       BYTE  a:4; //注意,每个位域都不能跨越8bit边界

       BYTE  b:2;

    }x;//其中类型为BYTE即可

    在需要指针型的地方,如何传入一个常量?

    如intf(int *)函数,怎样传入一个常量?     ——在C99中,可以使用“复合字面量”:f(   (int[]){5}   );    类似Java??

    函数名本质上也是一个地址,类似数组名; 函数指针本质上也是一个可以参与 赋值、算术 的变量。  () 是唯一可以用于函数指针的后缀运算符。

    不同的是,函数名、函数指针的地址实际上是代码区或者说ROM的地址,而数据变量名、数据指针的地址实际上是数据区RAM的地址,二者不能通用。例如,空的函数指针为void (*)(),空的数据变量指针为void *;

    NULL没有想象中的省力——在函数参数传入时,仍然需要使用类型转换。

    一般来说,NULL仅仅是一个提示:这里是一个指针0,没有其他意义。所以,在不需要指针的地方(如ASCII空字符:'/0')不要用它。

    使用规则:

    1、在需要空指针常量时,用0或NULL都是等价的;

    2、在函数调用传入0时,根据原型在0或NULL前添加强制转换;

    为什么需要NULL?因为NULL(表示空指针)在某些平台上实现为0,而在另一些平台上用特殊值来实现——为什么C标准不把它统一定义为0?因为在某些平台上用特殊值来触发自动的硬件陷阱,可以捕捉到空指针非法访问的错误,统一用0实现空指针是一种不幸的倒退。

    数组并非C语言一级元素,下标也并非一级运算符,而是定义在指针算术运算基础上——因此,a[i] 等价于 *( (a) + (i) ),也等价于 i[a]。

    通过定义不同类型(主要是维数)的指针p,关联数组a后,可以对p实现++或--不同粒度的重载。

     头文件保护不是组织编译器输入的银弹——

    #ifndef __XX_H

    #define __XX_H

    .......

    #endif

    到底能解决什么类型的问题呢?如果为#include关系的文件画一棵树的话,这个能解决编译树下各个节点由于同时包含根节点导致的“重复包含“的问题。

    那么,还有“递归包含“的问题,怎么解决?如A include B,  B include A

    具体例子是:

    A.h   A(B* b){...}

    B.h   B(A* a){...}

    解法一:拆开递归环,将开口的一端引向另一个端的上级: A include AA,A include B; B include AA, 这种解法适用于C++的继承树,虽然引入了新类AA,但是可以通过虚函数,将BB的功能完全的转移回A类来,

    解法二:没有解法一富于技巧性,但是更加透彻反映了问题的本质。包含问题的本质是编译器对本编译单元A进行类型检查时,需要通过include另一个头文件B获取信息,从而开始编译另一个头文件B。注意这是在一个编译单元A的流程中,因而先前头文件A中的内容是可见的有效的——A的头文件保护#ifndef ...#define ...#endif 执行过的,因而此时编译B时就会跳过A的内容。这样,B就无法获取头文件A的信息,导致出现“未定义的引用“错误。这可以说是编译器的缺陷——在切换一个编译对象时(从编译头文件A中类到编译头文件B),宏由于宏的预处理,

    一本C语言的习题汇编:

    C Programming FAQs, Steve Summit, 人民邮电2008

  • 相关阅读:
    ESAPI = Enterprise Security API
    WingIDE中调试GAE(google app engine)
    C# 发送Http请求 WebClient类
    [转]使用Google App Engine Helper for Django
    装上Window7
    最近遇到个关于接口的奇怪的问题
    JNDI概述(转载)
    Google App Engine 中通过自定义Django的filter解决时区问题
    C# string与byte[]互转
    Python天天美味(33) 五分钟理解元类(Metaclasses)[转]
  • 原文地址:https://www.cnblogs.com/wangfengju/p/6172305.html
Copyright © 2011-2022 走看看