打印

[灌水]初始化 VS 赋值

[复制链接]
6080|27
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
lenglx|  楼主 | 2007-6-21 12:22 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
[声明] 此发言乃杂家杂谈,我姑且写之,你姑且看之.如你有什么不同见解,我有什么错误,请指出.
        一切来人来函来电,均谢绝之. :)

                    初始化 VS 赋值
当你写下如下语句:

            <1>
                int x = 1;
            <2>
                int x;
                x = 1;  
    
它们之间可有什么区别?

有.这是两个不同的动作.
<1>是一个初始化操作,而<2>是一个赋值操作.前者是一个编译器动作,而后者是一个程序动作.

需要证据?好的,看下面的程序.

                <3>
                int main(void)
                {
                    static  int val0 = 0x55;
                    int val1 = 0x66;
                    int val2;
                    val2 = 0x77;
                }
进入调试状态,在main处停下.此时main()函数尚未执行,但val0的值已经准备妥当.
就像前面所说的, val0是一个初始化动作, 编译器在进入main()函数之前就将val0初始化完毕.

喔,等等.val1不也是一个初始化动作吗,那怎么它的值没有准备好.

是的,val1仍然是一个初始化动作.不过,唔,你知道的,局部变量有点特殊,它位于堆栈中.
从某些方面说,它具有overlay属性,所以在每次进入main()函数后(不是在之前),都需要将它初始化一次.

或者换一种说法,更好理解:
    val1是一个全局性的变量,它的生命周期,从程序开始,直到结束,都是有效的.所以它在整BPOSThttps://bbs.21ic.com/club/bbs/SaveOwnerEdC中,只需要初始化一次就好.
    而val2是一个局部性的变量,它的生命周期,从它声明开始,到它所在的函数退出前那一刻.所以每次这个函数被调用,它都将被初始化一次.
    
虽然,从汇编后的结果看来,val1和val2操作的汇编代码是相同的,但它们仍然是2个不同的动作(操作).它们的汇编代码之所以相同,那是因为你使用的是内置的数据类型(int),如果你尝试在C++下写类似的代码(看程序片断<7>),那就是另一个故事了.



杂项:
1) const 变量不能通过赋值动作获得一个值(废话),但可以通过初始化动作获得一个初值(还是废话).
            <3>
            const int x0 = 5;   //ok
            const int x1;
            x1 = 5;             //error

2) 关于struct   
            <4>
            typedef struct S{int v1, int v2};
            S s0 = {1,2};       //ok, 初始化
            S s1;
            s1 = {1,2};         //error, 不能这样赋值
            
3) 某种意义上,初始化动作是一个一次性的动作.
    你一定写过类似下面的程序:
            <5>
            foo(..)
            {
                static int cnt = 0;
                cnt ++;
                //.....
            }
    你也一定不会粗心的写成如下程序:
            <6>
            foo(..)
            {
                static int cnt;
                cnt = 0;
                cnt ++;
                //.....
            }
    因为程序<5>是一个初始化操作,是一次性的动作.
    而程序<6)是一个赋值操作,foo()每被调用,cnt将重新被赋值,这不是你要的结果,是不?
    这再次说明:初始化操作是编译器动作,而赋值操作不是.
                
4) 效率
    在C中,通常都是使用一些编译器内置的数据类型,你不大可能看到初始化动作和赋值动作的效率,也因此你不太容易看到它们之间的巨大区别.
    但是如果在C++中,你就能看到初始化和赋值是多么的不同了.
            <7>
            class X {....};
            X xx;
            
            X xx0 = xx; //初始化动作,它使用copy construtor直接初始化对象.
            X xx1;      //声明一个对象,对象获得内存,并调用default construtor初始化对象.
            xx1 = xx;   //使用operator= 将对象xx的值赋给xx1.
    

相关帖子

沙发
古道热肠| | 2007-6-21 14:25 | 只看该作者

我都用一种赋值法

即如楼主所写:         
int x;
x = 1;  
分开来,不容易忘记,定义的时候只管变量类型,
到了到用它时作一初始化,不必管它过去是什么内容。

使用特权

评论回复
板凳
gyt| | 2007-6-23 10:20 | 只看该作者

同意

赞成古版的做法

使用特权

评论回复
地板
农民讲习所| | 2007-6-23 11:24 | 只看该作者

全局变量才有初始化动作,局部是等效的

静态局部变量等效全局变量(MCU软件中)

使用特权

评论回复
5
HotPower| | 2007-6-23 11:33 | 只看该作者

古版有些问题,还是所长有理~~~(哪怕他不对都得说的~~~)

我很喜欢静态局部变量,这样有可能全局变量少点.

使用特权

评论回复
6
古道热肠| | 2007-6-23 11:57 | 只看该作者

不怕灌水

  大热天的,灌水就当洗涼水澡。
  我在2楼所述是仅仅针对局部变量和全局变量来说的。对静态变量,是否只有楼主所示的一法(static  int val0 = 0x55;)赋初值,请大家侃侃,如果不赋值时静态变量默认值是0这一说法是否可靠。

使用特权

评论回复
7
dengm| | 2007-6-23 12:02 | 只看该作者

这是 执行语句 vs 非执行语句 的问题

非执行语句在编译时, 编译器可能会通盘处理, 得到更小的代码

使用特权

评论回复
8
lenglx|  楼主 | 2007-6-24 16:55 | 只看该作者

随便一说.

=====================================================================
对静态变量,是否只有楼主所示的一法(static  int val0 = 0x55;)赋初值,请大家侃侃,如果不赋值时静态变量默认值是0这一说法是否可靠。
=====================================================================
私下以为: 一般情况下,这是正确的.(默认值是0)
然而,在main()函数获得控制权以前,有一段C运行库函数(启动程序)被添加到可执行程序中.这段启动程序,将对那些有初始化值的变量进行处理(当然还有一些其它的动作).
但这段启动程序是可以定制的(虽然很少这样做),也就是说:你自己可以控制程序的初始化行为.
编译器一般都提供了启动程序的源代码(比如C51是STARTUP.A51,VC++是CRT0.C,BC是C0W.ASM)

简单的例子:
在C51中,如果你修改STARTUP.A51如下:
IF IDATALEN <> 0
                MOV     R0,#IDATALEN - 1
                mov    A, #33h
                ;CLR     A
IDATALOOP:      MOV     @R0,A
                DJNZ    R0,IDATALOOP
ENDIF

IF XDATALEN <> 0
                MOV     DPTR,#XDATASTART
                MOV     R7,#LOW (XDATALEN)
  IF (LOW (XDATALEN)) <> 0
                MOV     R6,#(HIGH (XDATALEN)) +1
  ELSE
                MOV     R6,#HIGH (XDATALEN)
  ENDIF
                MOV     A,#66H
                ;CLR     A
XDATALOOP:      MOVX    @DPTR,A
                INC     DPTR
                DJNZ    R7,XDATALOOP
                DJNZ    R6,XDATALOOP
ENDIF

void main(void)
{
  static int x;
  //....
}
那么这个x的值将被初始化为:0x3333

使用特权

评论回复
9
ayb_ice| | 2007-6-25 08:34 | 只看该作者

本人喜欢用赋值方法,CODE变量除外

局部变量其实差不多,全局有点差异

使用特权

评论回复
10
zxq1234508| | 2007-6-25 08:58 | 只看该作者

长见识了

使用特权

评论回复
11
古道热肠| | 2007-6-25 10:34 | 只看该作者

每次构造项目时都把Startup.A51拿掉了

   原来这关系到变量的默认值了。以后我还是带上为好。其实仔细想想,静态变量也是可能先定义,后赋初值,再使用这静态变量进行运算的。

使用特权

评论回复
12
tjsheep| | 2007-6-25 10:41 | 只看该作者

可惜有些编译器在建立c环境时候,为了效率,

是不给不确定全局变量或静态变量赋值0的,结果很多时候反而带来不方便……

使用特权

评论回复
13
农民讲习所| | 2007-6-25 10:59 | 只看该作者

从编程严谨角度来讲,必须显式初始化:程序自己进行初始

模块化要求,而且特别在有睡眠重起时保证变量初始正确。
MCU中不要使用static,因为和显式初始化有冲突。

使用特权

评论回复
14
lenglx|  楼主 | 2007-6-25 12:23 | 只看该作者

?

农民讲习所 发表于 2007-6-25 10:59 侃单片机 ←返回版面    

13楼: 从编程严谨角度来讲,必须显式初始化:程序自己进行初始化 

模块化要求,而且特别在有睡眠重起时保证变量初始正确。
MCU中不要使用static,因为和显式初始化有冲突。
 
为什么"必须"显示初始化?
如你所说,一旦你决定不使用"implicit initialization",势必对static局部变量的初始化动作造成不便.
我不能这么写:
   static int local_val = 0x55; // 因为是"隐式"的初始化
因为是初始化动作是"只此一次"的动作,我也不能写:
{
   static int local_val;
  local_val = 0x55;  
}
也许可以这么做?
  bool g_flag;
{
   static int local_val;
   if(g_flag)
      local_val = 0x55;  
}
复杂且不说,这里又涉及到g_flag的初始化问题.
如你所说,只有放弃使用 static 局部变量了.

但static 局部变量确实是用处比较大的,比如"引用计数".
而且static 局部变量也可以减少全局名字空间的冲突.

还有: 你能否解释下"睡眠重起时保证变量初始正确"这句话?
我对这句话不是很理解.
我所理解的"睡眠"状态是MCU进入睡眠模式,此状态下,不过"MCU"暂停而已,RAM中的数据应当是仍然"保持"着的.
 

使用特权

评论回复
15
high| | 2007-6-25 12:30 | 只看该作者

你只看到形式,农民说的是本质

本质上,只要全局变量和静态才有初始化问题。
局部变量都是在stackheap里面开辟然后赋值,局部变量的初始化 本质就是赋值。

重启后变量初始化又是更高要求了。
另外,static本质上就是全局变量。

使用特权

评论回复
16
lenglx|  楼主 | 2007-6-25 12:58 | 只看该作者

high 发表于 2007-6-25 12:30 侃单片机 ←返回版面    

15楼: 你只看到形式,农民说的是本质 

本质上,只要全局变量和静态才有初始化问题。
局部变量都是在stackheap里面开辟然后赋值,局部变量的初始化 本质就是赋值。

重启后变量初始化又是更高要求了。
另外,static本质上就是全局变量。
 
 
第一: 我是对"MCU中不要使用static"这句话有不同意见,好像并不涉及你所说的"本质"?

第二: 你的说法在C中成立,但C++现在也比较普遍了.C++中,即使是建立在堆栈上的局部非静态对象,初始化和赋值仍然是不同的概念,也会有不同的代码产生,也就是说"本质"是不同的.而且,并不是所有的C,局部变量都是建立在堆栈上的(C51就不是,虽然C51中局部变量的初始化的"本质"也是赋值,但并不是建立堆栈上).

还有: 重启后变量初始化又是更高要求了。
 你能详细述说下,"更高要求"是怎么更高**

另: 请恕我孤陋寡闻,好像没有"stackheap"这种说**我只听说过"STACK"和"HEAP"而已.

使用特权

评论回复
17
农民讲习所| | 2007-6-25 13:24 | 只看该作者

power dowm
重新初始化:初始化工作环境
开始工作

如果使用static,极其容易出现难以察觉的BUG:变量初始化不对。

“但static 局部变量确实是用处比较大的,比如"引用计数".
而且static 局部变量也可以减少全局名字空间的冲突.”
都没什么意义,只要遵循良好的编程习惯就可以避免。

使用特权

评论回复
18
lenglx|  楼主 | 2007-6-25 13:35 | 只看该作者

呵呵

好像这么2句难以令人信服.

如果使用static,极其容易出现难以察觉的BUG:变量初始化不对。
我倒是觉得变量初始化"只要遵循良好的编程习惯就可以避免。"

只于避免使用static变量,我保留我的意见.我从不以为有必要去"避免使用".

使用特权

评论回复
19
古道热肠| | 2007-6-26 11:46 | 只看该作者

关键是一初始化问题

    Static用它小心为上,尤其是初始化值,其全局的存在性与局部的可见性都要分清楚。

使用特权

评论回复
20
农民讲习所| | 2007-6-26 11:55 | 只看该作者

举例:

init()
{
   static kkk = 0;
   .......
}

main()
{
   init();

   while(1){
    ...
    
    if( Powerdown_Requ ){
        powerdown();
        init();                 //static一定会存在BUG,没有再次初始化。因为没调用startup51.a51。本意是清0,不是RAM保持的内容。
     }
    
  }




}

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

17

主题

186

帖子

1

粉丝