嵌入式面试题总结【static,const。lvalue and 如rvalue】 继续整理

static 用法:

for C language:
    1.表示存储属性:
        存储于静态变量区,static int a;
        由于存储于静态变量区,导致变量的记忆性[不会由于栈的回收而丢失],int fun(){ static int a;...}
    2.链接属性
        说明变量或者函数内部链接的,只在本文件有效,及时在另外一个文件extern引用也不可以。
        int the file1:
            static int a; static int fun(){};
        int the file2:
            extern int local; extern void print_h();
    for c++ language:
        兼容c language;

        特有的:
            sattic 对象[函数和变量]是类所有,不会为每个对象创建一个,只有一个类所有的。 class A{int a; int fun(){};}; class B{static class A a1;};
            对于 类内部的static 内部链接属性也受限于外部类的作用域【可能可以在外部访问】。class A{ static int a;};
            对于 static 对象[非类内部的static],也是内部链接的.

const 用法:

    const是对编译器说明 ”不应该改变的[只读的]“,如果改变,编译器会报错!
    【到底const变量可不可以改变,[有可以的,也有不可以的]求大神指点】
    const或许可以取代c中的#define           
    for C language:
    1.表示[只读的]:
        修饰变量,const int a=9;(但c中允许const int a;  郁闷,c++不可以)
        修饰传入参数 int fun(const int a);
    2.链接属性
        说明变量或者函数内部链接的,只在本文件有效,及时在另外一个文件extern引用也不可以。
        int the file1:
            static int a; static int fun(){};
        int the file2:
            extern int local; extern void print_h();
    for c++ language:
        const 优点:便于进行类型检查,便于编译器检测不应该修改的数值。

        1. 限定符声明变量只能被读 ,const int a=5; [必须初始化],在另一连接文件[只能在.h头文件]中引用const常量 ,extern const int i;  

        2.修饰类的成员函数[非成员函数不可以定义为 const 函数] 
        const 成员函数禁止修改对象的所有变量[对象],可以修改类和全局的变量[对象]
            class A{ void print(int a) const {};}; 
原因[引用帖子]:
/-----------------------------------------------------------------------------------
成员函数都会在自己的形参表中加一个参数:这个参数是指向函数所在类的类型的CONST指针.比如:
class A
{
private:
    int n;
public:
    void set()
    {   n=10;  };
};

上边的set函数,是给n赋值的,但是它怎么知道是给哪一个对象的n赋值呢?这就需要在参数中告诉它。编译器为我们作了这些工作。实际生成的set函数可以理解成这样的:
public A::set(A* const this)
{   this->n=10; }
而我们调用的时候其实是这样的:
A a;
A::set(&a);
理解这个对你理解你的这个问题很关键.如果是const成员函数,编译器所作的这项工作也会改变。它会生成一个指向cosnt对象的const指针。所以你不能通过this来改变它所指向的对象。但是要知道static对象并不需要this指针来改变,所以它和const函数是没关系的。
以上内容,仅代表个人观点。
/-----------------------------------------------------------------------------------

        3.修饰函数[主要是成员函数]的返回值
            const可以用于修饰任何类型,只要返回值类型不是voidconst就可以用来修饰返回值的类型。但实际上const用于修饰非引用的返回值类型是没有意义的,
            因为返回值一般都会被赋值给另一个变量,此时用于传递返回值的对象已经被销毁,修饰返回值类型的const的作用也就终结了。
            当返回值是引用类型时,如果该引用的值不希望被修改是可以声明为常引用的,例如:
            class A
            {
            public:
                const A& operator=(const A& a) { return *this; }
            };

            int main( void )
            {

                A a, b, c, d;
                a = b = c = d; // ok
                ((a=b)=c)=d; // error,更加合乎常理
                return 0;
            }

        4.修饰函数的参数[防止意外改动,例如数组大小]
            void print(const int array[],const int size); void print(const A &a)[成员函数]; void print(const int *const array,const int size);
            void char* getmem(size_t size); const char *p=getmem();
            注意:应该按需要使用const,好的使用会使代码更健壮,糟糕的就是难维护的代码。
            对于语言内置类型的输入参数,不应该将“值传递”的方式改为“const 引用传递”,这不会提高效率。
             void fun(int a) 改为void fun(const int &a)。
            语言内置类型的临时变量的无需构造,析构。
            相反 类对象临时对象需要构造,复制,析构,影响效率。而const引用只是别名,不生成临时对象

/-----------------------------------------------------------------------------------------------------------------------------------

说说隐晦的 lvalue and rvalue:

    来源【不去考证】:
        1.{
        L-value中的L指的是Location,表示可寻址。Avalue (computer science)that has an address.
        R-value中的R指的是Read,表示可读。in computer science, a value that does not have an address in a computer language.           
        }
        {
        翻译error
        L-value中的L指的是Location,表示可寻址。The "l" in lvalue can be though of as location
        R-value中的R指的是Read,表示可读。The "r" in rvalue can be thought of as "read" value.         
        }
        {
        lvalue (pronounced "ell-value"): An expression that is an lvalue may appear as either the left-hand or right-hand side of an assignment.
        左值(发音为 ell-value):左值可以出现在赋值语句的左边或右边。
        rvalue (pronounced "are-value"): An expression that is an rvalue may appear on the right- but not left-hand side of an assignment.
        右值(发音为 are-value):右值只能出现在赋值的右边,不能出现在赋值语句的左边。
        Variables are lvalues and so may appear on the left-hand side of an assignment. Numeric literals are rvalues and so may not be assigned.            
        }       

        2.简单说来 rvalue就是只能出现在“赋值运算符【=,()】”right 边的, lvalue在 left or right都可以。

        3.原因[不可以读到的内存[表达式,#define],常量["abc"],不可变的变量[部分函数返回值,只是部分] ];
        典型的 rvalue:表达式,括号()表达式,部分函数返回值,常量

        4.以下引用http://www.cnblogs.com/catch/p/3500678.html
{
c++中的左值与右值

左值(lvalue),右值(rvalue) 是 c/c++ 中一个比较晦涩的概念,有的人可能甚至没有听过,但这个概念到了 c++11 后却变得十分重要,它们是理解move(),forward()等新语义的基础。

左值右值的定义

左值与右值这两概念是从c中传承而来的,在c中,左值指的是能够出现在等号左边及右边的变量(表达式),右值则指的是只能出现在等号右边的变量(表达式).


int a;
int b;

a = 3;
b = 4;
a = b;
b = a;

//不合法。

3 = a;
a+b = 4;

在 c 语言中,通常来说有名字的变量就是左值(如上面例子中的a, b),而由运算操作(加减乘除,函数调用返回值等)产生的中间结果(没有名字)就是右值,如上的3+4, a + b等。

我们暂且可以认为:左值就是在程序中能够寻值的东西,右值就是没法取到它的地址的东西(不完全准确),但如上概念到了c++中,就变得稍有不同。

在c++中,每一个表达式都会产生一个左值,或者右值,相应的,该表达式也就被称作“左值表达式”,“右值表达式”。对于内置的基本数据类型来说(build-in types),左值右值的概念和 c 没有太多不同,不同的地方在于自定义的类型。

而且这种不同比较容易让人混淆:

1) 对于内置的类型,右值是不可被修改的(non-modifiable),也不可被const, volatile 所修饰(cv-qualitification ignored)

2) 对于自定义的类型(user-defined types), 右值却允许通过它的成员函数进行修改。

对于1),这和c是一致的,2) 却是c++中所独有, 因此,如果你看到c++中如下的写法,千万不要惊讶:


class cs
{
    public:

        cs(int i): i_(i) { cout << "cs(" << i <<") constructor!" << endl; }
        ~cs() { cout << "cs destructor,i(" << i_ << ")" << endl; }

        cs& operator=(const cs& other)
        {
            i_ = other.i_;
            cout << "cs operator=()" << endl;
            return *this;
        }

        int get_i() const { return i_; }
        void change(int i) { i_ = i; }

    private:
        int i_;
};


cs get_cs()
{
    static int i = 0;
    return cs(i++);
}


int main()
{
    // 合法
    (get_cs() = cs(2)).change(323);
    get_cs() = cs(2);// operator=()
    get_cs().change(32);

    return 0;
}
这个特性多少有些奇怪,通常来说,c++ 中的自定义类型是应该设计地尽量和内置类型一样才对的,但这个特性却偏偏违背了这个原则。

对于这个特性,我们其实可以这样想,也许会好理解点:自定义类型允许有成员函数,而通过右值调用成员函数是被允许的,但成员函数有可能不是 const 类型,因此通过调用右值的成员函数,也就可能会修改了该右值,done!

左值引用,右值引用

关于右值,有一个十分值得关注的语言的特性:右值能被 const 类型的引用所指向

const cs& ref = get_cs();
而且只能被const 类型的 reference 所指向:

//error 

cs& ref = get_cs();
当一个右值被 const reference 指向时,它的生命周期就被延长了,这个用法我在前面一篇博客里讲到过它的相关应用,点这。

这里暗藏逻辑其实就是:右值不能直接转化成左值(但左值可以转化为右值).

关于前面提到的右值的两个特性:

 1) 允许调用成员函数。
 2) 只能被const reference指向。

导致了一些比较有意思的结果,比如:


void func(cs& c)
{
   cout << "c:" << c.get_i() << endl;
}

//error
func(get_cs());

//正确
func(get_cs() = get_cs());

其中:func(get_cs() = get_cs()); 能够被正常编译执行的原因就在于,cs 的成员函数 operator=() 返回的是 cs&!



不允许非 const reference 引用 rvalue 并不是完美的,它事实上也引起了一些问题,比如说拷贝构造函数的接口不一致了,这是什么意思呢?


class cs
{
    public:

        cs& operator=(const cs& c);
};

// 另一种写法
class cs2
{
    public:

        cs2& operator=(cs2& c);
};

上面两种写法的不同之处就在于参数,一个是const reference,一个是非const.

通常来说,如果不需要修改传进来的参数,我们往往就按 const reference 的写法,但对于copy constructor 来说,它经常是需要修改参数的值,比如 auto_ptr。


// 类似auto_ptr
class auto_ptr
{
   public:

       auto_ptr(auto_tr& p)
        {
             ptr_ = p.ptr_;
             p.ptr_ = NULL;
        }

    private:

         void*  ptr_;
};


所以,对于auto_ptr来说,它的 copy constructor 的参数类型是 non const reference。

这个种写法本来应该被鼓励的,non const reference 比 const reference 更能灵活应对各种情况,从而保持一致的接口类型。

但如果拷贝构造函数写成这样子,却又对 rvalue 的使用带来了极大的不变,如前面所讲的例子,rvalue 不能被 non const reference 所引用,所以像auto_ptr的这样的 copy constructor 就不能接受 rvalue.

//错误
auto_ptr p(get_ptr());

// operator=() 同理,错误。
auto_ptr p = get_ptr();
这也是auto_ptr很不好用的其中一个原因。

为了解决这个问题,c++11 中引入了一种新的引用类型,该种引用是专门用来指向 rvalue 的,有了这种新类型,对 lvalue 和 rvalue 的引用就能明确区分开来了,而在之前,它们是一样的。

因为有了这种新的类型,接着就引出了c++11 中新的语义,move(), forward() 等,这儿先卖个关子,我们下次再讲。            
}       


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值