zoukankan      html  css  js  c++  java
  • c/c++整理(一)

    1.swap(a, b)三种方式:

    1.临时变量

    2. a = a + b; b = a – b; a = a – b; 可能会导致数据溢出

    3.a ^ = b; b ^= a; a ^= b;

    两个字符串的交换

    void swap(char *& a, char  *&b)
    {
    	char *tmp;
    	tmp = a;
    	a = b;
    	b = tmp;
    }
    
    void swap(char **a, char **b)
    {
    	char *tmp
    	tmp = *a;
    	*a = *b;
    	*b = *tmp;
    }

    2.extern “c”含义

    使c++编译器提供与c连接交换指定的符号,用来解决名字匹配问题。详细:http://baike.baidu.com/view/2816461.htm

    3.main函数

    main函数代表进程主线程,系统调用c/c++运行期启动函数,进行运行期库进行初始化,之后调用main,main执行完毕后,启动函数再调用exit(),将返回值传给exit(),exit()会调用ExitProcess(),结束进程。

    int atexit(void (*func)(void))

    atexit()注册的函数类型应为不接受任何参数的void函数,exit调用这些注册函数的顺序与它们 登记时候的顺序相反。同一个函数如若登记多次,则也会被调用多次。

    #include <stdio.h>
    #include <stdlib.h>
    void exit_fn1(void)
    {
     printf("Exit function #1 called\n");
    }
    
    void exit_fn2(void)
    {
     printf("Exit function #2 called\n");
    }
    int main(void)
    {
     /* post exit function #1 */
     atexit(exit_fn1);
     /* post exit function #2 */
     atexit(exit_fn2);
     return 0;
    }
    输出:

    Exit function #2 called

    Exit function #1 called

    进程的终止方式:
    有8种方式使进程终止,其中前5种为正常终止  1:从 main 返回
      2:调用 exit
      3:调用 _exit 或 _Exit
      4:最后一个线程从其启动例程返回
      5:最后一个线程调用 pthread_exit
    异常终止有3种,它们是
      6:调用 abort
      7:接到一个信号并终止
      8:最后一个线程对取消请求做出响应
      #include <stdlib.h>
      void exit (int status);
      void _Exit (int status);
      #include <unistd.h>
      void _exit (status);
      其中调用 _exit,_Exit 都不会调用终止程序
      异常终止也不会。

    4.宏参数的连接

    #define STR(s) #s  //s代表字符串

    #define CONS(a,b) (int)(a##e##b)   //##把两个参数连在一起

    STR(ABC);  ABC

    CONS(1, 2); 100

    5.c++ const作用

    1.用于定义常量,编译器可对其进行数据静态类型安全检查。

    2.修饰形参,值传递改为const & 可提高效率,省去对象的构造 复制 析构过程消耗的时间。

    3.修饰函数返回值,返回值不能直接被修改,且只能赋给const修饰符的同类型指针。

    4.修饰类成员函数,任何不需要修改数据成员的函数都应该使用const修饰,这样即使不小心调用修改数据的非

    const函数,编译器也会报错。

    6.static

    static全局变量只初始化一次,防止在其他单元中引用。static局部变量只初始化一次。static函数在内存中只有一份。

    7.空类,编译器会安插一个char空类,标记它的每个对象,因此为1字节,空类多继承空类,大小仍为1字节。空类虚继承另一个空类,会分配一个指向父类的指针,因此不会再安插一个char,静态变量存储在静态存储区,被类共享,不算入实例化后对象大小。

    8.union和struct 对齐(class内变量对齐也类似)

    转:

    原则1、数据成员对齐规则:结构(struct或联合union)的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储)。

    原则2、结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。(struct a里存有struct b,b里有char,int,double等元素,那b应该从8的整数倍开始存储。)

    原则3、收尾工作:结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补齐。

    这三个原则具体怎样理解呢?我们看下面几个例子,通过实例来加深理解。

    例1:struct {
                         short a1;
                         short a2;
                         short a3;
                        }A;

                   struct{
                       long a1;
                       short a2;
                      }B;

           sizeof(A) = 6; 这个很好理解,三个short都为2。

           sizeof(B) = 8; 这个比是不是比预想的大2个字节?long为4,short为2,整个为8,因为原则3。

    例2:struct A{
                        int a;
                        char b;
                        short c;
                        };

                   struct B{
                       char b;
                       int a;
                       short c;
                        };

           sizeof(A) = 8; int为4,char为1,short为2,这里用到了原则1和原则3。

           sizeof(B) = 12; 是否超出预想范围?char为1,int为4,short为2,怎么会是12?还是原则1和原则3。

    深究一下,为什么是这样,我们可以看看内存里的布局情况。

                                       a         b         c
           A的内存布局:1111,     1*,       11

                                       b          a        c
           B的内存布局:1***,     1111,   11**

    其中星号*表示填充的字节。A中,b后面为何要补充一个字节?因为c为short,其起始位置要为2的倍数,就是原则1。c的后面没有补充,因为b和c正好占用4个字节,整个A占用空间为4的倍数,也就是最大成员int类型的倍数,所以不用补充。

           B中,b是char为1,b后面补充了3个字节,因为a是int为4,根据原则1,起始位置要为4的倍数,所以b后面要补充3个字节。c后面补充两个字节,根据原则3,整个B占用空间要为4的倍数,c后面不补充,整个B的空间为10,不符,所以要补充2个字节。

    再看一个结构中含有结构成员的例子:

    例3:struct A{
                         int a;
                         double b;
                         float c;
                        };

                    struct B{
                         char e[2];
                         int f;
                         double g;
                         short h;
                         struct A i;
                        };

           sizeof(A) = 24; 这个比较好理解,int为4,double为8,float为4,总长为8的倍数,补齐,所以整个A为24。

           sizeof(B) = 48; 看看B的内存布局。

                                     e         f             g                h                                    i
           B的内存布局:11* *,   1111,   11111111, 11 * * * * * *,        1111* * * *, 11111111, 1111 * * * *

           i其实就是A的内存布局。i的起始位置要为24的倍数,所以h后面要补齐。把B的内存布局弄清楚,有关结构体的对齐方式基本就算掌握了。

    以上讲的都是没有#pragma pack宏的情况,如果有#pragma pack宏,对齐方式按照宏的定义来。比如上面的结构体前加#pragma pack(1),内存的布局就会完全改变。sizeof(A) = 16; sizeof(B) = 32;

    有了#pragma pack(1),内存不会再遵循原则1和原则3了,按1字节对齐。没错,这不是理想中的没有内存对齐的世界吗。

                                      a                b             c
           A的内存布局:1111,     11111111,   1111

                                     e        f             g          h                     i
           B的内存布局:11,   1111,   11111111, 11 ,            1111, 11111111, 1111

    那#pragma pack(2)的结果又是多少呢?#pragma pack(4)呢?留给大家自己思考吧,相信没有问题。

    还有一种常见的情况,结构体中含位域字段。位域成员不能单独被取sizeof值。C99规定int、unsigned int和bool可以作为位域类型,但编译器几乎都对此作了扩展,允许其它类型类型的存在。

    使用位域的主要目的是压缩存储,其大致规则为:
           1) 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;
           2) 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;
           3) 如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式,Dev-C++采取压缩方式;
           4) 如果位域字段之间穿插着非位域字段,则不进行压缩;
           5) 整个结构体的总大小为最宽基本类型成员大小的整数倍。

    还是让我们来看看例子。

    例4:struct A{
                          char f1 : 3;
                         char f2 : 4;
                         char f3 : 5;
                         };

                                     a         b             c
           A的内存布局:111,    1111 *,   11111 * * *

    位域类型为char,第1个字节仅能容纳下f1和f2,所以f2被压缩到第1个字节中,而f3只能从下一个字节开始。因此sizeof(A)的结果为2。

    例5:struct B{
                        char f1 : 3;
                        short f2 : 4;
                        char f3 : 5;
                        };

    由于相邻位域类型不同,在VC6中其sizeof为6,在Dev-C++中为2。

    例6:struct C{
                         char f1 : 3;
                         char f2;
                        char f3 : 5;
                        };

    非位域字段穿插在其中,不会产生压缩,在VC6和Dev-C++中得到的大小均为3。

    考虑一个问题,为什么要设计内存对齐的处理方式呢?如果体系结构是不对齐的,成员将会一个挨一个存储,显然对齐更浪费了空间。那么为什么要使用对齐呢?体系结构的对齐和不对齐,是在时间和空间上的一个权衡。对齐节省了时间。假设一个体系结构的字长为w,那么它同时就假设了在这种体系结构上对宽度为w的数据的处理最频繁也是最重要的。它的设计也是从优先提高对w位数据操作的效率来考虑的。有兴趣的可以google一下,人家就可以跟你解释的,一大堆的道理。

    最后顺便提一点,在设计结构体的时候,一般会尊照一个习惯,就是把占用空间小的类型排在前面,占用空间大的类型排在后面,这样可以相对节约一些对齐空间。

    9.inline与宏

    宏只是简单替换,无法进行参数有效性检测,返回值爷爷不能被强制转换为可转换的何时类型,inline消除了他的缺点,又继承其优点。

    inline定义类的内联函数,函数代码放入符号表中,在使用时进行替换,没有了调用开销,效率很高,也会进行参数合法性检查,就像对待真正的函数一样。

    二者区别:

    1.内联函数在编译时展开,宏在预编译时展开。

    2.在编译时,内联函数可以直接嵌入到目标代码中,宏只是简单的文字替换。

    3.内联函数可以完成类型检测,语句是否正确等编译功能,宏不具有这样功能。

    4.宏不是函数,inline函数是函数。

    5.宏易出现参数二义性。

    10.引用和指针

    引用声明的同时必须被初始化,也只能在声明时被赋值于某个变量。

    常量引用: cont int & t = a; t = 1;错误,不可通过常量引用改变目标变量值。非常量引用不能指向常量类型。对于常量类型变量,其引用也必须是常量类型。对于非常量类型,其引用常量非常量即可。

    指针和引用的区别:

    1.指针定义时可以不必立刻初始化。

    2.引用一旦初始化,就不能再指向其他变量。

    3.不存在NULL引用。

    4.指针使用前要测试合法性 例:if (point == null)

    复杂指针声明

    用变量a给出下面定义:

    定义一个指向有10个整型数的数组指针:int (*a)[10]

    定义一个函数指针,该函数有一个整形参数并返回一个整形数 int (*a)(int)

    定义一个10个指针数组,指针指向一个整形参数并返回一个整形数的函数 int (*a[10])(int)

    注意:不同指针指向同样字符串,一般指向的是同一块数据区的内存

  • 相关阅读:
    Linux Core Dump
    ODP.NET Managed正式推出
    获取EditText的光标位置
    (Java实现) 洛谷 P1603 斯诺登的密码
    (Java实现) 洛谷 P1603 斯诺登的密码
    (Java实现) 洛谷 P1036 选数
    (Java实现) 洛谷 P1036 选数
    (Java实现) 洛谷 P1012 拼数
    (Java实现) 洛谷 P1012 拼数
    (Java实现) 洛谷 P1028 数的计算
  • 原文地址:https://www.cnblogs.com/seebro/p/2509268.html
Copyright © 2011-2022 走看看