• 朱兆祺教你如何攻破C语言学习、笔试与机试的难点


    第一节  C语言编程中的几个基本概念
    1.1      include< >与#include" " 
    1.   #include< >和#include" "有什么区别?

    这个题目考查大家的基础能力,#include< >用来包含开发环境提供的库,
    #include" "用来包含.c/.cpp文件所在目录下的头文件。注意:有些开发环境可以在当前目录下面自动收索(包含子目录),有些开发环境需要指定明确的文件路径名。

    1.2      switch()

    1.   switch(c) 语句中 c 可以是 int, long, char, float, unsigned int 类型?
    其实这个题目很基础,c应该是整型或者可以隐式转换为整型的数据,很明显不能是实型(float、double)。所以这个命题是错误的。

    1.3      const

    1.   const有什么用途?
    虽然const很常用,但是我相信有很多人仍然答不上来。
    (1) 欲阻止一个变量被改变,可以使用const 关键字。在定义该 const 变量时,通常需要对它进行初 始化,因为以后就没有机会再去改变它了;
    (2) 对指针来说,可以指定指针本身为 const,也可以指定指针所指的数据为 const,或二者同时指定为 const;
    (3) 在一个函数声明中,const 可以修饰形参,表明它是一个输入参数,在函数内部不能改变其值;
    (4) 对于类的成员函数,若指定其为 const 类型,则表明其是一个常函数,不能修改类的成员变量;
    (5) 对于类的成员函数,有时候必须指定其返回值为 const 类型,以使得其返回值不为“左值”。

    1.4      #ifndef/#define/#endif

    1.   头文件中的 #ifndef/#define/#endif 干什么用?
    其实#ifndef、#define、#endif这些在u-boot、linux内核文件中经常见到,在这么大型的程序中大量使用,可见它的作用不可小觑。
    这些条件预编译多用于对代码的编译控制,增加代码的可裁剪性,通过宏定义可以轻松的对代码进行裁剪。
    #ifndef/#define/#endif最主要的作用是防止头文件被重复定义。

    1.5      全局变量和局部变量

    1.         全局变量和局部变量在内存中是否有区别?如果有,是什么区别?
    全局变量储存在静态数据库,局部变量在堆栈。 其实,由于计算机没有通用数据寄存器,则函数的参数、局部变量和返回值只能保存在堆栈中。提示:局部变量太大可能导致栈溢出,所以建议把较大数组放在main函数外,防止产生栈溢出。
    思考:如程序清单1. 1所示。会出现怎样的情况?
    程序清单1. 1  大数组放在main函数中导致堆栈溢出
    int main(int argc, char *argv[])
    {
        int iArray[1024 * 1024];
        return 0;
    }
     
     
    第二节  数据存储与变量

    2.1      变量的声明与定义
    1.         如程序清单2. 1所示会不会报错?为什么?如果不会报错,又是输出什么结果?
    程序清单2. 1  变量的声明与定义
    #include<stdio.h>
     
    static int  a   ;
    static int  b[] ;
     
    int main( int argc , char *argv[] )
    {
        printf( "%d  %d \n" , a , b[0] ) ;
        return 0 ;
    }
     
    static int   a = 8 ;
    static int   b[4]  ;
    这个程序是不会报错的,并且连警告都不会出现。输出的结果是:8  0
    static int a ,这句程序是声明全局变量a;static int b[],这句程序是声明全局数组变量b,并且是不完全声明,也就是可以省略数组下标。static int a = 8,这里才是定义全局变量a,static int b[4],这里是定义全局变量b。
    2.2      局部变量与全局变量的较量
    1.         请问如程序清单2. 2所示输出什么?
    程序清单2. 2  局部变量与全局变量
    #include<stdio.h>
     
    static int a = 8 ;
    int main( int argc , char *argv[] )
    {
        int a = 4 ;
     
        printf( "%d \n" , a ) ;
     
        return 0 ;
    }
    C语言规定,局部变量在自己的可见范围内会“挡住”同名的全局变量,让同名的全局变量临时不可见。即在局部变量的可见范围内不能访问同名的全局变量。因此本程序输出为:4。
    2.3      charintfloatdouble的数据存储
    1.         请问如程序清单2. 3所示,ij输出什么?
    程序清单2. 3  数据存储
    float i = 3 ;
    int   j = *(int*)(&i) ;
     
    printf( "i = %f \n" , i ) ;
    printf( "j = %#x \n" , j ) ;
    i是毋庸置疑是:3.000000。但是j呢?3.000000?答案是否定的,j是输出:0x4040 0000。有人会问了,难道j是随机输出?瞎说,j输出0x4040 0000是有依据,是一个定值!
    由于i是float数据类型,而j是int数据类型。理论上说,j是取了i的地址然后再去地址,应该得到的就是i的值:3。但是问题的关键就是float数据类型的存储方式和int数据类型不一样,float是占用4个字节(32位),但是float存储是使用科学计数法存储,最高位是存储数符(负数的数符是0,正数的数符是1);接下来8位是存储阶码;剩下的23位是存储尾数。上面i=3.000000,那么3.000000(10进制) = 11(2进制) = <v:shape id=_x0000_i1027 style="WIDTH: 40.5pt; HEIGHT: 21.75pt" equationxml=' 121.1 脳<21' type="#_x0000_t75"> (二进制)。数据在电脑中存储都是二进制,这个应该都没有疑问。那么这里的数符为:0 ,阶码为:E – 127 = 1 ,那么阶码为:E = 128 即为:1000 0000 (2进制) 尾数为:100 0000 0000 0000 0000 0000 。那么存储形式就是:0100 0000  0100 0000 0000 0000 0000 0000。这个数据转换成16进制就是0x4040 0000。
    2. 1  数据存储方式

    char、int、float、double的存储方式如2. 1所示。
    提问:如果i = -3.5 的话,请问j输出多少?
    i = -3.500000
    j = 0xc0600000
    这个希望读者自行分析。
    再问:如果如程序清单2. 4所示。
    程序清单2. 4  数据存储
    double i = 3 ;
    int     j = *(int*)(&i) ;
     
    printf( "i = %lf \n" , i ) ;
    printf( "j = %#x \n" , j ) ;
    这样的话,j又输出多少呢?
    提示:double(  8个字节(64位)  )的存储方式是:最高位存储数符,接下来11位存储阶码,剩下52位存储尾数。
    是不是得不到你想要的结果?double是8个字节,int是4个字节。一定别忘记了这个。用这个方法也同时可以验证大小端模式!
    2.4      容易忽略char的范围
    1.         如程序清单2. 5所示,假设&b=0x12ff54,请问三个输出分别为多少?
    程序清单2. 5  char的范围
    unsigned int b = 0x12ff60 ;
    printf("( (int)(&b)+1 )         = %#x \n" , ( (int)(&b)+1 )           ) ;
    printf("*( (int*)( (int)(&b)+1 ) )  = %#x \n" , *( (int*)( (int)(&b)+1 ) )   ) ;
    printf("*( (char*)( (int)(&b)+1 ) ) = %#x \n" , *( (char*)( (int)(&b)+1 ) )  ) ;
    很显然,&b是取无符号整型b变量的地址,那么(int)(&b)是强制转换为整型变量,那么加1即为0x12ff54+1=0x12ff55。所以( (int)(&b)+1 )是0x12ff55。
    2. 3  指针加1取字符型数据
    由于( (int)(&b)+1 )是整型数据类型,通过(int *)( (int)(&b)+1 )转化为了整型指针类型,说明要占4个字节,即为:0x12ff55、0x12ff56、0x12ff57、0x12ff58,再去地址*( (int *)( (int) (&b)+1 ) )得到存储在这4个字节中的数据。但是很遗憾,0x12ff58我们并不知道存储的是什么,所以我们只能写出0x**0012ff。**表示存储在0x12ff58中的数据。如图2. 2所示。
    2. 2  指针加1取整型数据
    以此类推,*( (char *)( (int) (&b)+1 ) ) = 0xff。如图2. 3所示。
    但是,*( (char *)( (int) (&b)+1 ) )输出的却是:0xff ff ff ff !
    问题出现了,为什么*( (char *)( (int) (&b)+1 ) )不是0xff,而是0xff ff ff ff?char型数据应该占用1个字节,为什么会输出0xff ff ff ff?
    使用%d输出,
    printf("*( (char*)( (int)(&b)+1 ) ) = %d \n" , *( (char*)( (int)(&b)+1 ) ) ) ;
    结果为-1???
    问题出在signed char 的范围是:-128~127,这样肯定无法储存0xff,出现溢出。所以将
    printf("*( (char*)( (int)(&b)+1 ) ) = %#x \n" , *( (char*)( (int)(&b)+1 ) ) ) ;
    改成
    printf("*( (unsigned char*)( (int)(&b)+1 ) ) = %#x \n" ,
    *( (unsigned char*)( (int)(&b)+1 ) ) ) ;
    就可以输出0xff,因为unsigned char 的范围是:0~255(0xff)。

     

    图2.1 数据存储方式

    图2.1  数据存储方式

     

    图2.2 指针加1取整型数据

    图2.2  指针加1取整型数据

     

    图2.3 指针加1取字符型数据

    图2.3  指针加1取字符型数据
     
     
     
     
     
    第三节  数学算法解决C语言问题

    3.1      N!结果中0的个数
    1.        99!结果中后面有多少个0
    谁跟你说过高数没用?数学是C语言的支撑,没有数学建模的支撑就没有那么经典的C语言算法!
    如果你能一个一个乘出来我算你狠!我也佩服你!
    0也就意味着乘积是10的倍数,有10的地方就有5.有5就不担心没2.10以内能被5整除的只有5,但是能被2整除多的去了。所以就简化成了这个数只要有一个因子5就一定对应一个0.
    所以可以得出下面结论:
        当0 < n < 5时,f(n!) = 0;
        当n >= 5时,f(n!) = k + f(k!), 其中 k = n / 5(取整)。
    如程序清单3. 1所示。
    程序清单3. 1  N!中0的个数
    #include<stdio.h>   
     
    int fun(int iValue)   
    {   
        int iSum = 0;   
        while(iValue / 5 != 0)   
        {   
            iSum     += (iValue / 5 );   
            iValue  /= 5;   
        }   
        return iSum;   
    }   
      
    int main( int argc , char *argv[] )   
    {   
    int iNumber, iZoreNumber;  
     
        scanf( "%d", &iNumber);   
        iZoreNumber = fun(iNumber);   
    printf( "%d\n", iZoreNumber);
       
    return 0;   
    }
    所以99!后面有多少个0的答案是:99 / 5 = 19 , 19 / 5 = 3 ; 3 / 5 = 0 .也就是:19 + 3 + 0 = 22.
    这里延伸为N!呢,一样的,万变不离其宗!
    3.2      N!结果中的位数
    1.   请问N!结果中有几位数?
    数学!还是数学,数学真的博大精深,如果大学没有好好学数学,会成为你一辈子的遗憾。
    我们先假设:N! = 10 ^A,我们知道 10^1~10^2(不含10^2)之间是2位数,10^2~10^3(不含10^3)之间是3位数,以此类推,10^A~10^(A+1)(不含10^(A+1))则是(A+1)位数,那就是说,我们只要求出A,即可知道N!有几位数。A=log10(N!),
    N!= 1*2*3……*N,那么A= log10(1)+log10(2)+……+log10(N).
    这样好计算了吧!程序如程序清单6. 2所示。
    程序清单6. 2  N!结果中的位数
    #include <stdio.h>
    #include <math.h>
    int main(int argc, char *argv[])
    {
        int iNumber , i = 0 ;
        double sum = 0 ;
        printf("请输入iNumber :");
        scanf( "%d" , &iNumber );
        for( i = 1 ; i < ( iNumber + 1 ) ; i++) {
           sum += log10(i) ;
        }
        printf(" N!%d \n" , (int)sum  +  1  ) ;
        return 0;
    }
    我们看下调试结果:
    请输入iNumber :10
    N!有7位
    请按任意键继续. . .
        网友可以自行验证。
     
     
    第四节  关键字、运算符与语句
    1.1      static
    1.         如程序清单4. 1所示,请问输出i、j的结果?
    程序清单4. 1  static
    #include <stdio.h>
     
    static int  j ;
     
    void fun1(void)
    {
        static int i = 0 ;
        i++ ;
        printf("i = %d   " , i );
    }
     
    void fun2(void)
    {
        j = 0 ;
        j++ ;
        printf("j = %d \n" , j );
    }
     
    int main(int argc, char *argv[])
    {
       
        int k = 0 ;
        for( k = 0 ; k < 10 ; k++ ){
           fun1() ;
           fun2() ;
           printf("\n");
        }
       
        return 0;
    }
    答案:
    i = 1   j = 1
     
    i = 2   j = 1
     
    i = 3   j = 1
     
    i = 4   j = 1
     
    i = 5   j = 1
     
    i = 6   j = 1
     
    i = 7   j = 1
     
    i = 8   j = 1
     
    i = 9   j = 1
     
    i = 10   j = 1
     
    请按任意键继续. . .
     
    很多人傻了,为什么呢?是啊,为什么呢?!
    由于被static修饰的变量总存在内存静态区,所以运行这个函数结束,这个静态变量的值也不会被销毁,函数下次使用的时候仍然能使用这个值。
    有人就问啊,为什么j一直是1啊。因为每次调用fun2()这个函数,j都被强行置0了。
    static的作用:
    (1) 函数体内 static 变量的作用范围为该函数体,不同于 auto 变量,该变量的内存只被分配一次, 因此其值在下次调用时仍维持上次的值;
    (2) 在模块内的 static 全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问;
    (3) 在模块内的 static 函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明 它的模块内;
    (4) 在类中的 static 成员变量属于整个类所拥有,对类的所有对象只有一份拷贝;
    (5) 在类中的 static 成员函数属于整个类所拥有,这个函数不接收 this 指针,因而只能访问类的static 成员变量。
     
    1.2      for循环的深究
    1.         如程序清单4. 2所示,输出结果是什么?
    程序清单4. 2  for循环
    #include <stdio.h>
     
    int main(int argc, char *argv[])
    {
        int  i = 0 ;
       
        for( i = 0 ,printf("First = %d  " , i ) ;
             printf("Second = %d  " , i ) , i < 10 ;
            i++ , printf("Third = %d  " , i ))
    {
               printf("Fourth = %d  \n" , i) ;
    }
       
        return 0;
    }
    这个题目主要考察对for循环的理解。我们先来看看运行程序会输出什么?
    First = 0  Second = 0  Fourth = 0
    Third = 1  Second = 1  Fourth = 1
    Third = 2  Second = 2  Fourth = 2
    Third = 3  Second = 3  Fourth = 3
    Third = 4  Second = 4  Fourth = 4
    Third = 5  Second = 5  Fourth = 5
    Third = 6  Second = 6  Fourth = 6
    Third = 7  Second = 7  Fourth = 7
    Third = 8  Second = 8  Fourth = 8
    Third = 9  Second = 9  Fourth = 9
    Third = 10  Second = 10  请按任意键继续. . .
    从输出我们就可以知道程序到底是什么运行:
    首先i = 0 , 所以输出:First = 0 ; 接着输出:Second = 0  ;  i < 10 成立,则输出:Fourth = 0 。就此完成第一个循环。接着 i ++ , 此时i = 1 ,输出:Third = 1 ;接着输出:Second = 1 ;i < 10 成立,则输出:Fourth = 1 ······以此类推。
     
    1.3      尺子——sizeof
    1.   如程序清单4. 3所示,sizeof(a),sizeof(b)分别是多少?
    程序清单4. 3  sizeof
    #include <stdio.h>
    int main(int argc, char *argv[])
    {
        char  a[2][3] ;
        short b[2][3] ;
        printf( "sizeof(a) = %d \n" , sizeof( a ) ) ;
        printf( "sizeof(b) = %d \n" , sizeof( b ) ) ;
        return 0;
    }
    这个题目比较简单,由于char 是1个字节、short是2个字节,所以本题答案是:
    sizeof(a) = 6
    sizeof(b) = 12
    请按任意键继续. . .
     
    好的,再来看看如程序清单4. 4所示,sizeof(a),sizeof(b)分别是多少?
    程序清单4. 4  sizeof
    #include <stdio.h>
    int main(int argc, char *argv[])
    {
        char  *a[2][3] ;
        short *b[2][3] ;
        printf( "sizeof(a) = %d \n" , sizeof( a ) ) ;
        printf( "sizeof(b) = %d \n" , sizeof( b ) ) ;
        return 0;
    }
    是数组指针呢,还是指针数组呢?这里涉及*和[]和优先级的问题。我告诉大家的是这两个数组存放的都是指针变量,至于为什么,在后续章节会详细解释,然而指针变量所占的字节数为4字节,所以答案:
    sizeof(a) = 24
    sizeof(b) = 24
    请按任意键继续. . .
     
    1.4      ++ii++
    1.   或许大家都知道,++i是先执行i自加再赋值,但是i++是先赋值再自加,但是还有隐藏在后面的东西呢?
    int i = 0 ;
    int iNumber = 0 ;
    iNumber = (++i) + (++i) + (++i) ;
    C-Free编译输出是:7,有的编译器输出是:9。这两个答案都是对的,编译器不同所不同。7 = 2+2+39=3+3+3。区别在于答案是7的先执行(++i)+(++i)再执行+(++i),但是答案是9的是一起执行。
     
    这只是前奏,先看几个让你目瞪口呆的例子。编译环境是VS2010
    int i = 0 ;
    int iNumber = 0 ;
    iNumber = (i++) + (++i) + (++i) ;
    printf( "iNumber = %d \n" , iNumber ) ;
    这里输出是:4!!!4 = 1+1+2。
     
    int i = 0 ;
    int iNumber = 0 ;
    iNumber = (++i) + (i++) + (++i) ;
    printf( "iNumber = %d \n" , iNumber ) ;
    这里输出是:4!!!4=1+1+2。
     
    int i = 0 ;
    int iNumber = 0 ;
    iNumber = (++i)  + (++i) + (i++) ;
    printf( "iNumber = %d \n" , iNumber ) ;
    这里输出是:6!!!6=2+2+2。
     
    这里至少能说明两个问题,其一,先执行前面两个,再执行第三个;其二,(i++)这个i的自加是最后执行!
    int i = 0 ;
    int iNumber = 0 ;
    iNumber = (++i)  + (i++) + (++i) + (++i) + (i++) ;
    printf( "iNumber = %d \n" , iNumber ) ;
    这个会是多少?!答案是:10!!!10=1+1+2+3+3!
    不同的编译器或许会存在不同的答案,希望读者自行进行验证。
     
    1.5      scanf()函数的输入
    1.   如程序清单4. 5所示,运行程序,当显示Enter Dividend: , 输入的是a,按下Enter之后程序会怎么运行?
    程序清单4. 5  scanf()函数的输入
    #include<stdio.h>
     
    int main(void)
    {
            float fDividend,fDivisor,fResult;
     
            printf("Enter Dividend:");
            scanf("%f",&fDividend);
     
            printf("Enter Divisor:");
            scanf("%f",&fDivisor);
     
            fResult=fDividend/fDivisor;
            printf("Result is: %f\n",fResult);
     
            return 0;
    }
    这个问题有人会说,肯定是显示Enter Divisor:要我输入除数咯。是这样吗?
    答案是:如果你在Enter Dividend:之后输入非数字,按下Enter之后显示的不是Enter Divisor: 要你输入除数,而是程序到此就运行结束,显示一个不确定答案,这个答案每一次都会变。如果你在Enter Divisor:之后输入非数字,按下Enter之后显示的不是Reslut的结果, 而是程序到此就运行结束,显示一个不确定答案,这个答案每一次都会变。
    由于scanf()使用了%f,当输入数字的时候,scanf()将缓冲区中的数字读入fDividend,并清空缓冲区。由于我们输入的并非数字,因此scanf()在读入失败的同时并不会清空缓冲区。最后的的结果是,我们不需要再输入其他字符,scanf()每次都会去读取缓冲区,每次都失败,每次都不会清空缓冲区。当执行下一条scanf()函数读取除数时,由于缓冲区中有数据,因此它不等待用户输入,而是直接从缓冲区中取走数据。
    那么防止输入非数字的程序应该怎样呢?
    #include<stdio.h>
     
    int main( int argc , char *argv[] )
    {
        float   fDividend , fDivisor , fResult ;
        int     iRet ;
        char    cTmp1[ 256 ] ;
     
        printf( "Enter Dividend \n" ) ;
        iRet  =  scanf( "%f" , &fDividend ) ;
     
    if ( 1 == iRet )
    {
            printf( "Enter Divisor \n" ) ;
            iRet = scanf( "%f" , &fDivisor ) ;
            if ( 1== iRet )
    {
                fResult = fDividend / fDivisor ;
                printf( "Result is %f \n" , fResult ) ;
            }
            else
    {
                printf( "Input error ,not a number! \n" ) ;
                gets(cTmp1) ;
                return 1 ;
            }
        }
    else
    {
            printf( "Input error , not a number! \n" ) ;
            gets(cTmp1) ;
            return 1 ;
        }
     
        return 0 ;
    }
     
    1.6      scanf()函数的返回值
    1.         如程序清单4. 6所示,请问输出会是什么?
    程序清单4. 6  scanf()函数的返回值
    int a , b ;
    printf( "%d \n" , scanf("%d%d" , &a , &b) ) ;
    输出输入这个函数的返回值?!答案是:2。只要你合法输入,不管你输入什么,输出都是2。那么我们就要深入解析scanf这个函数。scanf()的返回值是成功赋值的变量数量。
     
    1.7      const作用下的变量
    1.         阅读如程序清单4. 7所示,想想会输出什么?为什么?
    程序清单4. 7  const作用下的变量
    const int   iNumber = 10 ;
    printf(" iNumber = %d  \n" ,  iNumber) ;
    int *ptr = (int *)(&iNumber) ;
    *ptr  = 100 ;
    printf(" iNumber = %d  \n" ,  iNumber) ;
    const的作用在第四章已经详细讲了,这里就不再累赘,答案是:10,10。这里补充一个知识点:
    const int *p                   指针变量p可变,而p指向的数据元素不能变
    int* const p                   指针变量p不可变,而p指向的数据元素可变
    const int* const p            指针变量p不可变,而p指向的数据元素亦不能变
     
    1.8      *ptr++*(ptr++)*++ptr*(++ptr)++(*ptr)(*ptr)++的纠缠不清
    1.         如程序清单4. 8所示程序,输出什么?
    程序清单4. 8  *ptr++
    int  iArray[3] = { 1 , 11 , 22} ;
    int  *ptr     = iArray ;
     
    printf( "*ptr++ = %d \n" , *ptr++ ) ;
    printf( "*ptr   = %d \n" , *ptr   ) ;
    纠结啊,是先算*ptr还是ptr++;还是纠结啊,ptr是地址加1还是偏移一个数组元素!
    这里考查了两个知识点,其一:*与++的优先级问题;其二,数组i++和++i的问题。*和++都是优先级为2,且都是单目运算符,自右向左结合。所以这里的*ptr++和*(ptr++)是等效的。
    首先ptr是数组首元素的地址,所以ptr++是偏移一个数组元素的地址。那么ptr++运算完成之后,此时的ptr是指向iArray[1],所以第二个输出*ptr = 11 。如图4. 1所示。那么倒回来看第一个输出,ptr++是在执行完成*ptr++之后再执行的,所以,*ptr++ = 1 。
    4. 1  ptr++
     
     
     
     
     
     
    如程序清单4. 9所示程序,输出会是什么?
    程序清单4. 9  *++ptr
    int  iArray[3] = { 1 , 11 , 22} ;
    int  *ptr       = iArray ;
     
    printf( "*++ptr = %d \n" , *++ptr ) ;
    printf( "*ptr   = %d \n" , *ptr   ) ;
    这个解释和上面解释差不多,就是++ptr和ptr++的差别,所以这里的两个输出都是:11。同样的道理,*++ptr和*(++ptr)是等效。
     
    再如程序清单4. 10所示,输出又会是什么?
    程序清单4. 10  *ptr++
    int  iArray[3] = { 1 , 11 , 22} ;
    int  *ptr       = iArray ;
     
    printf( "(*ptr)++ = %d \n" , (*ptr)++ ) ;
    printf( "*ptr   = %d \n" , *ptr   ) ;
    这个的输出是:1,2。原因请读者分析。
    <ignore_js_op>

    4.jpg (94.96 KB, 下载次数: 6)

     

    图4.1 ptr++

    图4.1  ptr++
  • 相关阅读:
    android 4种启动模式
    android定位
    android GestureDetector 手势基础
    Android 软键盘盖住输入框的问题
    Android Fragment应用实战,使用碎片向ActivityGroup说再见
    Android Fragment完全解析,关于碎片你所需知道的一切
    setImageResource和setBackgroundResource的區別
    【Android面试】Android面试题集锦 (陆续更新)(最新2012-6-18) eoe上看到的
    Android内存性能优化(内部资料总结)
    关于BFC
  • 原文地址:https://www.cnblogs.com/lijunlin/p/3242005.html
Copyright © 2020-2023  润新知