c语言核心技术 一

================================================

C语言编译分析记号,有一个原则是尽可能靠左合并符号使其得到符合语法的记号,所以
a+++b 会被解释为 (a++)+b 而不是 a+(++b)

================================================

函数作用域和语句块作用域:
一直以为函数内部申明的标识符其作用域就叫做函数作用域,其实应该是语句块作用域,它和函数作用域还是有区别的,语句块作用域的作用范围是从申明处开始,到包含这个申明的最小语句块结束为止。即使你申明在函数的最开头,他依然是属于语句块作用域,什么样的标识符具有函数作用域呢? 嘿嘿,goto用的Label

这个从目标代码的角度来看就很明显了,标识符这些基本上是放在寄存器或堆栈内,没有赋值之前自然没法使用,Label直接就翻译成一个地址,写在jump命令里,当然在函数内部哪都可以用了。

================================================

类似 x+=y之类的复合赋值运算表达式和 x=x+y这样的表达式效果上有什么区别么?区别在于x+=y这种形式里,左值x只被计算了一次,如果x是一个有副作用的表达式,着两种表达方式可能就不等价了。

逗号运算符的表达式操作顺序是从左到右,所以整个运算结果是最后一个表达式的结果,计算的顺序可以得到保证。 但是在函数调用时,函数的参数列表里面的逗号“并不是”逗号运算符,所以函数调用时,参数表达式的计算顺序C语法是没有保证的。

================================================

元素修饰符:
类似初始化结构体时,C99里面可以用成员修饰符(类似 .member = value 的形式)来初始化特定的结构元素一样,数组的初始化可以用:
int A[30] = {1, 2, [15] = 15, 16 };
这样的形式来初始化第1,2,15,16个元素。

================================================

对数组采用指针来操作通常可能比采用下标索引来操作要更有效率,按数组操作可能涉及到计算索引再加基地址的问题,指针本身就可以递增。

这个要看下标索引的计算复杂度,如果直接就是类似循环变量i这样,其实下标表示还更快

测试一下:

int main()
{
        int a[40];
        int *pa=&a[0];
        int i,j;

        for(i=0; i<10; i++)
                a[i] = 3;

        for(i=0; i<10; i++){
                *pa++ = 4;
        }
        return 0;
}

循环部分汇编代码,指针版还要多一条命令:

------


.L3:
        movl    -12(%ebp), %eax # i, i.0
        movl    $3, -176(%ebp,%eax,4)   #, a
        addl    $1, -12(%ebp)   #, i
.L2:
        cmpl    $9, -12(%ebp)   #, i
        jle     .L3     #,


.L6:
        movl    -16(%ebp), %eax # pa, pa
        movl    $4, (%eax)      #,* pa
        addl    $4, -16(%ebp)   #, pa
        addl    $1, -12(%ebp)   #, i
.L5:
        cmpl    $9, -12(%ebp)   #, i
        jle     .L6     #,


如果下标计算比较复杂,指针就要更快了


int main()
{
        int a[40];
        int *pa=&a[0];
        int i,j;

        for(i=0; i<10; i++)
                a[3*i] = 3;

        for(i=0; i<10; i++){
                *pa = 4;
                pa += 3;
        }

        return 0;
}

循环部分汇编代码,指针版要少两条命令:
--------------

.L3:
        movl    -12(%ebp), %eax # i, i
        movl    %eax, %edx      # i, i
        addl    %edx, %edx      # i
        leal    (%edx,%eax), %eax       #, D.1786
        movl    $3, -176(%ebp,%eax,4)   #, a
        addl    $1, -12(%ebp)   #, i
.L2:
        cmpl    $9, -12(%ebp)   #, i
        jle     .L3     #,
        movl    $0, -12(%ebp)   #, i
        jmp     .L5     #


.L6:
        movl    -16(%ebp), %eax # pa, pa
        movl    $4, (%eax)      #,* pa
        addl    $12, -16(%ebp)  #, pa
        addl    $1, -12(%ebp)   #, i
.L5:
        cmpl    $9, -12(%ebp)   #, i
        jle     .L6     #,

 

================================================

弹性结构成员

C99允许结构体的最后一个成员属于不完整的数组类型,如:
typedef Struct { int len; int data[]} A_t;
分配内存的时候,要用类似
malloc( sizeof(A_t) + 10*sizeof(int) ); 
的方式来为data数组分配内存。

这种情况下,sizeof计算 A_t 的size的时候,这个弹性结构成员的大小是不会被计算的。
--- to do --- 写一个小代码验证一下

通常如非特殊必要,用指针的方式来实现结构体内的空间可变数据项,要比使用这种弹性结构成员更合适。

================================================

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值