记录踩过的坑-C/C++

目录

C++11 Lambda表达式(匿名函数)

C++ RAII

FE_DOWNWARD/FE_TONEAREST/FE_TOWARDZERO/FE_UPWARD宏常量

函数签名

inline

const

integral_constant

nullptr

C++ Technical Report 1 (TR1)

tr1里的tuple

std::future

C++ LHS RHS

有符号整数溢出  

auto

stdout(标准输出)

stderr(标准错误)

C语言中条件编译相关的预编译指令 

memcpy

C++ extern "C"

uniform_int_distribution和uniform_real_distribution

C语言pow()函数:求x的y次方的值

C++11 std::is_same

C++ lvalue和rvalue


C++11 Lambda表达式(匿名函数)

Lambda 表达式(lambda expression)是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数。Lambda表达式可以表示闭包(注意和数学传统意义上的不同)。
格式
[capture list] (parameter list) ->return type {function body}

//完整的例子
    {
        auto fun = [](int a,int b)->int{return  a+b;};
        //输出30
        printf("%d\n",fun(10,20));
    }

C++ RAII

RAII是Resource Acquisition Is Initialization(wiki上面翻译成 “资源获取就是初始化”)的简称,是C++语言的一种管理资源、避免泄漏的惯用法。利用的就是C++构造的对象最终会被销毁的原则。RAII的做法是使用一个对象,在其构造时获取对应的资源,在对象生命期内控制对资源的访问,使之始终保持有效,最后在对象析构的时候,释放构造时获取的资源。

FE_DOWNWARD/FE_TONEAREST/FE_TOWARDZERO/FE_UPWARD宏常量

常量

说明

FE_DOWNWARD

趋于负无穷

FE_TONEAREST

向最接近的整数舍入

FE_TOWARDZERO

四舍五入到零

FE_UPWARD

向正无穷的方向前进

函数签名

C++中的函数签名(function signature):包含了一个函数的信息,包括函数名、参数类型、参数个数、顺序以及它所在的类和命名空间。普通函数签名并不包含函数返回值部分,如果两个函数仅仅只有函数返回值不同,那么系统是无法区分这两个函数的,此时编译器会提示语法错误。函数签名用于识别不同的函数,函数的名字只是函数签名的一部分。在编译器及链接器处理符号时,使用某种名称修饰的方法,使得每个函数签名对应一个修饰后名称(decorated name)。编译器在将C++源代码编译成目标文件时,会将函数和变量的名字进行修饰,形成符号名,也就是说,C++的源代码编译后的目标文件中所使用的符号名是相应的函数和变量的修饰后名称。C++编译器和链接器都使用符号来识别和处理函数和变量,所以对于不同函数签名的函数,即使函数名相同,编译器和链接器都认为它们是不同的函数

inline

inline是C++关键字,在函数声明或定义中,函数返回类型前加上关键字inline,即可以把函数指定为内联函数。

函数调用的开销:   函数被调用时,要有函数调用和返回。要保存当前程序上下文信息,以便函数调用完毕后返回原来的地方,继续执行程序。将函数的参数进行压栈、出栈,执行函数,函数调用完毕后释放内部变量占用的内存。  

inline作用:   将函数声明为inline,是对编译器的一种建议,编译器可以选择不进行inline。大多数编译器是在编译过程中进行inline。所以不能对virtual函数进行inline,因为编译的时候编译器不知道该调用哪个函数。编译器也不对通过函数指针进行的调用实施inlining.   不要轻易的对构造函数和析构函数进行inline,因为即使看着函数体是空的,其实内部编译器会产生一些代码,比如添加默认构造函数,默认拷贝构造函数等等。   将函数声明为inline,编译器不把它当做是一个函数,而是类似于把函数代码拷贝到原来的地方,这样就省下了函数调用的开销。   将大多数inlining限制在小型、被频繁调用的函数上。内联函数会在每一处调用函数的地方进行代码复制,会使产生的代码膨胀,对于函数很大来说,这样获得的速度提高会很小甚至没有。所以对于递归,循环等不进行inline。   inline函数要将声明和定义放在一块,不然没有效果。在类内部的函数定义会自动扩展成inline。在类外部则需显式加上inline声明。   通常要将inline函数放在头文件内。   大部分调试器对inline函数束手无策,因为在一个不存在的函数内设立断点并不容易。

const

欲阻止一个变量被改变,可使用const,在定义该const变量时,需先初始化,以后就没有机会改变他了;

对指针而言,可以指定指针本身为const,也可以指定指针所指的数据为const,或二者同时指定为const;

在一个函数声明中,const可以修饰形参表明他是一个输入参数,在函数内部不可以改变其值;

对于类的成员函数,有时候必须指定其为const类型,表明其是一个常函数,不能修改类的成员变量;

对于类的成员函数,有时候必须指定其返回值为const类型,以使得其返回值不为“左值”。

integral_constant

integral_constant就可方便地定义编译期常量,而无需再通过enum和static const变量方式,这也为定义编译期常量提供另一种方法。

这个类是所有traits类的基类,分别提供了以下功能:

  • value_type 表示值的类型
  • value表示值
  • type 表示自己, 因此可以用::type::value来获取值
  • true_type和false_type两个特化类用来表示bool值类型的traits,很多traits类都需要继承它们

nullptr

nullptr是c++11中的关键字,表示空指针

C++ Technical Report 1 (TR1)

C++ Technical Report 1 (TR1)是ISO/IEC TR 19768, C++ Library Extensions(函式库扩充)的一般名称。TR1是一份文件,内容提出了对C++标准函式库的追加项目。这些追加项目包括了正则表达式、智能指针、哈希表、随机数生成器等。TR1自己并非标准,他是一份草稿文件。然而他所提出的项目很有可能成为下次的官方标准。这份文件的目标在于「为扩充的C++标准函式库建立更为广泛的现成实作品」。

tr1里的tuple

相当于stl里面pair的扩展。pair只能放两个元素,而tuple至少可以放10个元素。

std::future

C++11提供了std::future类模板,future对象提供访问异步操作结果的机制,很轻松解决从异步任务中返回结果。  在C++标准库中,有两种“期望”,使用两种类型模板实现,  唯一期望(unique futures,std::future<>) std::future的实例只能与一个指定事件相关联。 共享期望(shared futures)(std::shared_future<>) std::shared_future的实例就能关联多个事件。

C++ LHS RHS

Right hand side, left hand side

有符号整数溢出  

C/C++中的整数类型分为有符号整数和无符号整数,其中有符号整数的最高位表示符号(正或负),其余位表示数值大小,而无符号整数则所有位都用于表示数值大小。有符号整数的取值范围为[−2n−1, 2n−1−1],当有符号整数的值超出了有符号整数的取值范围时就会出现整数溢出,导致有符号整数溢出的重要原因之一是有符号整数的运算操作不当,常见运算 "+"、"-"、 "*"、 "/"、 "%"、 "++"、 "--" 等等,如果没有对值的范围进行判断和限制,很容易导致有符号整数溢出问题。

auto

auto是一个C/C++语言存储类型,仅在语句块内部使用,初始化可为任何表达式,其特点是当执行流程进入该语句块的时候初始化可为任何表达式。C语言中提供了存储说明符auto、register、extern、static说明的四种存储类别。四种存储类别说明符有两种存储期:自动存储期和静态存储期。其中auto和register对应自动存储期。具有自动存储期的变量在进入声明该变量的程序块时被建立,它在该程序块活动时存在,退出该程序块时撤销。在函数内部定义的变量成为局部变量。在某些C语言教材中,局部变量称为自动变量,这就与使用可选关键字auto定义局部变量这一作法保持一致。

stdout(标准输出)

输出方式是行缓冲。输出的字符会先存放在缓冲区,等按下回车键时才进行实际的I/O操作。 

stderr(标准错误)

是不带缓冲的,这使得出错信息可以直接尽快地显示出来。

C语言中条件编译相关的预编译指令 

#define            定义一个预处理宏

#undef            取消宏的定义  

#if                   编译预处理中的条件命令,相当于C语法中的if语句

#ifdef              判断某个宏是否被定义,若已定义,执行随后的语句

#ifndef            与#ifdef相反,判断某个宏是否未被定义

#elif                若#if, #ifdef, #ifndef或前面的#elif条件不满足,则执行#elif之后的语句,相当于C语法中的else-if

#else              与#if, #ifdef, #ifndef对应, 若这些条件不满足,则执行#else之后的语句,相当于C语法中的else

#endif             #if, #ifdef, #ifndef这些条件命令的结束标志.

defined         与#if, #elif配合使用,判断某个宏是否被定义

memcpy

memcpy指的是C和C++使用的内存拷贝函数,函数原型为void *memcpy(void *destin, void *source, unsigned n);函数的功能是从源内存地址的起始位置开始拷贝若干个字节到目标内存地址中,即从源source中拷贝n个字节到目标destin中。

C++ extern "C"

extern是C/C++语言中表明函数全局变量作用范围(可见性)的关键字,该关键字告诉编译器,其声明的函数和变量可以在本模块或其它模块中使用。通常,在模块的头文件中对本模块提供给其它模块引用的函数和全局变量以关键字extern声明。例如,如果模块B欲引用该模块A中定义的全局变量和函数时只需包含模块A的头文件即可。这样,模块B中调用模块A中的函数时,在编译阶段,模块B虽然找不到该函数,但是并不会报错;它会在连接阶段中从模块A编译生成的目标代码中找到此函数。

与extern对应的关键字是 static,被它修饰的全局变量和函数只能在本模块中使用。因此,一个函数或变量只可能被本模块使用时,其不可能被extern “C”修饰。

典型的,一个C++程序包含其它语言编写的部分代码。类似的,C++编写的代码片段可能被使用在其它语言编写的代码中。不同语言编写的代码互相调用是困难的,甚至是同一种编写的代码但不同的编译器编译的代码。例如,不同语言和同种语言的不同实现可能会在注册变量保持参数和参数在栈上的布局,这个方面不一样。

为了使它们遵守统一规则,可以使用extern指定一个编译和连接规约。例如,声明C和C++标准库函数strcyp(),并指定它应该根据C的编译和连接规约来链接:

1

extern "C" char* strcpy(char*,const char*);

注意它与下面的声明的不同之处:

1

extern char* strcpy(char*,const char*);

下面的这个声明仅表示在连接的时候调用strcpy()。

extern "C"指令非常有用,因为C和C++的近亲关系。注意:extern "C"指令中的C,表示的一种编译和连接规约,而不是一种语言。C表示符合C语言的编译和连接规约的任何语言,如Fortran、assembler等。

还有要说明的是,extern "C"指令仅指定编译和连接规约,但不影响语义。例如在函数声明中,指定了extern "C",仍然要遵守C++的类型检测、参数转换规则。

再看下面的一个例子,为了声明一个变量而不是定义一个变量,你必须在声明时指定extern关键字,但是当你又加上了"C",它不会改变语义,但是会改变它的编译和连接方式。

如果你有很多语言要加上extern "C",你可以将它们放到extern "C"{ }中。

uniform_int_distribution和uniform_real_distribution

C++11提供的均匀分布模板类为:uniform_int_distribution和uniform_real_distribution。前一个模板类名字中的int不是代表整型,而是表示整数。因为它是一个模板类,可以用int、long、short等整数类型来实例化。后一个表示浮点数模板类,可以用float和double来实例化。使用例子如下:

#include<iostream>
#include<random>
#include<time.h>
 
using std::cout;
using std::endl;
using std::cin;
 
 
int main()
{
    std::default_random_engine random(time(NULL));
    std::uniform_int_distribution<int> dis1(0, 100);
   std::uniform_real_distribution<double> dis2(0.0, 1.0);
 
    for(int i = 0; i < 10; ++i)
        cout<<dis1(random)<<' ';
    cout<<endl;
 
    for(int i = 0; i < 10; ++i)
        cout<<dis2(random)<<' ';
    cout<<endl;
 
    return 0;
}

可以看到,在uniform_int_distribution的构造函数中,参数说明了随机数的范围。uniform_int_distribution的随机数的范围不是半开范围[  ),而是[  ],对于uniform_real_distribution却是半开范围[  )。也是就是说上面的例子中,能产生100,但不会产生1.0。

 如果uniform_int_distribution使用了无参构造函数,那么其随机数的范围是[0,numberic_limits<type>::max()],也就是0到对应实例化类型能表示的最大值。对于uniform_real_distribution的无参构造函数,则是[0, 1)。

C语言pow()函数:求x的y次方的值

C语言 pow() 函数用来求 x 的 y 次方的值。

头文件:math.h

语法/原型:

double pow(double x,double y);

参数说明:

  • x:双精度数。
  • y:双精度数。

返回值:x 的 y 次方的值。

C++11 std::is_same

位于头文件<type_traits>

这个结构体作用很简单,就是两个一样的类型会返回true

bool isInt = std::is_same<int, int>::value; //为true

C++ lvalue和rvalue

lvalue估计来源于left value。 在赋值语句中lvalue = rvalue;位置处于左边。就是可以修改的值。
rvalue估计来源于right value。处于赋值语句右边,是只读的不可修改的值。
接下来是我所悟到内容的详细分析

lvalue是可以赋值的,说明它是一个变量,它在内存中一定存在,一定有地址。所以&lvalue是有效的,能取到在内存中的地址。
访问lvalue一定会导致CPU访问存储器(相对较慢的操作)。

lvalue的例子:

int a;
a = 10; #a是lvalue

rvalue是不可以赋值的,它不是一个变量,在内存中没有存在,没有地址。它要么是存在于CPU的寄存器中,要么是存在于指令中(立即数)。所以只要对rvalue取地址,那么就一定是错误的(编译器会抱怨的)。

访问rvalue不会导致CPU访问存储器(对立即数和寄存器的访问很快)。

rvalue的例子:

int a;
a = 10; #10是rvalue
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值