宝典——C/C++程序设计

1 计算转化为二进制后包含1的数量

int fun(int n)
{
    int count = 0;
    while(n)
    {
        count++;
        n &= n-1;
    }

    return count;
}

2 判断是否为2N

!(x&(x-1))

3 求均值

int fun(int x, int y)
{
    return (x & y) + ((x ^ y) >> 1);
}

4 求最大值

int fun(int a, int b)
{
    return ((a+b) + abs(a-b))/2;
}

5 求三个数的中间数

inline int max(int a, int b) { return a >= b ? a : b; };
inline int min(int a,int b) { return a <= b ? a : b; };
inline int medium(int a, int b, int c)
{
    int t1 = max(a, b);
    int t2 = max(b, c);
    int t3 = max(a, c);
    return min(t1, min(t2, t3));
}

6 交换数值

void fun(int &a, int &b)
{
    a = a ^ b;
    b = a ^ b;
    a = a ^ b;
}

7 C++中调用被C编译器编译后的函数,为什么要加extern “C”?

C++语言支持函数重载,C语言不支持函数重载。
用于解决名字匹配问题。

8 头文件中的#ifndef/#define/#endif作用?

防止头文件被重复引用。

9 #define

需要注意括号的使用,例如

#define MIN(A,B) ((A) <= (B) ? (A) : (B))

10 const

int b = 500;
const int *a = &b; //情况1
int const *a = &b; //情况2
int* const a = &b; //情况3
const int* const a = &b //情况4

对于情况1和2,则const用来修饰指针所指向的变量,这种情况下不允许对内容进行更改:

*a = 600; //错误

但是有其他方法可以改变*a的值:

int b = 500, c = 600;
const int *a = &b;
a = &c; //方式1
a = 800; //方式2

对于情况1和2,可以先不进行初始化。虽然指针指向的内容是常量,但是指针本身不是常量。

const int *a; //正确

对于情况3,定义时必须同时初始化:

int b = 500, c = 600;
int* const a; //错误,没有初始化
int* cosnt a = &b; //正确,必须初始化
*a = 600; //正确,允许改值
cout<<a++<<endl; //错误

对于情况4,指针本身和指向的内容均为常量。

任何不修改成员数据的函数都应该声明为const函数。
const声明必须出现在函数的声明和函数的实现里,否则编译器会把它看成一个不同的函数。

11 const与#define相比有什么不同?

C++可以用const或者#define定义常量,但是前者比后者有更多的优点:

  • const常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查,而对后者只能进行字符替换,没有类型安全检查,并且在字符替换中可能会产生意料不到的错误。
  • 有些集成化的工具可以对const常量进行调试,但是不能对宏常量进行调试。在C++常量中只使用常量而不适用宏常量,即const常量完全可以取代宏常量。

12 sizeof

char *ss1 = "0123456789"; //sizeof(ss1)=4,指针大小为定值4
char ss2[] = "0123456789"; //sizeof(ss2)=11
char ss3[100] = "0123456789"; //sizeof(ss3)=100
int ss4[100]; //sizeof(ss4)=400
char q1[]="abc"; //sizeof(q1)=4
char q2[]="a\n"; //sizeof(q2)=3,'\n'为一位
char* q3 = "a\n"; //sizeof(q3)=4
char *str1=(char*)malloc(100); //sizeof(str1)=4
void *str2=(void*)malloc(100); //sizeof(str2)=4

对于结构体,结构体的长度一定是最长的数据元素的整数倍。结构体对齐参数按默认的8字节对齐。

struct{
 short a1;
 short a2;
 short a3;
}A; //sizeof(A)=6

struct{
 long a1;
 short a2;
}B; //sizeof(B)=8

13 类的大小

空的类是会占用内存空间的,而且大小是1,原因是C++要求每个实例在内存中都有独一无二的地址。

  • 类内部的成员变量:

    • 普通的变量:是要占用内存的,但是要注意对齐原则(这点和struct类型很相似)。
    • static修饰的静态变量:不占用内容,原因是编译器将其放在全局变量区。
  • 类内部的成员函数:

    • 普通函数:不占用内存。
    • 虚函数:要占用4个字节,用来指定虚函数的虚拟函数表的入口地址。所以一个类的虚函数所占用的地址是不变的,和虚函数的个数是没有关系的

14 sizeof和strlen之间的区别

  • sizeof操作符的结果类型是size_t,它在头文件中的typedef为unsigned int类型。该类型保证能容纳所建立的最大对象的字节大小。
  • sizeof是运算符,strlen是函数。
  • sizeof可以用类型做参数,strlen只能用char*做参数,且必须是以“\0”结尾的。sizeof还可以用函数做函数。
  • 数组做sizeof的参数不退化,传递给strlen就退化为指针。
  • 大部分编译程序在编译的时候就把sizeof计算过了,是类型或是变量的长度。这就是sizeof(x)可以用来定义数组维数的原因。
  • strlen的长度要在运行的时候才能计算出来,用来计算字符串的长度,而不是类型占内存的大小。
  • sizeof后如果是类型必须加括号,如果是变量名可以不加括号,因为sizeof是个操作符而不是个函数。
  • 当使用了一个结构类型或变量时,sizeof返回实际的大小。当使用一静态的空间数组时,sizeof返回全部数据的尺寸。sizeof操作符不能返回被动态分配的数组或外部的数组的尺寸。
  • 数组作为参数传递给函数时传的是指针而不是数组,传递的是数组的首地址。

15 空类默认产生4个成员函数:默认构造函数、析构函数、拷贝构造函数和赋值函数。

16 类和结构

struct也可以有构造函数、析构函数,之间也可以继承。唯一的不同是struct里面默认的访问控制是public,class中默认的访问控制是private。

17 3种继承方式:(默认为私有继承)

继承方式 对派生类可见性 对派生类对象可见性 作为派生类对象时状态
public 公有和保护 公有的可见 保持原状态
protected 公有和保护 都不可见 保护
private 公有和保护 都不可见 私有

18 虚拟继承是多重继承中特有的概念,是为解决多重继承而出现的。(防止多次出现某个共同基类)

19 虚指针或虚函数指针是一个虚函数的实现细节。带有虚函数的类中的每一个对象都有一个虚指针指向该类的虚函数表。

20 防止被实例化:使用抽象类或者构造函数被声明为private。

21 运算符重载:全局函数(一元为一个参数,二元为两个参数),成员函数(一元没有参数,二元为两个参数)。

22 位置转换

printf("%f", 5);   // 0.000000
printf("%d", 5.01);  //一个大数

22 类型转换操作符

  • static_cast 在编译时使用类型信息执行转换,在转换执行时进行必要的检测,操作数相对是安全的。
  • const_cast 最普通的用途就是转换掉对象的const属性
  • reinterpret_cast 复制要转换对象的比特位到目标(是为了映射到完全不同的类型,双刃剑,谨慎使用)
  • dynamic_cast 运行时检查,用于在继承体系中进行安全的向下转换(基类指针/引用到派生类指针/引用的转换),如果源和目标类型没有继承关系,编译器会报错。必须在代码里判断返回值是否为NULL来确认转换是否成功。是四个转化中唯一的RTTI操作符,提供运行时类型检查。(当基类指针指向或引用派生类对象时,转换成功;否则得到NULL)

23 求值

unsigned int i = 0; i -= 1; // i=65535=pow(2,8) - 1;

24 设置和清楚某个位

#define BIT3 (0x1 << 3)
static int a;
void set_bit3()
{
    a |= BIT3;
}
void clear_bit3()
{
    a ^= BIT3;
}

25 输出结果为3c,即15*4=60。

int *pa = NULL;
int *pb = pa + 15;
printf("%x", pb);
return 0;

26 volatile

语法与const一样,但是volatile的意思是在编译器认识的范围外,这个数据可以被改变。(该变量可能会被意想不到地改变)

27 设置绝对地址为0x67a9的整型变量的值为0xaa66。

int *ptr;
ptr = (int *)0x67a9;
*ptr = 0xaa66;

28 补码

  • 10的二进制补码为-(2^8 - |-10|) = - 246 = 11110110;
  • 10的三进制4位数补码为3^4 - |-10|=71=2111;

29 关键字static的作用

  • 函数体内static变量的作用范围为该函数体,不同于auto变量,该变量的内存植被分配一次,因此其值在下次调用时扔维持上次的值。
  • 在模块内的static全局变量可以被模块内所有函数访问,但不能被模块外其他函数访问。
  • 在模块内的static函数只可被模块内的其他函数使用,这个函数的使用范围被限制在声明它的模块内。
  • 在类中的static成员变量属于整个类所拥有,对类的所有对象只有一份拷贝。
  • 在类中的static成员函数属于整个类所拥有,这个函数不接收this指针,因而只能访问类的static成员变量。
上一篇:【Spring注解驱动开发】使用@Lazy注解实现懒加载


下一篇:spring注解开发:bean的作用域与懒加载