C语言学习笔记3

本文探讨了大端和小端对齐的概念及其应用场景,并分析了不同编译器环境下函数参数的进栈顺序和计算顺序。此外,还讨论了函数默认参数的使用差异。

函数参数的进栈顺序和参数计算顺序

大端对齐和小端对齐

unsigned int a = 0x12345678;

* 大端:就是把数值高位数值放在内存的低字节,把数值的低位数放在内存的高位字节(低存高,高存低)
案例:

地址0x00c10x00c20x00c30x00c4
数值0x120x340x560x78

* 小端:就是把熟知的高位数值放在内存的高位字节,吧数值的地位数值放在内存的地位字节(高存高,低存低)
案例:

地址0x00c10x00c20x00c30x00c4
数值0x780x560x340x12

大小端对齐应用:
* 大端:IBM、Sun的服务器是大端,初代的苹果电脑PowerMac也是大端;
* 小端:x86架构的CPU是小端,ARM(英国)架构大多也是小端;

函数参数的进栈顺序

#include <stdio.h>

void func(int a, int b, int c)
{
    printf("a = %d : [%p]\n", a, &a);
    printf("b = %d : [%p]\n", b, &b);
    printf("c = %d : [%p]\n", c, &c);
}

int main(void)
{
    func(100,200,300);

    return 0;
}

Win32下的编译结果:

    a = 100 : [0x5fcad0] // +8
    b = 200 : [0x5fcad8] // +8
    c = 300 : [0x5fcae0]

Ubuntu Linux下编译结果:

    a = 100 : [0x5fcad0] // +4
    b = 200 : [0x5fcad8] // +4
    c = 300 : [0x5fcae0] 

C程序在执行时:” 先入栈的数据在栈底,为高地址;后入栈的数据在栈顶,为地地址 ”
* 可以总结为 ” 先进后出,后进先出 ”
Mac OS X下的编译结果:

    a = 100 : [0x5fcae0] // -4
    b = 200 : [0x5fcad8] // -4
    c = 300 : [0x5fcad0] 

Mac OS X 使用的编译期是LLVM Clang,这个编译器会优化一些代码,导致函数参数的进栈顺序是” 从左往右 “

函数参数计算顺序

#include <stdio.h>

int main(void)
{
    int a = 10, b = 20, c = 30;
    printf("%d, %d, %d\n",a + b + c, (b = b * 2),(c = c * 2));
    return 0;
}

Win32 MSVC 下编译结果是

    110, 40, 60

Ubuntu Linux GUN GCC 下编译结果是

    110, 40, 60

Mac OS X LLVM Clang下编译结果

    60, 40, 60

由此可知:
* 在MSVC 和GCC编译器下,参数的计算顺序使” 从右往左 “,和其参数进栈顺序相同
* 在LLVM Clang编译器下,参数的计算顺序使” 从左往右 “,和其参数进栈顺序相同
* 所以,当一个函数参数有多个的时候,C/C++语言设计者没有规定函数参数的进栈顺序和计算顺序是哪一种,这些是由编译器厂商自己规定的

函数的默认参数

#include <stdio.h>

void func(int a, int b, int c = 300)
{
    printf("a = %d : [%p]\n", a, &a);
    printf("b = %d : [%p]\n", b, &b);
    printf("c = %d : [%p]\n", c, &c);
}

int main(void)
{
    func(100, 200);
    return 0;
}

上面的写法,在Ubuntu Linux和MSVC下是不允许,会报语法错误,实参数量要匹配型参数量,
也不允许函数参数在参数列表内赋值。
但是在LLVM Clang下是允许的,这是由于编译器支持这么干。

C编译器有很多,除了GCC、MSVC、Clang外,
还有一种编译叫ICC,这个编译器是Intel公司做的,会优化代码在Intel CPU上的效率。
还有Turbo C,是美国borland公司的产品,现在估计已经被淘汰了。TC

重点:
由于C语言编译器厂商规范不统一,所以我们在写代码的时候,不要写类似的”UB语句”。
“Undefined Behavior” 代码行为未定义:
如果你的程序违反了C标准的某些规则,那么具体会发生什么,C语言没有定义,
也就是说会得到一些奇怪的结果,都是有可能的。比如说整数溢出就是这种情况。

“Unspeakable Behavior”方案未定义:
C标准可能给你的代码提供了好几种可选方案,但是具体用哪一种,C标准没有定义。
比如说上面的例子,函数参数的计算顺序和进栈顺序不一致,而且任何计算方式都行。

写程序的态度要严谨,我们的代码是给人看的,所以为了将来的代码维护和升级,尽量不要用表达过于笼统的语句。

反面教材:

#include <stdio.h>
int main(void)
{
    int i = 0;
    int a = i + ++i;
    printf("%d\n", a);
    return 0;
}

上述代码最后的值会因为编译器不同而不同。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值