• C++中的陷阱(一)


    导言

    程序设计语言中充满陷阱,一不小心就会掉入其中万劫不复,之所以有陷阱,是因为语言的设计细节不符合程序员的直觉

    所以你会发现,语言越高级越注重顺从程序员的直觉。

    c++也有许多陷阱,所谓山不过来,我就过去,因此将c++中易错点、难点集合于此,会不定期更新。

    字符串与vector

    字符串字面值与标准库string不是同一种类型

    string s("hello");
    cout<<s.size()<<endl;        //OK
    cout<<"hello".size()<<endl;  //ERROR
    cout<<s+"world"<<endl;       //OK
    cout<<"hello"+"world"<<endl; //ERROR 

    strlen、sizeof与size()求字符串长度的区别

    cout<<strlen("123")<<endl;   //返回 3
    cout<<sizeof("123")<<endl;   //返回 4
    string s = "123";
    cout<<s.size()<<endl;        //返回 3

    标准string库中的getline函数返回时会丢弃换行符

    const iterator与const_iterator的区别

    vector<int>::const_iterator //不能改变指向的值,自身的值可以改变
    const vector<int>::iterator //可以改变指向的值,自身的值不能改变
    const vector<int>::const_iterator //自身的值和指向的值都是只读的


    任何改变vector长度的操作都会使已存在的迭代器失效。如:在调用push_back之后,就不能再信赖指向vector的迭代器了

    vector<int> ivec;
    ivec.push_back(10);
    vector<int>::iterator it = ivec.begin();
    cout<<*it<<endl;
    ivec.push_back(9);
    cout<<*it<<endl;      //迭代器已经失效

    数组与指针

    字符数组除了可以用花括号在定义时初始化外,还可以用字符串字面值初始化,但谨记字符串字面值包含一个额外的空字符

    char c1[] = {'h','e','l','l','o'};
    char c2[] = "hello";
    cout<<sizeof(c1)/sizeof(char)<<endl;  //长度是5
    cout<<sizeof(c2)/sizeof(char)<<endl;  //长度是6


    一个数组不能用另一个数组初始化,也不能将一个数组赋值给另一个数组

    int a[3] = {1,2,3};
    int b[3][3] = {{1,2,3},{1,2,3},{1,2,3}}; //right
    int c[3][3] = {a,a,a};   //error


    若指针保存0值,表明它不指向任何对象。但是把int型变量赋值给指针是非法的,尽管此int型变量的值可能为0

    int a = 0;
    int *p1 = 0;   //right
    int *p2 = a;   //error
    typedef string *pstring; 
    const pstring cstr; 

    cstr的类型是 string * const 还是 const string * ?
    答:是string *const cstr,而非 const string *cstr。容易产生误解的原因是const限定符既可以放在类型前也可以放在类型后,const pstring cstr等价于pstring const cstr。遇到此类问题时,把const放在类型之后来理解。

    区分:int *ip[4] 和 int (*ip)[4]
    第一个表示一个数组,元素是int指针
    第二个表示一个指针,指向int数组,遇到此类问题时,由内向外读。

    运算符

    除法/和求模%
    若两个操作数是正数,则除法的结果是正数,求模的结果也是正数
    若两个操作数是负数,则除法的结果是正数,求模的结果是负数
    若只有一个操作数是负数,则除法和求模的结果取决于机器,除法可以确定结果是负数

    逻辑与和逻辑或操作符总是先计算其左操作数,然后再计算其右操作数,只有在仅靠左操作数的值无法确定该逻辑表达式的结果时,才会求解其右操作数

    区分 if(i<j<k) 和 if(i<j && j<k)
    第一个i<j或者为0或者为1,只要k大于1,表达式就为true
    第二个必须i<j且j<k表达式才为true

    区分 if(val) 和 if(val == true)
    第一个只要val非零则表达式为true,val为0则表达式为false
    第二个只有val为1表达式为true,val非1则表达式为false

    int val = 2;
    if(val==true){              //不会进入if
      cout<<"val==true"<<endl;
    }


    多个赋值操作符中,各对象必须具有相同的数据类型,或者具有可转换为同一类型的数据类型。

    int ival; int *pval;
    ival = pval = 0;  //error 尽管ival和pval都可以赋值为0
    string s1,s2;
    s1 = s2 = "OK"    //ok


    如果指针指向不是用new分配的内存地址,则在该指针上使用delete是不合法的。

    通常编译器不能断定一个指针是否指向动态对象,因此尽管这样做是错误的,但在大部分编译器上仍能运行通过,但是会产生运行时错误。

    整形提升

    对于所有比int小的整形(char, signed char, unsigned char, short, unsigned short),如过该类型所有可能值都包含在int中,他们会被提升为int型,否则,他们将被提升为unsigned int。

    对于包含signed和unsigned int型的表达式,表达式中的signed型整数会被转换为unsigned型。

    int i = -5;
    unsigned int ii = 1;
    cout<<(i>ii)<<endl;   //输出1,非常有趣的结果 原因是int型的i转换为unsigned int型
    short i = -5;
    unsigned short ii = 1;
    cout<<(i>ii)<<endl;  //输出0 比较时short和unsigned short都提升为int型
  • 相关阅读:
    php安全编程&python测试实例编写
    MySQL注入技巧性研究
    第一届“百度杯”信息安全攻防总决赛
    不想在315“中奖”?你得躲过这些坑!
    这些故事你尽管听,不奇葩算我输!
    str2-045漏洞事件,你想要的这里都有
    python多线程在渗透测试中的应用
    【ZCTF】easy reverse 详解
    UVA
    用Thinphp发送电子邮件的方法
  • 原文地址:https://www.cnblogs.com/moonlight-chao/p/3594548.html
Copyright © 2020-2023  润新知