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<string> vs={ "first", "second", "third"};    
map<string,string> singers ={ {"Lady Gaga", "+1 (212) 555-7890"},{"Beyonce Knowles", "+1 (212) 555-0987"}}; 

下面这两种方法都可以

vector<int> result = {2, 3, 7};

vector<int> result;

result = {2,3,7};

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

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

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

vector<int> 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<int> ilist5(7,3);

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

再举个例子
    vector<int> old_one(4);         // std::vector variable with elements 0, 0, 0, 0
    vector<int> old_two(4, 2);      // std::vector variable with elements 2, 2, 2, 2
    vector<int> uni_one{4};         // std::vector variable with elements 4
    vector<int> uni_two{4, 2};      // std::vector variable with elements 4, 2

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

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

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

返回一个空vector:

方法1: return vector<int>();

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

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

举例如下:

    vector<vector<string>> groupAnagrams(vector<string> &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<<sizeof(c)<<endl;    //2
     cout<<sizeof(c)/sizeof(c[0])<<endl;   //2
     cout<<strlen(c)<<endl;     //1

     char d[]="";
     cout<<sizeof(d)<<endl;   //1
     cout<<sizeof(d)/sizeof(d[0])<<endl;   //1
     cout<<strlen(d)<<endl;   //0
 

33) 关于for循环:

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

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

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<int> 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. 注意 :sort的第3个参数可以是struct或class的instance, 也可以是函数指针。
priority_queue的第3个参数只能是struct或class的定义。因为priority_queues是模板! 

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<Node,vector<Node>,cmp> q;

B. 

map<int, int> freqs; //<num, freq>
int n = nums.size();
for (int i = 0; i < n; i++) {
    freqs[nums[i]]++;
}
auto comp = [&](int a, int b){return freqs[a] > freqs[b];};
priority_queue<int, vector<int>, decltype(comp)> minHeap(comp);

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<T> v1          

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

vector<T> v2(v1)       

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

vector<T> v2 = v1       

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

vector<T> v3(n, val)      

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

vector<T> v4(n)          

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

vector<T> v5{a,b,c...}

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

vector<T> v5={a,b,c...}

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

42) stringstream tokenize 模板:

只用空格来隔开:

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

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

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

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

vector<int> nums;
nums[0] = 3;

44) C++ double_min 的定义是

#include <limits>

constexpr double lowest_double = std::numeric_limits<double>::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<char,int> mymm;

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

  std::cout << "mymm contains:\n";
  for (char ch='a'; ch<='d'; ch++)
  {
    std::pair <std::multimap<char,int>::iterator, std::multimap<char,int>::iterator> ret;
    ret = mymm.equal_range(ch);
    std::cout << ch << " =>";
    for (std::multimap<char,int>::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 

注意: lower_bound() 和 upper_bound() 都是返回指针。

结果: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<int>(v.begin() + i, v.end()));
v2 = vector<int>(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.1) 关于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 <cmath>。
3) 为什么是floor()+1,而不是ceil()呢?
一个例子是log10(100)=2,但是ceil()后还是2。

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

即下面的代码为错:

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

注意:这里可能不对。有时候也可以用auto遍历unordered_map。那什么时候可以,什么时候不可以呢?这里我觉得遍历是可以的,但它不是按大小顺序遍历,而是按hash 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 <iostream>
#include <set>

int main ()
{
  std::multiset<int> mymultiset;
  std::multiset<int>::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<int> mymultiset;
  std::multiset<int>::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<string, int> node = make_pair(“abc”, 3);
        pair<string, int> node("abc", 3);
         pair<string, int> 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<int> a = {1,2,3,4,5};
vector<int>::iterator iter = a.end();

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

    vector<int> a = {1,2,3};
    cout<<a.front()<<" "<<a.back()<<endl; //print 1 3

61) 关于set的一个大坑。

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

#include <iostream>
#include <set>

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<Node> 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()="<<s.size()<<endl;
    return 0;
}

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<pair<int, int>> q{{{3, 4}}};

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

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

queue<pair<int, int>> q{{3, 4}};

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


#include <iostream>
#include <climits>
using namespace std;

int main()
{
    cout<<"int size="<<sizeof(int)<<endl;
    cout<<"long size="<<sizeof(long)<<endl;
    cout<<"long long_size="<<sizeof(long long)<<endl;
    cout<<"INT_MAX="<<INT_MAX<<" INT_MIN="<<INT_MIN<<endl;
    cout<<"LONG_MAX="<<LONG_MAX<<" LONG_MIN="<<LONG_MIN<<endl;
    cout<<"LLONG_MAX="<<LLONG_MAX<<" LLONG_MIN="<<LLONG_MIN<<endl;

    return 0;
}

的输出为

int size=4                                                                                                            

long size=8                                                                                                           

long long_size=8                                                                                                      

INT_MAX=2147483647 INT_MIN=-2147483648                                                                                

LONG_MAX=9223372036854775807 LONG_MIN=-9223372036854775808                                                            

LLONG_MAX=9223372036854775807 LLONG_MIN=-9223372036854775808 

66) C++里面map的iterator返回的是pair<key, value>类型的指针。所以要得到key或value必须用it->first或it->second。不能用*it来得到value的值。

map<int, int> mp;

假设mp[3] = 6;

mp.find(3) is to return the iterator。 

mp.find(3)->second = 6

(*mp.find(3)).second = 6

C++里面vector的iterator返回的是vector里面具体元素的指针,所以*it就对应具体的元素值。

66) 如何把一个char字符转换成string? 

    char a = '3';
    string sol = "" + a;  //错误!
    string sol = string() + a; //正确!

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

67) 数组离散化例子,用来将类似将[3,2,100000]的稀疏数组简化为[2,1,3]
        vector<int> 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<int, int>重定义operator < 如下:
 

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

然后sort数组如下

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

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

写成下面这样倒是可以,

struct compare {
    bool operator () (const pair<int, int> & a, const pair<int, int> & 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 "<<"("<<a.x<<","<<a.y<<") ("<<b.x<<","<<b.y<<")"<<endl;
    if (a.x == b.x) return a.y > b.y;
    return a.x < b.x;
}

int main()
{
    vector<Node> 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类型必须还要加个.c_str()将其转换成C语言的字符串。stoi可以直接用于C++的string。

73)

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

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

74) 2的n次方用1<<n或pow(2,n),不能用2^n。

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<int> a(10);               // int a[10];
   vector<int> b(10, 0);            // int b[10] = {0};
   vector<int> 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<int> a = {1, 2, 3, 4, 5};
vector<int> 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<<hex<<12。
3. 八进制,以0开头,比如030。输出八进制用oct关键字格式化,如cout<<oct<<12。

b=010;//八进制

cout<<"b="<<b<<endl;

//输出8

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

#include<bitset>

#include<iostream>

int main()

{

    int c = -1;

    std::bitset<sizeof(int) * 8> a(c);

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

    return 0;

}

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

#include <stdio.h>

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 <stdio.h>

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)的结果是一样的。
char string[] = "Hello"; 实际上相当于初始化了string数组为
string[0] = 'H';
string[1] = 'e';
string[2] = 'l';
string[3] = 'l';
string[4] = 'o';
string这里是数组名,地址是在栈上,其内容'H'也是在栈上。

而string2实际上是指针,指针的地址是在栈上,指针的内容指向内存中存储常量字符串的那个地址(0x400754)。

下面是一个参考程序

#include <stdio.h>
  
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 <stdio.h>
  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 <stdio.h>
 
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 <stdio.h>
 
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 <stdio.h>
#include <inttype.h>
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 <stdio.h>
  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;

};

如何编写一个sizeof()宏?

#define _sizeof(x) (char *)(&x+1)-(char*)(&x)

下面这个方法也可以。不过C编译器要支持typeof()才行。

#define _sizeof(x) ({                              \
    typeof(x) v[2];                                \
    size_t ret = (char *)&v[1] - (char *)&v[0];    \ 
    ret;                                           \
})

如何实现container_of宏?
container_of宏通过struct结构体中的某个成员的指针(地址),从而获取指向struct结构体起始地址的指针。我们先通过offsetof()找到该成员的偏移地址,然后用该成员的地址减去这个偏移地址即可。

其实现主要的关键点如下面所述:

  • typeof关键字的使用;
  • 0地址指针的使用;
  • offsetof()如何获取传入的结构体成员的偏移地址?
     
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

#define container_of(ptr, type, member) ({                      \
        const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
        (type *)( (char *)__mptr - offsetof(type,member) );})

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 <stdio.h>

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 <iostream>

using namespace std;

int main()
{
    cout<<"sizeof(void *)"<<sizeof(void *)<<endl;
    cout<<"sizeof(char *)"<<sizeof(char *)<<endl;
    cout<<"sizeof(int *)"<<sizeof(int *)<<endl;
    cout<<"sizeof(long long *)"<<sizeof(long long *)<<endl;
    cout<<"sizeof(float *)"<<sizeof(float *)<<endl;
    cout<<"sizeof(double *)"<<sizeof(double *)<<endl;

    return 0;
}

在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 <iostream>
using namespace std;
int main()
{
    int n;
    int maxN = 0;
    while (cin >> n){  //输入没有结束,cin 就返回 true,条件就为真
        if (maxN < n)
            maxN = n;
    }
    cout << maxN <<endl;
    return 0;
}

在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 <stdio.h>
#include <string.h>

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

下面的程序

#include <iostream>
#include <cstring>
using namespace std;

int main()
{
    char str1[6] = {'t', 'o', 'a' ,'d' , 's', '\0'}; //注意着一些要加后面的'\0'
    char str2[6] = "toads";
    char str3[] = "toads";
    cout <<sizeof(str1) << sizeof(str2) << sizeof(str3) << endl; //print 666
    cout <<strlen(str1) << strlen(str2) << strlen(str3) << endl; //print 555
    char *str4 = "abcdef";   //warning: ISO C++ forbids converting a string constant to ‘char*’ [-Wwrite-strings]               
    cout <<sizeof(str4) << " " << strlen(str4) << endl;
    char *str5 = "a";  //warning: ISO C++ forbids converting a string constant to ‘char*’ [-Wwrite-strings]               
    cout <<sizeof(str5) << " " << strlen(str5) << endl;
    const char *str6 = "abcdef";           
    cout <<sizeof(str6) << " " << strlen(str6) << endl;
    const char *str7 = "a"; 
    cout <<sizeof(str7) << " " << strlen(str7) << endl;
    return 0;
}

输出结果为

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;    //m
    cout << *("some" + 3) << endl;  //e

112) C++大括号优化
 

#include <iostream>
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 <iostream>
#include <vector>
using namespace std;

class A {
public:
    vector<int> 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)
https://cloud.tencent.com/developer/article/1703257

在没有#pragma pack这个宏的声明下,C结构体对其遵循下面三个原则:

a) 第一个成员的首地址为0。

b) 每个成员的首地址是自身大小的整数倍

c) 结构体的总大小,为其成员中所含最大类型的整数倍。

/******************************************************************************

                              Online C++ Compiler.
               Code, Compile, Run and Debug C++ program online.
Write your code in this editor and press "Run" button to compile and execute it.

*******************************************************************************/

#include <iostream>

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; // = 16;
    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 <stdio.h>
#include <stdlib.h>

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

122. C的字符比较相等可以用==,字符串比较相等不可以用==,只能用strcmp
C++的字符和字符串比较相等都可以用==,因为C++可以用操作符重载定义==的函数。

123. 类里面static函数不可以是虚函数,
                   inline函数可以是虚函数,
                   friend函数不可以是虚函数,
                   构造函数不可以是虚函数,
                   析构函数可以是虚函数,甚至可以是纯虚函数。

124. C++ static 变量初始化为0,所以会放在已初始化数据段。
125. 所谓左物右指都是针对p和*p而言,不是针对p所指向的那个i。
        注意int j = c可以,但是int &j = c 不可以,这里c是 const int。

#include <iostream>
using namespace std;
int main(){
    int i = 10;
    const int *const p = &i;//指向常量int的常量指针p,指向了非常量i
    cout << "i = " << i << ",*p = " << *p << endl;
    i = 20;
    cout << "i = " << i << ",*p = " << *p << endl;
    //*p = 30;//出错
    int j = 0;
    //p = &j;//出错
    
    const int ci = i; // 正确:i的值被拷贝给了ci
    int j2 = ci; //正确 : ci的值被拷贝给了j
    // int &j3 = ci; //错误
    return 0;
}

129. const => read only,不能保证值不会变
        constexpr => const,严格保证值不会变
130. 我个人对macro和inline的理解
        1) macro和inline都会把代码展开,减少函数调用开销,所以速度会更快,但是代码会膨胀。
        2) macro是在precompile的时候代码直接展开,没有检查是否有错。inline是在compile的时候代码展开,编译器会检查是否有错,所以inline更安全。所以如果你希望代码更安全就用inline。
        那什么时候用macro呢?下面几点是我的理解。
        3)macro不管数据类型,所以可以用它来实现模板函数,这样,一个macro #define max(x,y) ((x) > (y) ? (x) : (y))就可以替换一堆inline 函数,比如max(int, int), max(double, double)等。这样在这种情况下macro代码更简洁。
        4)inline函数是会进symbol table的,macro不会,这样macro编译后的结果更小。
        5)老版本的C,好像是C99之前都不支持inline,那么用macro更有通用性。

131. 很重要 
c++ - Difference between char* and char[] - Stack Overflow在

char a[] = "string"; // the compiler puts {'s','t','r','i','n','g', 0} onto STACK 
                     // 这里a存放在stack,"string"存放在代码段(.text)
char *a = "string"; // the compiler puts just the pointer onto STACK 
                    // 这里a存放在stack,"string"存放在文字常量区(也有的说是静态存储区,但我觉得是文字常量区).

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

132.C++ 函数指针玩法

#include <iostream>

using namespace std;

int f(int  x)
{   
    cout << " x = " << x << endl; 
    return x + 3;
}

int main(void)
{   
    int (*pFunc)(int);
    void *p = (void *)addressof(f);
    printf("address of function funct() is : %p\n", p);
    pFunc = (int (*)(int))p;
    printf("address of function funct() is : %p\n", pFunc);
    pFunc(3);
    return 0;
}

133. 注意union, struct, pack的联动。
 

#include <iostream>

using namespace std;

typedef struct {
   uint16_t a;
   uint32_t b;
}__attribute__ ((packed)) AAA;

typedef struct {
   uint16_t a;
   uint32_t b;
}BBB;

union CCC {
    AAA aaa;
    BBB bbb;
};

int main()
{
    cout << sizeof(AAA) << endl;
    cout << sizeof(BBB) << endl;
    cout << sizeof(CCC) << endl;
    CCC c;
    c.aaa.a = 0x1234;
    c.aaa.b = 0xAABBCCDD;
    cout << hex << c.aaa.a << " " << c.aaa.b << endl;
    cout << hex << c.bbb.a << " " << c.bbb.b << endl;

    return 0;
}

输出结果为:
6
8
8
1234 aabbccdd
1234 aabb

注意: 实际的内存布局上  0xffff...ffaabbccdd1234
但如果把代码改成:
 

/******************************************************************************

                              Online C++ Compiler.
               Code, Compile, Run and Debug C++ program online.
Write your code in this editor and press "Run" button to compile and execute it.

*******************************************************************************/

#include <iostream>

using namespace std;

typedef struct {
   uint16_t a;
   uint32_t b;
}__attribute__ ((packed)) AAA;

typedef struct {
   uint16_t a;
   uint32_t b;
}BBB;

union CCC {
    AAA aaa;
    BBB bbb;
};

int main()
{
    cout << sizeof(AAA) << endl;
    cout << sizeof(BBB) << endl;
    cout << sizeof(CCC) << endl;
    CCC c;
    c.bbb.a = 0x1234;
    c.bbb.b = 0xAABBCCDD;
    cout << hex << c.aaa.a << " " << c.aaa.b << endl;
    cout << hex << c.bbb.a << " " << c.bbb.b << endl;

    return 0;
}

输出结果为:
6
8
8
1234 ccdd0000
1234 aabbccdd
实际内存布局为 0xff...ffaabbccdd00001234

134. 注意指针delete之后它仍然存在,只是其指向的内容变成非法了。

#include <iostream>

using namespace std;

int main()
{
    int *a = (int *)malloc(3);
    a[0] = 12; 
    a[0] = 7;
    a[0] = 89;
    delete a;
    if (a) cout << "a still exits" << endl;
    cout << a[0] << " " << a[1] << " " << a[2] << endl;
    return 0;
}

输出结果为:
a still exits
0 0 -307544048

下面的代码主要存在的问题是 clear 函数在 delete m_pCount;后并没有置指针为空(m_pCount = NULL),这样当第二次调用 clear 时,会出现问题。

class CC { 
 int* m_pCount; 
public: 
 void clear() { if ( m_pCount ) delete m_pCount; } 
 CC() { m_pCount = new int; } 
 ~CC() { clear(); } 
} 

135. 注意C++里面的string的operator +不能用来连接两个const string。
string a = "Hello World" + "aaa";
main.cpp:15:30: error: invalid operands of types ‘const char [12]’ and ‘const char [4]’ to binary ‘operator+’ 15 | string a = "Hello World" + "aaa"; | ~~~~~~~~~~~~~ ^ ~~~~~ | | | | const char [12] const char [4]

136. find_first_of("abcde")是指找到"abcde"里面的任何一个字符就行,注意不是整个。

std::string str ("Please, replace the vowels in this sentence by asterisks.");
std::size_t found = str.find_first_of("aeiou");

137. refer to Difference between char [] and char * in C? - MYCPLUS - C and C++ Programming Resources

  1. Main difference between both the statements is that s_name is an array where as p_name is a pointer type variable.
  2. The sizeof(s_name) and sizeof(p_name) are different because memory handling is different in both statements as explained above.
  3. Variable s_name and it’s address &s_name is same. where as p_name and &p_name are not same.
  4. The statement s_name=”Program” is invalid while p_name=”Program” is a invalid C statement.
  5. 如果给字符数组一个字符一个字符的赋值,最后一定要加一个'\0'。
    char a_name[] = {'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!', '\0'};
  6. p_name是指向常量字符串,不可修改。a_name和s_name可以修改。
    a_name[3] = 'G';
    s_name[3] = 'G';
    //p_name[3] = 'G';
#include <stdio.h>

int main()
{
    char a_name[] = {'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!', '\0'};
    char s_name[] = "Hello, World!"; 
    char *p_name = "Hello, World!"; 
    
    printf("The size of a_name[] is %lu\n",sizeof(a_name));
    printf("The size of s_name[] is %lu\n",sizeof(s_name));
    printf("The size of p_name[] is %lu\n",sizeof(p_name));
    
    printf("a_name[]: %p\n", a_name);
    printf("&a_name[]: %p\n", &a_name);
    
    printf("s_name[]: %p\n", (void*)s_name);
    printf("&s_name[]: %p\n", &s_name);
    
    printf("p_name[]: %p\n", (void*)p_name);
    printf("&p_name[]: %p\n", &p_name);
    
    printf("\n");
    a_name[3] = 'G';
    s_name[3] = 'G';
    //p_name[3] = 'G';    //error: cannot change a text const string
    
    printf("Print a_name[]: %s\n", a_name);
    printf("Print s_name[]: %s\n", s_name);
   // printf("Print p_name[]: %s\n", p_name);
    
    //a_name = "Program";   //error: assignment to expression with array type
    p_name = "Program";
    //s_name = "Program";   //error: assignment to expression with array type
    
    return 0;
}



138. 关于sizeof()
When using sizeof with a type, you need parentheses around the type. When using it with an expression, you don't. But you can of course include them in this case as well, and you don't have to worry about operator precedence in such case. With uncommon operators such as this one, fewer people would be sure of the precedence, so clarity certainly helps.

 

#include <stdio.h>

int main()
{
    int x = 2;
    printf("\nsizeof(x)=%ld\n", sizeof(x));
    printf("\nsizeof x=%ld\n", sizeof x);
    printf("\nsizeof(2)=%ld\n", sizeof(2));
    printf("\nsizeof 2=%ld\n", sizeof 2);
    //printf("\nsizeof int=%ld\n", sizeof int); //错误
    printf("\nsizeof(int)=%ld\n", sizeof(int));

    return 0;
}

结果是
sizeof(x)=4

sizeof x=4

sizeof(2)=4

sizeof 2=4

sizeof(int)=4

139. 关于sprintf的用法。可以保证打印出来是"0A"而不是" A".
 

#include <stdio.h>

int main()
{
    int c = 0xA;
    char buf[8];
    snprintf(buf, sizeof(buf), "%02X", c);
    printf("%s", buf);

    return 0;
}

140. 参考的
结构体后面定义一个空数组的含义_结构体空数组_流风回雪1988的博客-CSDN博客

最近在写C代码,经常看到Linux 的头文件中有的结构体后面会定义一个空数组,不知道其为何作用?经过高人指点终于明白其要点!

struct inotify_event {
   __s32 wd;
   __u32 mask;
   __u32 cookie;
   __u32 len;
   char name[0];
};
如上,结构体最后一个元素name为空数组。

这是个广泛使用的常见技巧,常用来构成缓冲区。如果你是做嵌入式开发,这种技巧应该用得漫天飞了。 比起指针用空数组有这样的优势:

1. 不需要初始化,数组名直接就是缓冲区数据的起始地址(如果存在数据)

2. 不占任何空间,指针需要占用4 byte长度空间,空数组不占任何空间,节约了空间

“这个数组不占用任何内存”,意味着在计算结构体的size时,不会计算最后一个元素,例如上面sizeof(struct inotify_event) = 16 bytes (前四个元素的内存长度)


这种空数组定义最适合制作动态buffer,因为可以这样分配空间:

malloc( sizeof(struct XXX)+ buff_len );
这样的好处是:直接就把buffer的结构体和缓冲区一块分配了,空数组其实变成了buff_len长度的数组了,一次分配省了不少麻烦。

1. 大家知道为了防止内存泄漏,如果是分两次分配(结构体和缓冲区),那么要是第二次malloc失败了,必须回滚释放第一个分配的结构体。这样带来了编码麻烦。

2. 其次,分配了第二个缓冲区以后,如果结构里面用的是指针,还要为这个指针赋值。同样,在free这个buffer的时候,用指针也要两次free。如果用空数组,所有问题一次解决

如此看来,用空数组既简化编码,又解决了内存管理问题提高了性能,何乐不为?应该广泛采用。

141. 注意用空数组和指针,数组长度不一样。

#include <stdio.h>

typedef struct {
    int a;
    int b[];
} T1;

typedef struct {
    int a;
    int *b;
} T2;

int main()
{
    printf("sizeof(T1)=%ld, sizeof(T2)=%ld", sizeof(T1), sizeof(T2));
    return 0;
}

运行结果
sizeof(T1)=4, sizeof(T2)=16。 sizeof(T2)等于16因为有padding。

141. Long C/C++ string handling.
 

#include <stdio.h>
#include <string.h>
int main()
{
 char* my_str = "Here is the first line AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA."\
"Here is the second line BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB.";

    printf("\nstrlen(my_str)=%ld\n", strlen(my_str));
    printf("\n%s", my_str);
    return 0;
}

Output:
strlen(my_str)=213

Here is the first line AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.Here is the second line BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB.

142. int或long long 在达到INT_MAX 7FFFFFFF之后,再加1就是INT_MIN 80000000。注意INT_MIN-INT_MAX结果是1!这个技巧可以用来实现timestamp int数的+1的wrap,wrap之后和之前的时间差还是正确的正数。
而-1 - INT_MAX的结果是INT_MIN。

#include <stdio.h>
#include <limits.h>
int main() {
    int a = INT_MAX, b = INT_MIN;
    printf("\na=%X %d\n", a, a);
    printf("\nb=%X %d\n", b, b);
    int a1 = a + 1;
    printf("\na1=%X %d\n", a1, a1);
    printf("\na1-a=%X\n", a1-a);
    
    int a3 = a + 30;
    printf("\na3=%X %d\n", a3, a3);
    unsigned int threshold = 20;
    printf("\na3 - a = %d", a3 - a);
    if (a3 - a > threshold) printf("\n > threshold!");
    else printf("\n<= threshold!\n");
    
    int a4 = -1;
    printf("\na4=%X %d\n", a4, a4);
    printf("\na4 - a = %d\n", a4 - a);
    return 0;
}

a=7FFFFFFF 2147483647

b=80000000 -2147483648

a1=80000000 -2147483648

a1-a=1

a3=8000001D -2147483619

a3 - a = 30
 > threshold!
a4=FFFFFFFF -1

a4 - a = -2147483648

unsigned int 也满足这个性质 

#include <stdio.h>
#include <limits.h>
int main() {
    unsigned int a = UINT_MAX;
    printf("\na=%X %d %u\n", a, a, a);
    
    unsigned int a1 = a + 1;
    printf("\na1=%X %d\n", a1, a1);
    printf("\na1-a=%X\n", a1-a);
    
    unsigned int a3 = a + 30;
    printf("\na3=%X %d\n", a3, a3);
    unsigned int threshold = 20;
    printf("\na3 - a = %d", a3 - a);
    if (a3 - a > threshold) printf("\n > threshold!");
    else printf("\n<= threshold!\n");
    
    return 0;
}
 

输出结果:
a=FFFFFFFF -1 4294967295

a1=0 0

a1-a=1

a3=1D 29

a3 - a = 30
 > threshold!

143.
 

#include <stdio.h>

typedef struct{
    unsigned int  age    : 4;
    unsigned char gender : 1;
    char                 : 0;
    unsigned int  size   : 2;
}child_t;

int main()
{
    child_t child;
    child.age = 6;
    child.gender = 1;
    child.size = 3;
    printf("%d, %X\n", sizeof(child_t), child);

    return 0;
}

运行结果:
4, 316
注意:sizeof(child_t)=4是来自于第1行的unsigned int。如果只有第1行,把第2-4某些行注释掉,sizeof(child_t)还是4,但内容分布会变。 
 

#include <stdio.h>

typedef struct{
    unsigned int  age    : 4;
    unsigned char gender : 1;
 //   char                 : 0;
    unsigned int  size   : 2;
}child_t;

int main()
{
    child_t child;
    child.age = 6;
    child.gender = 1;
    child.size = 3;
    printf("%d, %X\n", sizeof(child_t), child);

    return 0;
}

运行结果为:
4, 76

144. C++ map注意事项
原文链接:https://blog.csdn.net/wangjingqi930330/article/details/81938998
1、在map中,由key查找value时,首先要判断map中是否包含key。
2、如果不检查,直接返回map[key],可能会出现意想不到的行为。如果map包含key,没有问题,如果map不包含key,使用下标有一个危险的副作用,会在map中插入一个key的元素,value取默认值,返回value。也就是说,map[key]不可能返回null。
3、map提供了两种方式,查看是否包含key,m.count(key),m.find(key)。
4、m.count(key):由于map不包含重复的key,因此m.count(key)取值为0,或者1,表示是否包含。
5、m.find(key):返回迭代器,判断是否存在。
6、对于下面的场景,存在key就使用,否则返回null,有下面两种写法:

if(m.count(key)>0)
{
    return m[key];
}
return null;

iter = m.find(key);
if(iter!=m.end())
{
    return iter->second;
}
return null;


这里需要注意:前一种方法很直观,但是效率差很多。因为前面的方法,需要执行两次查找。因此,推荐使用后一种方法。
7、对于STL中的容器,有泛型算法find(begin,end,target)查找目标,map还提供了一个成员方法find(key)
 

145. 注意const int  *p = &x; 这里如果x不是const的话,那么这里 x++是没有问题的,但是++*p是不允许的。 
 

    int x = 10;
    const int * p = &x;
    x++;
    // ++*p;   //wrong
    cout << x << endl;

146.#define MSG(X) #X   会把X当一个字符串返回

#define MSG(X) #X        

cout<< MSG(Hello);  //会打印Hello

147. str += 'a'; 和 str = str + 'a'; 不一样!!!
str += 'a';        占内存少!! 
str = str + 'a';  占内存多!! 

148. C++的string可以包含NULL, 而且string.size()或 length()也会把NULL计算在内。

#include <iostream>
#include <string>

using namespace std;

int main()
{
    std::string s;
    s.push_back('\0');
    s.push_back('a');    
    cout << s.size() << " " << s.length() << endl;  //输出 "2 2"
    return 0;
}

149. 0 % 1 和 1 % 1的结果都是0。
        pow(0,0)的结果是1。
150. C++里面char -> string 不能直接用std里面的to_string(),因为char本来就是integer,to_string(char)会生成"100","103"这样的字符串。可以用string(1,c)来生成一个临时的内容为c,长度为1的字符串。

#include <iostream>
#include <string>
using namespace std;
int main() {
    // Write C++ code here
    string a = "";
    a = a + 'x';
    std::cout << "a = " << a << endl;  //输出x
    
    string a2 = "";
    a2 += 'x';
    std::cout << "a2 = " << a2 << endl;  //输出x
    
    string b = "" + 'x';
    std::cout << "b = " << b << endl;  //输出乱码 
    
    string c = "" + string(1, 'x');
    std::cout << "c = " << c << endl;  //输出x

    string d = "" + to_string('x');
    std::cout << "d = " << d << endl;  //输出x
    
    string e = "";
    e = e + to_string('x');
    std::cout <<  "e = " << e << endl;  //输出x
    
    return 0;
}

输出:
a = x
a2 = x
b = 161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
c = x
d = 120
e = 120

151. 关于C 的二维数组初始化。
1) 如果是在函数里面写成这样是不行的, 除非是定义成全局变量,不过那样也就不用加={0};了。
char a[3][3]={0};
2) 在函数里面写成这样可以。
    char a[3][3];
    memset(a,0,sizeof(a));
3) 如果是动态分配内存方式构建数组的话,把malloc函数改用成calloc函数。

152. 对于一维数组a[3], &a 和 a的值相同。
        对于二维数组b[3][3], &b, b和*b的值相同。

#include <iostream>

using namespace std;

int main()
{
    int a[3];
    cout << &a << " " << a << endl;
    int b[3][3];
    cout << &b << " " << b << " " << *b << endl;
    
    return 0;
}

output:
0x7fff7902e514 0x7fff7902e514
0x7fff7902e520 0x7fff7902e520 0x7fff7902e520

153. priority_queue第3个参数cmp必须是class/struct,而sort()的第3个参数可以是function或class/struct的实例(注意是实例,不是class/struct的定义)。

154. 把全局变量当形参传入,函数里面会生成它的拷贝来处理,返回后全局变量不会变化。

下面的代码 g_count 返回0。 

#include <iostream>

using namespace std;
int g_count = 0;
int func(int num) {
    num++;
    return num;
}
int main()
{
    func(g_count);
    cout << " g_count = " << g_count << endl;

    return 0;
}

155. 下面的代码没有正确理解指针的移动长度。实际上arr+1 就是移动4个字节。结果代码里面arr+=sizeof(int),等于指针每次移动了16个字节。

int sum(int* arr, int len) {
   int sum = 0;
   for (int i = 0; i < len; i++) {
     cout << "i="<<i<< " arr=" << arr << endl;
     sum += *arr;
     arr += sizeof(int);
     //should be arr++;
    }
   return sum;
}

156. C 二维数组分配  

int **create(int n) {
 int i;
 int **M = (int **)malloc(n * sizeof(int *)); //注意这里是int *
 for (i = 0; i < n; i++)
   M[i] = (int *)malloc(m * sizeof(int)); //注意这里是int
 return M;
}

157.栈缓冲器溢出
下面这段代码总是假定用户的输入不过超过 32 字节,一旦超过后,那么将立刻破坏栈帧中相邻的数据。


void buffer_overflow() {
 char buf[32];
 gets(buf);
 return;
}

158. *x--;

上述代码的问题在于 * 和 -- 有相同的优先级,该代码实际上会将 x指针减1而不是把 x指向的值减1。

159.

#include <iostream>
using namespace std;
#include<algorithm>
class Str{
	public:
	 Str(int x){
		cout<<"我是想把整数变字符串"<<endl;
	}
	Str(const char* a)
	{
		cout<<"我是想把字符数组变字符串"<<endl;
	}

};


int main( ){

	//Str s = 'c';   //这里s实际上是const char a类型, 不是const char *a类型
	Str s = "abawegawe0";  //这里s是const char *类型
	// 输出:"我是想把整数变字符串"
	// 它把'c'的ASCII码传进去了,如果这样变成字符串那就得到一个数字,
        // 而我们期待的是把'c'变成字符串。
	return 0;
}

160. union和struct的size都必须是最大size项的整数倍。 下面的最大size项是long i。

#include <iostream>
typedef union {
    long i;
    int k[5];
    char c;
} A;

typedef struct {
    long i;
    int k[5];
    char c;
} B;

int main()
{
    std::cout << sizeof(A) << " " << sizeof(B) << std::endl;  //输出结果是24, 32

    return 0;
}

下面的1 4 2  不能拼一起,因为每项都必须以自己长度的整数倍开头!所以int a必须以4的整数倍开头。

//64位
struct C 
{ 
 double t;   //8   1111 1111
 char b;  //1      1
 int a;   //4      0001111  
 short c;  //2     11000000
};  
 //sizeof(C) = 24;  //注意:1 4 2 不能拼在一起

161. 注意, +-*/ %的优先级高于<< 高于& 高于 ^

//a=3, b=5, c=4
int sum = 3 & 5 << 4 + 3 ^ 4;
  //3 & 5 << 7 ^ 4      //先执行+
  //3 & 640 ^ 4         //再执行<<
  //0 ^ 4               //再执行&
  //4                   //再执行^ 
sum=a&b<<c+a^c;   //=4

162. C++ stl的container的front()和back()通常是元素,begin()和end()通常是指针。 

163. 下面的代码在C 可以通过,赋值时候的空洞会用0代替。
 

#include <stdio.h>

int main()
{
    int values[4] = {[0] = 7, [2] = 6, [3] = 7};
    printf("%d %d %d %d", values[0], values[1], values[2], values[3]);

    return 0;
}
C++ 编译会出错。
main.cpp: In function ‘int main()’:
main.cpp:5:47: sorry, unimplemented: non-trivial designated initializers not supported
    5 |     int values[4] = {[0] = 7, [2] = 6, [3] = 7};
      |                                               ^
main.cpp:5:47: sorry, unimplemented: non-trivial designated initializers not supported
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值