C/C++ 一些知识点总结

临时整理的,有些可能不对。

1)如果一个类里面有const-qualifier或reference, compiler不会为它生成default copy assignment operator函数。

2) Hidden是指子类和父类有同名函数,但又没有virtual关键字。这里函数只要同名就可以了,参数并不需要match。也就是说,compiler只要在子类看到有这个函数名,即使参数不match,也不会再往父类找了。

3) delete function, default function 都是C++ 11新加的函数属性。 

4) Decision of constructor happens at compile time。

5) Non-member function 不能在它后面加const。

6) macro的缺点: a) macros不会放进symbol table; b) macros 不管scope; c) macro 替换有时可能不是想要的地方。

Enum的缺点应该也包含上面的a)和b)。

7) delete p 和 free p都不会改变p的值。

8) 在line 2后放断点,x在symbol table里面是数组还是指针? 答案是一个数组。sizeof(x)=10*sizeof(int)。p是一个指向int的指针。

int main {
    int x[10];    //line 1
    int *p;       //line 2
}

9) pragma once 不portable。

10) 在copy constructor和copy assignment operator中,如果类中有一个member是数组,当数组成员不包含指针时,用memcpy是安全的,否则要用loop挨个调用copy assignment operator初始化。

11)  C++中,private是对class而言。同一个class的不同object可以互相访问对方的private member。

12) 类的static成员不增加类实例的大小。

13) 通常不需要显式调用destructor。

14) static 成员在类中出现只是声明(declaration),应该还要另外definition。为什么呢? 因为类的definition都是放在头文件内,如果算definition则包含该头文件的每个.cpp文件都有一个copy。

15) 如果两个pointer刚好相等(first==last),delete ptr危险!因为对应的析构函数调用两次!

16) inline是一个request, 编译器可以选择拒绝。

17) 

int *p;
int const *q;   //值const
int *const r;   //指针const 
p=q; //no, invalid conversion from 'const int*' to 'int *'
p=r; //ok
q=p; //ok
q=r; //ok
r=p; //no 
r=q ;//no

18) int const * const p = &x; 指针和值都const。典型例子是read-only register。

19) T& const r 不合法,因为引用&本身自带const属性,即一个引用不能再指向别人???  

20) 引用相当于const pointer。

21) lvalue 是addressable,可以找到地址。

22) Compiler 会自行决定是否给const object 分配地址 。

23) 

int &ri=3;    //no
int const &ri=3; //OK

24) 

void fun(int n) {
  int a[n];
   ...
}

这种写法C99和C++14都是支持的。但像TurboC和VC平台仅支持C89的不支持这种写法。

25) C/C++的堆上变量若未初始化,缺省值为0。栈上变量若未初始化,缺失值为未定义。

26) 

struct A {
 ...
};

C要用struct A a;  C++可以直接用A a;

27) C++11定义的大括号初始化

class Test{    
    int a;    
    int b;    
public:    
    C(int i, int j);    
};    
Test t{0,0};                    //C++11 only,相当于 Test t(0,0);    
Test* pT=new Test{1,2};         //C++11 only,相当于 Test* pT=new Test{1,2};    
int* a = new int[3]{ 1, 2, 0 }; //C++11 only
// C++11 container initializer    
vector vs={ "first", "second", "third"};    
map singers ={ {"Lady Gaga", "+1 (212) 555-7890"},{"Beyonce Knowles", "+1 (212) 555-0987"}}; 

下面这两种方法都可以

vector result = {2, 3, 7};

vector result;

result = {2,3,7};

下面这个写法也是返回一个vector的临时实例。
vector{a[i], a[i+1]};

但27)这些写法一般只用于POD(plain old data) 变量。

28) vector(8) 会生成{0,0,0,0,0,0,0,0}这样一个临时vector。
vector(1,7) 会生成{7}这样一个临时vector。

vector c = {a[i], b[i]};  //生成一个vector c并初始化。

如果用数组,那么
int a[10] = {0}; //表示把a数组10个元素全部清零。
int a[10] = {1}; //表示把a[0]=1, a[1]-a[9]清零。
其实int a[10] = {0} 也是表示把a[0]=0, a[1]-a[9]清零。效果就等价于把a[0]-a[9]全部清零。

另外,二维数组呢?也是一样的。
int a[3][4] = {3}; //表示a[0][0]=3, 其他所有元素都是0。
int a[3][4] = {0}; //表示a[0][0]=0, 其他所有元素都是0。当然所有元素也都是0了。

vector ilist5(7,3);

指定值初始化,ilist5被初始化为包含7个值为3的int

29) int *a, b;  其中a是指针,b是integer.

30) vector > results;
      vector sol;
这里sol和results的值都是[]。那么怎么才能让results的值变成[[]]呢?
results.push_back(vector());
当然用
result.push_back(sol);
也可以。

将result变成空集可以用result = {}。

返回一个空vector:

方法1: return vector();

方法2:return {}    //since C++ 11

返回一个2D的空vector:return {{}}

举例如下:

    vector> groupAnagrams(vector &strs) {
        int n = strs.size();
        if (n == 0) return {{}};
        ...
}

31) 关于C里面 数字0,字符'0',字符串"0",字符串结尾标志符'\0' 的区别。参考了

关于C里面 数字0,字符'0',字符串"0",字符串结尾标志符'\0'_wmlhust的博客-CSDN博客

字符‘0’:
     在这个表中,字符零,也就是C中的 ‘0’ 对应的是48,即,字符零在计算机中的存储是48。也就是说以下代码执行结果是48。

  1. char czero = '0';
  2. printf("czero = %d\n", czero);

执行结果:    czero = 48

数字0:
      数字类型的数据在计算机中存储即是本身,就是0,应该是对应ASCII码里面的NULL。
     int izero = 0;
这里的izero和上面的czero是两码事。

字符串“0”:
    字符串“0” 相当于存了两个符号,一个是字符‘0’,一个是字符串结尾标志‘\0’,其存储的十进制数就是数字0。

字符串结尾标志‘\0’:
    这里 '\0' 中的反斜线 \ 可以当做转义符,跟 \\ 表示 \,\' 表示 ' 一样,\0 表示的就是 0。
别忘记了这里的 \0 是字符类型的,相当于裸0(我自己想的名字==),也就是数字0,数字0对应着哪个字符呢,查看下ASCII表格,发现第一个就是,NULL,这样也好理解了,在读取字符串的时候,末尾是NULL,但是必须有这个NULL,才能告诉编译器字符串结束了。

对于memset,
      memset(*dst, 0, size)  和  memset(*dst, '\0', size)效果是一样的,但是和memset(*dst, '0', size)不一样。

32) int a[] = {1,4,5,2,8,6,0};
      sizeof(a)=4*7=28    
      sizeof(a)/sizeof(int) = 7

     char c[]="a";
     cout<
     cout<      cout<

     char d[]="";
     cout<
     cout<     cout<
 

33) 关于for循环:

for(表达式1;表达式2;表达式3){
循环语句
}
首先执行表达式1,一般是进行变量初始化操作,然后执行表达式2,即对循环条件进行判断,如果结果为真,则执行循环体;循环体执行完毕后,执行表达式3,改变循环变量的值,再次执行表达式2;结果为真,继续循环;如果结果为假,则终止循环,执行后面的语句。

34) unsigned 和 signed 混在一起,结果为unsigned。
int a = -20;
unsigned int b = 10;
cout<

35) C++如何定义一个只能在堆上(或栈上)生成对象的类?
参考 如何定义一个只能在堆上(栈上)生成对象的类?__牛客网
只能在堆上  方法:将析构函数设置为私有
原因:C++ 是静态绑定语言,编译器管理栈上对象的生命周期,编译器在为类对象分配栈空间时,会先检查类的析构函数的访问性。若析构函数不可访问,则不能在栈上创建对象。

只能在栈上  方法:将 new 和 delete 重载为私有

原因:在堆上生成对象,使用 new 关键词操作,其过程分为两阶段:第一阶段,使用 new 在堆上寻找可用内存,分配给对象;第二阶段,调用构造函数生成对象。将 new 操作设置为私有,那么第一阶段就无法完成,就不能够在堆上生成对象。

36) 符号位(sign bit), 阶码(exponent),也叫指数,尾数(mantissa或fraction)

sign bit: 用来表示正负号
exponent: 用来表示次方数
mantissa: 用来表示精确度

               31        30-23       22-0

float       符号位     阶码        尾数

                63        62-52       51-0

double    符号位     阶码        尾数

37) 在win7 64bit, Code::block 17.12 下面测得
sizeof(short)=2
sizeof(int)=4
sizeof(long)=4
sizeof(long long)=8
sizeof(float)=4
sizeof(double)=8
sizeof(long double)=12

38) set s(a.begin(), a.end());
       a.assign(s.begin(), s.end());
可以帮vector a排序并去重,利用了set本身无重复元素和自动排好序的特性。

注意unordered_set不能排序。


39) std::lower_bound(a.begin(), a.end(), x);    //在vector a (也可以是其他stl容器如set)里面找到不小于x的第一个元素。

std::upper_bound(a.begin(), a.end(), x);    //在vector a (也可以是其他stl容器如set)里面找到不小于x的最后一个元素。

40) 下面几种方法等价 :

A. 

struct cmp {
    bool  operator()(Connection &a, Connection &b) {
       return a.cost < b.cost;
    }    
}cmpObject;

sort(result.begin(), result.end(), cmpObject);

//Note, for priroity_queue, it should be defined as follows:    
priority_queue,cmp> q;

B. 

bool cmp(const Node& c1, const Node& c2) {
     return c1.value<= c2.value;
}        
sort(nodes.begin(), nodes.end(), cmp);

C.

inline bool operator < (const struct Node &n1, const struct Node &n2) {
    return n1.value < n2.value;
}
sort(nodes.begin(), nodes.end());

41) C++ 一维vector初始化的不同方法

vector v1          

v1是一个空vector,它潜在的元素是T类型的,执行默认初始化

vector v2(v1)       

v2中包含有v1所有元素的副本

vector v2 = v1       

等价于v2(v1)v2中包含有v1所有元素的副本

vector v3(n, val)      

v3包含了n个重复的元素,每个元素的值都是val

vector v4(n)          

v4包含了n个重复地执行了值初始化的对象

vector v5{a,b,c...}

v5包含了初始值个数的元素,每个元素被赋予相应的初始值

vector v5={a,b,c...}

等价于v5{a,b,c...}

42) stringstream tokenize 模板:

只用空格来隔开:

        stringstream ss(doc.content);
        string buf;
        vector tokens; // Create vector to hold our words
        while (ss >> buf) tokens.push_back(buf);

如果还需要考虑','的话

        stringstream ss(s);
        string buf;
        vector words;
        while(getline(ss, buf, ',')) {
            words.push_back(buf);
        }

43) 下面这种写法不对。因为vector的size未定,空间还未分配,不能直接用下标定位。
 

vector nums;
nums[0] = 3;

44) C++ double_min 的定义是

#include 

constexpr double lowest_double = std::numeric_limits::lowest();

 std::numeric_limits::max
     C++ double_min的定义是std::numeric_limits::min

45) stl::multimap里面的equal_range(key)表示这个key所有的pairs。

        //The function equal_range returns a pair, whose member pair::first is the lower bound of the range (the same as lower_bound), and pair::second is the upper bound (the same as upper_bound).
       用法如下: 

std::multimap mymm;

  mymm.insert(std::pair('a',10));
  mymm.insert(std::pair('b',20));
  mymm.insert(std::pair('b',30));
  mymm.insert(std::pair('b',40));
  mymm.insert(std::pair('c',50));
  mymm.insert(std::pair('c',60));
  mymm.insert(std::pair('d',60));

  std::cout << "mymm contains:\n";
  for (char ch='a'; ch<='d'; ch++)
  {
    std::pair ::iterator, std::multimap::iterator> ret;
    ret = mymm.equal_range(ch);
    std::cout << ch << " =>";
    for (std::multimap::iterator it=ret.first; it!=ret.second; ++it)
      std::cout << ' ' << it->second;
    std::cout << '\n';
  }

46) C++ stl里面把string 变 int 用stoi(), 把int变string用to_string()

47) 

在C++98标准里,只有static const声明的整型成员能在类内部初始化,并且初始化值必须是常量表达式。这些限制确保了初始化操作可以在编译时期进行。例如:

int var = 7;
class X {
    static const int m1 = 7;   // 正确
    const int m2 = 7;    // 错误:无static
    static int m3 = 7;              // 错误:无const
    static const int m4 = var;  // 错误:初始化值不是常量表达式
    static const string m5 = “odd”; //错误:非整型
    // …
};

C++11的基本思想是,允许非静态(non-static)数据成员在其声明处(在其所属类内部)进行初始化。这样,在运行时,需要初始值时构造函数可以使用这个初始值。考虑下面的代码:

class A {
public:
    int a = 7;
};

class A {
public:
    int a;
    A() : a(7) {}
};

48) lower_bound()的意义是对于给定的已经排好序的a,key最早能插入到那个位置
upper_bound()的意义是对于给定的已经排好序的a,key最晚能插入到那个位置**
举例:
int a[]={0,1,2,2,3};  
lower_bound(a,a+5,2)-a);   //=2  
upper_bound(a,a+5,2)-a);  //=4 

结果:2 4
0 1 | 2 2 3 所以2最早插入到2号位置
0 1 2 2 | 3 所以2最晚插入到4号位置0 1 | 2 2 3 所以2最早插入到4号位置
6) lower_bound()和upper_bound()还可以加入cmp函数。

49) vector子段赋值是
vv.push_back(vector(v.begin() + i, v.end()));
v2 = vector(v.begin() + i, v.end());50) 关于int和int()的初始化区别:
 

void f() {

    int a;   //a 初始化值不确定
    int b = int();    //b被初始化为0, 注意不能写成 int b(); 这是函数声明
    int * c = new int;   // c指向的值不确定
    int * d = new int();  // d指向值是0
    int e;   //e 初始化不确定
    new (&e) int() ;  // 使用placement new
}

struct Foo{
    Foo() : g() {}  // f 不确定, g初始化为0
    int f;
    int g;  
}

50) 关于C语言的取整:

    double x;
    printf("%d",int(x))  取整;   
    printf("%d",int(x+0.5)) 四舍五入

    直接赋值给整数变量 
    int i = 2.5; 或 i = (int)2.5;   //舍去小数部分。
    注意:
        int i = 2.5 (i=2)
        int i = 2.9 (i=2)
        int i = -2.3 (i=-2)
         int i = -2.5(i=-2)
         int i = -2.6(i=-2)

   注意对负数的取整!!!     我的总结就是不管对正数还是负数,取整就是把小数部分截去。 

    C/C++中的整数除法运算符“/”本身就有取整功能(int /int)。整数除法对正数的取整是舍去小数部分。但是整数除法对负数的取整结果和使用的C编译器有关。 

    floor(x)返回的是小于或等于x的最大整数, 即向负无穷大舍入。 如: 
    floor(2.5) = 2 
    floor(-2.1) = -3
    floor(-2.5) = -3 
    floor(-2.6) = -3

    ceil(x)返回的是大于x的最小整数,即向正无穷大舍入,如: 
    ceil(2.1) = 3
    ceil(2.5) = 3 
    ceil(2.9) = 3
    ceil(-2.1) = -2
    ceil(-2.5) = -2
    ceil(-2.9) = -2

关于floor()有一个有趣的应用,即如何判断一个整数的位数呢?

可以用下面的函数,基于log10()。

int digit(int x) {
    return (int)floor(log10(x)) + 1;
}

注意
1) floor()函数返回double。
2) 注意要#include
3) 为什么是floor()+1,而不是ceil()呢?
一个例子是log10(100)=2,但是ceil()后还是2。

51) C++里面,unordered_map不可以用 for (auto m : mp),只有map才可以。

即下面的代码为错:

unordered_map ump;
for (auto a : ump) { //wrong!
     if (a.second > 0) {
      ...
}

注意:这里可能不对。有时候也可以用auto遍历unordered_map。那什么时候可以,什么时候不可以呢?

注意:unordered_set是基于hash table,所以要重载operator == (不需要重载opertor <),并且要定义NodeHash结构体,这样hash table才知道调用哪个hash function。
具体参考 LintCode 778: Pacific Atlantic Water Flow (BFS好题)_roufoo的博客-CSDN博客

//具体看LintCode 171 ”Anagrams“那题(不可以) 和 LintCode 78 "Longest Common Prefix"那题(可以)。

52) C++操作符重载好像不能用指针做参数?

也就是说

inline bool operator < (TreeNode & a, TreeNode & b) {

return a.val < b.val;

}

不能写成

inline bool operator < (TreeNode * a, TreeNode * b) {

return a->val < b->val;

}

53) 产生一定范围随机数的通用表示公式 
取得[a,b)的随机整数,使用(rand() % (b-a))+ a; 
取得[a,b]的随机整数,使用(rand() % (b-a+1))+ a; 
取得(a,b]的随机整数,使用(rand() % (b-a))+ a + 1; 
通用公式:a + rand() % n;其中的a是起始值,n是整数的范围。 
取得a到b之间的随机整数,另一种表示:a + (int)b * rand() / (RAND_MAX + 1)。 
取得0~1之间的浮点数,rand() / double(RAND_MAX)。

54) C++里面,

如果用for (auto a : A),这里面的a都是一个个的A里面的成员,不是指针。

如果是
auto iter = A.begin();
while
 (iter != a.end()) {
    ++iter;
}

这里面的iter是指针,指向A中的一个个成员。

55) C++里面的容器,如何删掉了iter之后还能遍历呢?
参考网页:

c++如何遍历删除map/vector里面的元素 - 大宝pku - 博客园

代码1:

 auto iter = a.begin();
    while (iter != a.end()) {
        if (iter->second > 30) {
            a.erase(iter++);
        }
        else {
            ++iter;
        }
    }

代码2:
 

    auto iter = a.begin();
    while (iter != a.end()) {
        if (*iter > 30) {
            iter = a.erase(iter);
        }
        else {
            ++iter;
        }
    }

56) C++ multiset, 如果参数是iterator,只删除对应iterator的元素;如果参数是值,删掉所有等于该值的元素。

// erasing from multiset
#include
#include

int main ()
{
  std::multiset mymultiset;
  std::multiset::iterator it;

  // insert some values:
  mymultiset.insert (40);                            // 40
  for (int i=1; i<7; i++) mymultiset.insert(i*10);   // 10 20 30 40 40 50 60

  it=mymultiset.find (40);
  mymultiset.erase (it); 

  std::cout << "mymultiset contains:";
  for (it=mymultiset.begin(); it!=mymultiset.end(); ++it)
    std::cout << ' ' << *it;
  std::cout << '\n';

  return 0;
}

结果为:mymultiset contains: 10 30 40 50 60 

把代码改成:
 

int main ()
{
  std::multiset mymultiset;
  std::multiset::iterator it;

  // insert some values:
  mymultiset.insert (40);                            // 40
  for (int i=1; i<7; i++) mymultiset.insert(i*10);   // 10 20 30 40 40 50 60

  //it=mymultiset.find (40);
  mymultiset.erase (40); 

  std::cout << "mymultiset contains:";
  for (it=mymultiset.begin(); it!=mymultiset.end(); ++it)
    std::cout << ' ' << *it;
  std::cout << '\n';

  return 0;
}

结果为:mymultiset contains: 10 20 30 50 60  

57) *m.rbegin(); 可返回set/multiset最大值。也可以用*(m.end()-1)。

但注意,multiset的erase()支持iterator参数,但不支持reverse iterator rbegin(),也不支持m.end()-1这种操作。

58) pair的初始话的方法有
        pair node = make_pair(“abc”, 3);
        pair node("abc", 3);
         pair node = {"abc", 3};

即很多场合make_pair(x, y)可以直接用{x,y}代替。

59) 
a) 一维和二维vector都有iterator,一维vector的iterator就好比指向每个元素的指针,二维vector的iterator就好比指向其中每个一维vector的指针。
b) 空的一维和二维vector的begin()和end()相等。
c) *(iter++)先返回\*iter,然后iter++。
跟*(i++)一回事。
d) iter像指针,但不能与指针比较。一个未初始化的iter不能与NULL比较。如果非要初始化,可将其初始化为对应container的end()。
即vector a = {1,2,3,4,5};
vector::iterator iter = a.end();

60) vector a的front()相当于a[0], back()相当于a[a.size()-1];

    vector a = {1,2,3};
    cout<

61) 关于set的一个大坑。

当set元素为结构体时,必须要重载<,即结构体之间的排序以某字段为准。但此时切记set就只认该字段了,即如果两个节点,该字段一样,但其他字段不一样,set就认为它们是一个节点!

#include 
#include 

using namespace std;

struct Node {
    int value;
    int index;
    Node (int v = 0, int id = 0) : value(v), index(id) {}
    bool operator < (const Node & n) const {
        return index < n.index;
    }
};

int main()
{
    set s;
    s.insert(Node(9, -1));
    s.insert(Node(3, 0));
    s.insert(Node(15, 0));
    s.insert(Node(20, 1));
    s.insert(Node(7, 2));
                
    cout<<"s.size()="<

62) 只有当某个函数是class里面的函数时,后面才能加const

int distance (const Point & a, const Point & b)  {
    return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y);

写成下面就错了。

int distance (const Point & a, const Point & b) const {
    return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y);

63) 下面的针对奇数偶数的不同处理

int thresh = (n & 0x1) ? n / 2 + 1 : n / 2;

可以简化为

int thresh = (n + 1) / 2;


64) queue的初始化必须是一个container,诸如list。比如下面的queue初始化必须3个{}。
queue> q{{{3, 4}}};

下面这个方式也可以:
queue> q({{3, 4}});
 

但下面两种方式都不对:
queue> q({3, 4});

queue> q{{3, 4}};

65) 以下代码在Online C++ Compiler - online editor


#include 
#include 
using namespace std;

int main()
{
    cout<<"int size="<     string sol = string() + a; //正确!

也可以string sol = string(1, a);

67) 数组离散化例子,用来将类似将[3,2,100000]的稀疏数组简化为[2,1,3]
        vector sortedA = A;
        sort(sortedA.begin(), sortedA.end());
        uniqLen = unique(sortedA.begin(), sortedA.end()) - sortedA.begin();
        //discrete A[]
        for (int i = 0; i < A.size(); ++i) {
            A[i] = lower_bound(sortedA.begin(), sortedA.begin() + uniqLen, A[i]) - sortedA.begin() + 1;
        }

68) c=(KT-1)/100+1 是表示不小于KT/100的最小整数。比如说KT=33, 那么KT/100=0, c=1。KT=1, KT/100=0, c=1。
那么为何不直接用KT/100+1呢?因为如果KT/100刚好为1的话,那么c=2,但实际上c应该等于1。

69) 对pair重定义operator < 如下:
 

bool operator < (const pair & a, const pair & b) {
        cout<<"compare "<<"("< b.second;
    return a.first < b.first;
}

然后sort数组如下

        vector> nums;
        //nums[] initialization
        sort(nums.begin(), nums.end());

上面的sort()里面根本不会调用重载的operator<,sort()还是用的pair的defult的operator <。 原因应该是pair是C++内置类型,不能对内置类型进行操作符重载。

写成下面这样倒是可以,

struct compare {
    bool operator () (const pair & a, const pair & b) {
        if (a.first == b.first) return a.second > b.second;
        return a.first < b.first;
    }
}cmp;
sort(nums.begin(), nums.end(), cmp);

或者重定义一个struct Node来搞也可以。

struct Node {
    int x;
    int y;
    Node(int _x, int _y) : x(_x), y(_y) {}
};

bool operator < (const Node & a, const Node & b) {
    cout<<"compare "<<"("< b.y;
    return a.x < b.x;
}

int main()
{
    vector nodes;
    nodes.push_back(Node(4,5));
    nodes.push_back(Node(4,6));
    sort(nodes.begin(), nodes.end());
    return 0;
}

70) 科学计数法1E+6 必须保存为double型,不能是integer或long long。

为什么呢?想想1E-6必须是double型就知道了。

71) C++的引用内部实现可能是用const ptr来实现的。

int & r = i;

在背后的实现可能为

int * const ptr = &i;

然后
r=9;
等价于
*ptr = 9;

详见通过引用实现C++多态 - 屠戮者 - 博客园

72) 注意atoi和stoi的区别。atoi是C语言用的,因为C语言不知道string,所以atoi的参数如果是string类型必须还要加个.basic_string()将其转换成C语言的字符串。stoi可以直接用于C++的string。

73)

struct compare{
    bool operator()(node a, node b){ 
        return a.key > b.key; 
    } 
} cmp; 
priority_queue, compare> q;
sort(nums.begin(), nums.end(), cmp);

如果是sort,需要用类实例cmp。
如果是priority_queue,需要用类名compare。

74) 2的n次方用1<

75) 对比C++的array和vector的用法

int main() {
    int a[10];
    int b[10] = {0};
    int c[] = {1, 2, 3, 4, 5};
    int x = c[2];
    c[3] = x;
    return 0;
}

对应于
 

int main() {
   vector a(10);               // int a[10];
   vector b(10, 0);            // int b[10] = {0};
   vector c = {1, 2, 3, 4, 5}; // int c[] = {1, 2, 3, 4, 5};
   int x = c[2];                    // int x = c[2];
   c[3] = x;                        // c[3] = x;
   return 0;
}

76) 寻找数组a是否有在数组b中的元素。可以用find_first_of()。
vector a = {1, 2, 3, 4, 5};
vector b = {3, 4, 5, 6, 7};
auto it = find_first_of(begin(a), end(a), begin(b), end(b));
bool found = (it != end(a));

上面的例子中*it会返回3。

77) C 语言printf格式占位符
有符号整数型: %d或%i, example: -392
字符型: %c 
无符号整数型: %u, example: 392
浮点数: %f或%F, example: 39.2
科学计数法浮点数: %e或%E, example, 3.9265e+2
实数(不显示无意义0): %g或%G, example, 39.2 和 1.2e+02
内存地址: %p, example: b8000000
字符串 %s 

78) scanf的格式:
程序将会接受的一组整数是由空格分隔开的,我们无需在 scanf 中将描述输入格式的第一个参数写成"%d %d" 或者 "%d %d\n",而只需要写 "%d%d" 即可。这是因为 scanf 在处理输入时,如果格式占位符不是用于读入单个字符的 %c,它就会将空白字符(空格符、制表符和换行符)都视为一次输入的终止标记。 例如,当我们输入形如 3 5 这样的输入时,"%d%d" 会先用第一个 %d 匹配到 3,遭遇到一个空白字符(空格符)后,第二个 %d 会再匹配到 5,完成两个数字的读入。如果,我们的输入不是 3 5 而是先输入 3,回车后再输入 5,最终完成的输入效果也会是一样的(因为回车带来的换行符 \n 也是一个等效的空白字符)。

79) 另外,如果我们在数组声明时进行了初始化,我们则可以不在方括号内说明数组的长度。也就是说,int b[2] = {5, 8};可以被我们简写为int b[] = {5, 8}。

80) 

1. 十进制。如56。 
2. 十六进制,以0x开头,比如0x7a。输出十六进制hex关键字格式化,如cout< 3. 八进制,以0开头,比如030。输出八进制用oct关键字格式化,如cout<

b=010;//八进制

cout<<"b="<

//输出8

C/C++中二进制是不直接支持的。想输入二进制,可以用bitset进行转换输出。如下示例程序:

#include

#include

int main()

{

    int c = -1;

    std::bitset a(c);

    std::cout << a << "\n";

    return 0;

}

 81)下面两种写法的输出结果没有什么区别。 但是,它们的工作原理是不同的,char *string2 = "Hello";的写法实际上是在string2这个变量中保存了"Hello"这个字符串字面量在程序运行时在内存中的地址。 同时,请注意,因为string2指向的"Hello"是一个字符串常量,所以我们没有办法直接通过string2来对字符串做修改。 而string1我们可以直接修改。

#include 

int main() {
    char string[] = "Hello";
    printf("%s\n", string);
    string[0] = 'w';
    printf("%s\n", string);  //输出Wello
    char *string2 = "Hello";
    printf("%s\n", string2);
    string2[0] = 'w';   //出错!
    printf("%s\n", string2);    
    return 0;
}
#include 

int main() {
    char string[] = "Hello";
    printf("%s\n", string);
    char *string2 = "Hello";
    printf("%s\n", string2);
    printf("%p\n", &string);
    printf("%p\n", &string2);
    printf("%p\n", string);
    printf("%p\n", string2);
    printf("%p\n", &"Hello");
    
    return 0;
}

输出结果

Hello                                                                                                                 
Hello                                                                                                                 
0x7ffd196fca82     //printf("%p\n", &string);                                                                                                   
0x7ffd196fca78     //printf("%p\n", &string2);  //指针的地址,二级指针                                                                                                 
0x7ffd196fca82     //printf("%p\n", string);                                                                                                   
0x400754           //printf("%p\n", string2);   //指针的值,即地址                                                                                                
0x400754           //printf("%p\n", &"Hello");  //string2所指字符串常量的地址


注意:
string的地址是内存栈区的地址,string2直接关联到"Hello"字符串常量在内存中管理字符串常量的那个位置。
这里string和string2这2个变量都分配在栈内存上,所以&string的地址(0x7ffd196fca82)和&string2的地址(0x7ffd196fca78)比较接近。

我的理解是string是一个变量,所以printf("%p\n", &string)和printf("%p\n", string)的结果是一样的。
而string2实际上是指针,它指向内存中存储常量字符串的那个地址(0x400754)。

下面是一个参考程序

#include 
  
int main()
{
    int a = 3;
    int *p = &a;
    printf("%p\n", a);
    printf("%p\n", &a);
    printf("%p\n", p);
    printf("%p\n", *p);
}

输出为
0x3
0x7ffe2c239afc
0x7ffe2c239afc
0x3
可见对于非指针变量a,printf("%p\n", a)的结果就是a的值, printf("%p\n", &a)是a的地址。
对于指针变量p, printf("%p\n", p)的结果是p的值,即p所指向的地址。printf("%p\n", *p)的结果是p所指向的那个地址所含变量的值。

总结:
指针为何不能修改其指向的常量字符串_wxywxywxy110的博客-CSDN博客

指针指向常量字符串(位于常量存储区),常量字符串的内容是不可以被修改的,企图修改常量字符串的内容而导致运行错误。所以这个问题出现的原因是char*str=”abcdefg”,赋值的是字符串常量,存储在常量存储区,而常量存储区的内容是无法修改的。

如果使用数组来代替的话,数据就存储在堆栈空间,堆栈空间的内容是可以修改的,就不会出现运行时错误。

程序的内存分配:
1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其
操作方式类似于数据结构中的栈。
2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回
收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。
3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的
全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另
一块区域。 - 程序结束后由系统释放。
4、文字常量区 —常量字符串就是放在这里的。 程序结束后由系统释放
5、程序代码区—存放函数体的二进制代码。

上代码:

//main.c 
int a = 0; 全局初始化区  
char *p1; 全局未初始化区  
main()  
{  
int b; 栈  
char s[] = "abc"; 栈  
char *p2; 栈  
char *p3 = "123456"; 123456\0在常量区,p3在栈上。  
static int c =0; 全局(静态)初始化区  
p1 = (char *)malloc(10);  
p2 = (char *)malloc(20);  
分配得来得10和20字节的区域就在堆区。  

}  

82) calloc和 malloc的不同:
calloc函数申请的内存空间是经过初始化的,全部被设成了0,而不是像malloc所申请的空间那样都是未经初始化的。 calloc函数适合为数组申请空间,我们可以将第二个参数设置为数组元素的空间大小,将第一个参数设置为数组的元素数量。

83) 带有空格的输入,可以使用 scanf 读入时可以逐字符读入,第一个参数使用 "%c",每行读入以 \n 字符被读入来判断结束。对于是否还有新的行没有读入的情况,可以用: while (scanf(/* 这部分省略*/) != EOF) { ... } 的方式进行。

example:

    char names[20][100];
    char c;
    scanf("%d\n", &n);
    for (int i = 0; i < n; ++i) {
        j = 0;
        //读取一个字符,直到是\n或者是EOF停止, 等价于 scanf("*[^\n]");
        while((c = getchar()) != '\n' && c != EOF) { 
             names[i][j++] = c;
        }
        names[i][j] = '\n';
    } 

下面程序实现一个读入有空格的字符串并print出来。其中scanf("%[^\n]s", str)) != EOF) {   //除了换行符号\n之外的所有字符都允许输入,包括空格。比如读入"Hello World",它会一直读到Hello World之后的那个\n换行符,然后打印出来。注意这里需要一个

getchar()吃掉那个\n换行符,不然while循环会卡在\n换行符这里陷入死循环,因为\n != EOF。


  1 #include 
  2 
  3 int main() {
  4     char str[100] = {0};
  5     int ret;
  6     while ((ret = scanf("%[^\n]s", str)) != EOF) {   //除了换行符号\n之外的所有字符都允许输入,包括空格。
  7         getchar();
  8         printf("%s %d\n", str, ret);
  9     }
 10 
 11     return 0;
 12 }

下面程序实现输入一个带空格的字符串并将其包含字符数打印出来。因为printf函数的返回值就是打印的字符数。

#include 
 
int main() {
     char str[100] = {0};
     while (scanf("%[^\n]s", str) != EOF) {   //除了换行符号\n之外的所有字符都允许输入,包括空格。
     getchar();
     printf(" has %d chars!\n", printf("%s", str));
     }
  
     return 0;
}

//注意上面的while也可以写成
    while (~scanf("%[^\n]s", str)) {  

printf("%g\n", x)可以输出浮点数x的有效位数,不用补0。而printf("%lf\n", x)会补0,printf("%?d\n", n)也会补0。

#include 
 
int main()
{
    int n = 23;
    double x = 2.3;
    printf("%g\n", x);
    printf("%lf\n", x);
    printf("%4d\n", n);
    return 0;
}

输出结果为

2.3
2.300000
  23

84) 注意C语言里面EOF是文件结束符,'\0'是字符串结束符,'\n'是换行符。它们的值都是整型。
 

int main()
{
    printf("%d\n", '\0');
    printf("%d\n", '\n');
    printf("%d\n", EOF);

    return 0;
}

输出结果为

0     =>'\0'
10    =>'\n'
-1    =>EOF

85) 在语法上,构造函数具有这样的性质:
1) 函数名与类名完全相同
2) 不能定义返回值类型,也不能有return语句
3)可以有形参,也可以没有形参,可以带有默认参数
4)可以重载。

我们在定义构造函数的时候,可以有参数表,也可以让参数表空着——同样,即使是一个构造函数有参数,我们也可以给它的所有参数都设置一个默认值。这样的构造函数,称为默认构造函数。同一个类中不能出现两个默认构造函数。下面的代码编译器会报错

class Clock{
public:
    Clock();
    Clock(int newH = 0, int newM = 0, int newS = 0);
};

86) 如果我们定义一个类的时候,不声明任何构造函数,那么编译器在编译的时候,就会为我们自动生成一个默认构造函数,它具有这样的特点:
1) 参数列表为空,不为数据成员赋初值
2) 如果类内定义了成员的初始值,则使用内类定义的初始值
3) 如果没有定义类内的初始值,则以默认方式初始化
4) 基本类型的数据默认初始化的值是不确定的(类似于我们在主函数中声明一个类却不赋初始值的情况)
简而言之,这样一个构造函数,它的特点就是“什么都不做”,单纯只是创建一个类而已——相当于这样的一个形式:
 Clock(){}
需要注意的是,如果我们在类中已经定义了一个构造函数(可以是任意形式)的话,那么编译器就不会再为我们定义默认构造函数了——这个时候,如果我们需要使用到默认构造函数的话,不要忘记自己再定义它。

87)关于构造函数的选择题:答案是1)4)6)
1) 如果没有需要的话,构造函数可以什么都不做
2) 构造函数的返回值类型为void
3) C++ 语言中,空的默认构造函数会把成员变量初始化为0
4)同一个类可以重载多个不同参数列表的构造函数
5)已经定义了其他构造函数的话,编译器仍然会为我们提供一个默认构造函数
6)如果不写构造函数的话编译器会为我们自动生成一个

88) C99后有int8_t, int16_t, int32_t, int64_t等类型。其打印方法如下 (以int32_t为例,用PRId32宏):

#include 
#include 
int main() {
    int32_t a = 70000;
    printf("%s\n", PRId32);
    printf("%s\n", PRId64);
    printf("%s\n", PRId16);
    printf("%s\n", PRId8);
    printf("%" PRId32 "\n", a);
    printf("INT8_MIN = " "%" PRId8 " INT8_MAX=" "%" PRId8 "\n", INT8_MIN, INT8_MAX);
    printf("INT16_MIN = " "%" PRId16 " INT16_MAX=" "%" PRId16 "\n", INT16_MIN, INT16_MAX);
    printf("INT32_MIN = " "%" PRId32 " INT32_MAX=" "%" PRId32 "\n", INT32_MIN, INT32_MAX);
    printf("INT64_MIN = " "%" PRId64 " INT64_MAX=" "%" PRId65 "\n", INT64_MIN, INT64_MAX);
    return 0;
}

89) For语句
for (初始化;循环条件;执行后操作) {
    代码块;
}
内部执行如下:
Step 1: 初始化
Step 2: 循环条件判断
Step 3: 执行代码块
Step 4: 执行后操作
Step 5: 跳转到Step2

90) C 语言里面只要condition非0就是true, 0就是false。

  1 #include 
  2 
  3 int main() {
  4     int a = 3, b = -1, c = 0, d = 1;
  5     if (a) printf("a is true.\n");
  6     else printf("a is false.\n");
  7 
  8     if (b) printf("b is true.\n");
  9     else printf("b is false.\n");
 10 
 11     if (c) printf("c is true.\n");
 12     else printf("c is false.\n");
 13 
 14     if (d) printf("d is true.\n");
 15     else printf("d is false.\n");
 16     return 0;
 17 }
 18 

输出结果为
a is true
b is true
c is false
d is true
对于a=3, b=-1这样的不标准的true condition, 怎么把他们标准化呢?答案就是!!(a), !!(b)。
这里!!(a)=1, !!(b)=1。

91) Unix系统里,每行结尾只有"<换行>",即"\n";Windows系统里面,每行结尾是"<回车><换行>",即"\r\n";Mac系统里,每行结尾是"<回车>"。一个直接后果是,Unix/Mac系统下的文件在Windows里打开的话,所有文字会变成一行;而Windows里的文件在Unix/Mac下打开的话,在每行的结尾可能会多出一个^M符号。
详见 回车和换行 - 阮一峰的网络日志

92) memset函数是按byte来设置。
把a指针开头的地址连续n个字节的对应bit全部置1。不能用
memset(a, 1, n)
而必须用
memset(a, -1, n)
因为1对应0b00...1,而-1对应0b11...1。

93) 如何编写一个打印日志宏? 

#define log(frm, argc...) {\
    printf("[%s : %d] ", __func__, __LINE__);\
    printf(frm, ##argc);\
    printf("\n");\
}

94) 如何编写一个打印结构体offset宏?

#define offset(T, a) (long)(&(((T*)(NULL))->a))

struct Data {
    int a;
    double b;
    char c;

};

95) g++ -E a.cpp  可以显示宏展开的结果。
96) #define和typedef并不等价。
比如

#define ppchar char *
typedef char * pchar;

pchar p1, p2;
ppchar p3, p4; //p4 is defined as char

97) fprintf, sprintf和printf区别。

fprintf向文件输出, fp为文件指针

fprintf(fp,"%s",name);

sprintf向字符串buffer输出, buff为字符数组

sprintf(buff,"%s",name);

printf是标准输出流(stdout)的输出函数,向屏幕标准设备输出,

printf("%s", name);

相当于:

fprintf(stdout,"%s",name);

98) 关于printf打印字符串的一些格式:

#include 

int main() {
    char a[100] = "abcdefg";
    printf("%5s1\n", a);
    printf("%15s1\n", a);
    printf("%.2s1\n", a);
    printf("%10.2s1\n", a);
    
    return 0;
}

输出为:

abcdefg1
        abcdefg1
ab1
        ab1

99) C的main函数的argc和argv都包括./a.out
./a.out -a -l abc xyz
argc=5, argv[0]="./a.out", argv[1]="-a", argv[2]="-l", argv[3]="abc", argv[4]="xyz"

100) C的printf函数里面有表达式或函数的话,结合顺序通常是从右往左。

printf("pop %d from the Queue = %d\n", front(q), pop(q));
由于printf的执行顺序是从右到左(至少在我的机器上是这样),所以pop(q)先执行,使得head已经加1,这样打印front(q)的时候实际上是queue的新head,而不是pop掉的那个数。

101) 如何将一个文件或标准输入中的字符串(空格隔开)输入给一个字符数组?
    string name[6005];
    while (cin >> name[n]) {
        n++;
    }

102) 数组指针和指针数组的区别
原链接在 数组指针和指针数组的区别 - hongcha_717 - 博客园

数组指针(也称行指针)
定义 int (*p)[n];
()优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n,也可以说是p的步长。也就是说执行p+1时,p要跨过n个整型数据的长度。

如要将二维数组赋给一指针,应这样赋值:
int a[3][4];
int (*p)[4]; //该语句是定义一个数组指针,指向含4个元素的一维数组。
 p=a;        //将该二维数组的首地址赋给p,也就是a[0]或&a[0][0]
 p++;       //该语句执行过后,也就是p=p+1;p跨过行a[0][]指向了行a[1][]

所以数组指针也称指向一维数组的指针,亦称行指针。

指针数组
定义 int *p[n];
[]优先级高,先与p结合成为一个数组,再由int*说明这是一个整型指针数组,它有n个指针类型的数组元素。这里执行p+1是错误的,这样赋值也是错误的:p=a;因为p是个不可知的表示,只存在p[0]、p[1]、p[2]...p[n-1],而且它们分别是指针变量可以用来存放变量地址。但可以这样 *p=a; 这里*p表示指针数组第一个元素的值,a的首地址的值。
如要将二维数组赋给一指针数组:
int *p[3];
int a[3][4];
for(i=0;i<3;i++)
p[i]=a[i];
这里int *p[3] 表示一个一维数组内存放着三个指针变量,分别是p[0]、p[1]、p[2]
所以要分别赋值。

这样两者的区别就豁然开朗了,数组指针只是一个指针变量,似乎是C语言里专门用来指向二维数组的,它占有内存中一个指针的存储空间。指针数组是多个指针变量,以数组形式存在内存当中,占有多个指针的存储空间。
还需要说明的一点就是,同时用来指向二维数组时,其引用和用数组名引用都是一样的。
比如要表示数组中i行j列一个元素:
*(p[i]+j)、*(*(p+i)+j)、(*(p+i))[j]、p[i][j]

优先级:()>[]>*

103) 同一平台下指针大小都相同,不同类型的指针步长不同;比如PC x86上是4字节,x64上是8字节, 不过这个结论不包括void* (实践中void *的大小在x64上也是8)。


#include 

using namespace std;

int main()
{
    cout<<"sizeof(void *)"<

在64位机下上面显示结果全为8。

104)
n & 1 等价于 n % 2
n & 3 等价于 n % 4
n & 7 等价于 n % 8
n & (2^i - 1) 等价于 n % (2^i)

105) 如何读入有空格的字符串?
scanf("%[^\n]s", str);

106) 如何舍掉小数点后2位?不能用printf因为会四舍五入!
    12.12345 => 12.12
    方法1: 12.12345 * 100 = 1212.345, 1212.345/100.0 = 12.12
    double x = 12.12345
    printf("%.2f\n", (int)(l * 100) / 100.0);
    方法2:12.12345 - 0.005 = 12.11845,再printf%.2f

107) 关于sort函数里面的cmp函数的写法:
参考链接
http://www.cplusplus.com/reference/algorithm/sort/

sort函数对于cmp函数的特殊要求"strict weak ordering"。
a) 反自反性,也就是cmp(x,x)必须返回false
b) 非对称性,也就是cmp(x,y)和cmp(y,x)结果必须相反
c) 可传递性,也就是cmp(x,y)=true, cmp(y,z)=true, 则cmp(x,z)=true.

所以,下面的写法可能有问题,因为违反了a)b)

bool cmp(Node a, Node b) {
    return a.val <= b.val;
}

应该写成如下:
 

bool cmp(Node a, Node b) {
    if (a.val == b.val) return a.val2 > b.val2;
    return a.val < b.val;
}

108) 几种根据输入来自动截止的方法:
1)


struct node {
    char s[4];
    int num; //flag 表示直接清空,间接清空还是没清空
};

node b[15];

//如果
for (int i = 0; cin >> b[i].s; i++) {
   ...
}

2)用getchar(), 它的作用是从stdin流中读入一个字符,getchar()函数等待输入直到按回车才结束(前提是缓冲区没有数据),回车前的所有输入字符都会逐个显示在屏幕上。但只有第一个字符作为函数的返回值。

char c;
while((c=getchar())!='\n')     //每个getchar()依次读入一个字符
    printf("%c",c);            //按照原样输出

3) 在 Windows 系统中,通过键盘输入时,按 Ctrl+Z 组合键后再按回车键,就代表输入结束。
在 UNIX/Linux/Mac OS 系统中,Ctrl+D 代表输入结束。

#include 
using namespace std;
int main()
{
    int n;
    int maxN = 0;
    while (cin >> n){  //输入没有结束,cin 就返回 true,条件就为真
        if (maxN < n)
            maxN = n;
    }
    cout << maxN <

在windows中输入数据后, 然后在按下 Ctrl+Z 组合键(可以在当前行,也可以在新的一行),接着按下回车键,输入就结束了,此时 cin 返回 false,循环结束,得到了最大值。

  109)  如果char数组作为函数参数,求其长度可以用strlen,但必须定义i为size_t,而不是int。
int hash(HashTable *h, char value[]) {
    int code = 0;
    for (size_t i = 0; i < strlen(value); i++)

110) strstr()是返回原字符串的一个字串,注意它不会重新分配一个字串。

/* strstr example */
#include 
#include 

int main ()
{
  char str[] ="This is a simple string";
  char * pch;
  pch = strstr (str,"simple");
  puts(pch);
  if (pch != NULL)
    strncpy (pch,"sample",6);
  puts (str);
  return 0;
}

111) 关于C字符串以下写法等价:

    char str1[5] = {'g', 'e', 'e' ,'k' , '\0'}; //注意着一些要加后面的'\0'
    char str2[5] = "geek";
    char str3[] = "geek";

那char *str4 = "toad"跟它们有什么区别呢?
下面表格引用自What's difference between char s[] and char *s in C? - GeeksforGeeks

C/C++ 一些知识点总结_第1张图片

下面的程序

#include 
#include 
using namespace std;

int main()
{
    char str1[6] = {'t', 'o', 'a' ,'d' , 's', '\0'}; //注意着一些要加后面的'\0'
    char str2[6] = "toads";
    char str3[] = "toads";
    cout <

输出结果为

main.cpp:20:18: warning: ISO C++ forbids converting a string constant to ‘char*’ [-Wwrite-strings]                                                                                 
main.cpp:22:18: warning: ISO C++ forbids converting a string constant to ‘char*’ [-Wwrite-strings]                                                                                 
666                                                                                                                                                                                
555                                                                                                                                                                                
8 6                                                                                                                                                                                
8 1                                                                                                                                                                                
8 6                                                                                                                                                                                
8 1 

注意在64位机器上指针长度为8。//上面表格中显示sizeof(p)长度是4, 因为是32位机器

111) cout << "some"[2] << endl;    //o
    cout << *("some" + 3) << endl;  //e

112) C++大括号优化
 

#include 
using namespace std;

class A {
public:
    A() : data{new int[10]{3,4,5,6}} {}
    int operator[](int x) {return data[x];}
private:
    int* data;
};

int main()
{
    A a;
    cout << a[3] << endl;
    return 0;
}

113) cout设置精度
参考关于C++的cout输出小数的精度控制_杨领well的专栏-CSDN博客_cout 精度

保留小数点后**位数

cout.setf(ios::right); // 设置对齐方式
cout.width(8); //设置输出宽度
cout.fill('0'); //将多余的空格用0填充
 
cout.flags(ios::fixed);
cout.precision(4); //设置输出精度,

114) 两个object大小不一样,但类型一样,也可以用swap

#include 
#include 
using namespace std;

class A {
public:
    vector arr;
};

int main() {
    A a;
    A b;
    a.arr = {1, 5};
    b.arr = {2, 7, 8, 9};
    swap(a, b);
    cout << a.arr[3] << endl;
    return 0;

}

输出9

115) signed 和 unsigned 相加,返回unsigned。

116) C++ 的类成员函数参数的默认值是在声明里赋值,还是在实现里面赋值?
只能是类的声明里面赋值!
下面这个链接讲的不错。
C++类/函数默认参数_VioletHan7的博客-CSDN博客_c++ 成员函数默认参数
而如果不是类的成员函数,即普通函数的话,函数参数的默认值可以是在声明里面,也可以是在实现里面。

117)
 


#include 

using namespace std;

struct C 
{ 
 double t;   //8   1111 1111
 char b;  //1      1
 int a;   //4      0001111  
 short c;  //2     11000000
};  
 

struct D 
{ 
 double t;   //8   1111 1111
 char b;  //1      1
 short c;  //2     11000000
 int a;   //4      0001111  
};  

int main()
{
    cout << "sizeof(char *) = " << sizeof(char *) << endl;
    cout << "sizeof(C)=" << sizeof(C) << endl; // = 24;  //注意:1 4 2 不能拼在一起
    cout << "sizeof(D)=" << sizeof(D) << endl; // = 24;  //注意:1 4 2 不能拼在一起
    return 0;
}

 64位编译,结果为:
sizeof(char *) = 8
sizeof(C)=24
sizeof(D)=16

118) 注意x=+a 和 x+=a的区别
    int x=10, a=-3;
    x=+a;
    cout << x << endl;   // x=-3

119) C++的内部类型和stl自带类型比如int, string,不能重载它们的类内的操作符,但是可以做类似这样
struct cmp {
    bool operator() (const string &a, const string &b) {
    ...
}
};

sort(words2.begin(), words2.end(), cmp());

的重载。

120. 对齐宏,参考的Linux Loader script 里面的ALIGN()宏。仅限于2的幂次方对齐。

#define ALIGN(X, Y) ((X + Y - 1) & ~(Y - 1))

int main()
{
    //(. + exp - 1) & ~(exp - 1)
    
    printf("ALIGN(9, 2) = %d\n", ALIGN(9, 2));
    printf("ALIGN(19, 2) = %d\n", ALIGN(19, 2));
    printf("ALIGN(22, 8) = %d\n", ALIGN(22, 8));
    printf("ALIGN(26, 4) = %d\n", ALIGN(26, 4));
    printf("ALIGN(26, 8) = %d\n", ALIGN(26, 8));
    printf("ALIGN(26, 16) = %d\n", ALIGN(26, 16));
    return 0;
}

结果如下:
ALIGN(9, 2) = 10
ALIGN(19, 2) = 20
ALIGN(22, 8) = 24
ALIGN(26, 4) = 28
ALIGN(26, 8) = 32
ALIGN(26, 16) = 32

121. 又看了一下指针。数组名和函数名本身就是指针,在前面加上&也还是指针,其值不变。
 

#include 
#include 

int func(int x) {
    return x * 2;
}

int main() {
    int a[10] = {7, 8, 9};
    printf("a = %p, &a = %p\n", a, &a);

    printf("func = %p, &func = %p\n", func, &func);;
    return 0;
}

结果如下:
 

a = 0x7ffe5726b030, &a = 0x7ffe5726b030
func = 0x55e8794e4169, &func = 0x55e8794e4169

你可能感兴趣的:(C++,C,c++)