签到

05月06日
尚未签到

共有回帖数 0

    人心难测

    等级:
    本文代码编写编译运行的环境:[Mac-10.7.1 Lion Intel-based]
    Q: 有的时候总是发现一个数组的字符串可以修改,但是如果使用字符串字面量就不能修改,这是为什么? [cpp] view plaincopy

    #include stdio.h

    int main()
    {
    char buf[] = "hello";
    char *str = "hello";

    buf[0] = 'a';
    str[0] = 'a';
    return 0;
    }

    Q: 那么怎么证明char buf[] = "hello"; 中buf保存的数据hello在栈中呢? A: 使用gcc -S char_string.c得到它的汇编形式: [cpp] view plaincopy

    movb L_.str(%rip), %al
    movb %al, -14(%rbp)
    movb L_.str+1(%rip), %al
    movb %al, -13(%rbp)
    movb L_.str+2(%rip), %al
    movb %al, -12(%rbp)
    movb L_.str+3(%rip), %al
    movb %al, -11(%rbp)
    movb L_.str+4(%rip), %al
    movb %al, -10(%rbp)
    movb L_.str+5(%rip), %al
    movb %al, -9(%rbp)
    leaq L_.str(%rip), %rax
    movq %rax, -24(%rbp)
    movb $97, -14(%rbp)
    其中L_.str为:[cpp] view plaincopy

    L_.str:
    .asciz "hello" 可以看到,上面的汇编代码中的%rbp即为和堆栈基址相对应的寄存器。

    Q: 常常看到,如果arr是个数组,arr和*(arr + i)是等同的, 这里的i可以为负数吗? A: 是的。数组就是个数据块,至于是取arr更高地址还是更低地址的数据,这有程序员决定。当然,arr表示数组的初始地址,取比它更低的地址可能不是程序员的本来意图,小心为之。 [cpp] view plaincopy

    #include stdio.h
    #define PRINT_D(intValue) printf(#intValue" is %dn", (intValue));

    int main()
    {
    int arr[] = {1, 2};
    int n = 3;
    PRINT_D(arr[0])
    PRINT_D(arr[-1])
    return 0;
    } 编译运行:



    [cpp] view plaincopy

    #include stdio.h
    #define PRINT_P(pointer) printf("%10s is %pn", #pointer, (pointer));

    int main (int argc, const char * argv[])
    {
    int arr[2][3] = {1, 2, 3, 4, 5, 6};
    PRINT_P(arr)
    PRINT_P(&arr)
    PRINT_P(arr[0])
    PRINT_P(&arr[0])
    PRINT_P(arr[1])
    PRINT_P(&arr[1])

    return 0;
    }




    #include stdio.h
    #define PRINT_P(pointer) printf("%10s is %pn", #pointer, (pointer));
    #define PRINT_D(intValue) printf(#intValue" is %dn", (intValue));

    void print_arr(int *arr, int size)
    {
    int i = 0;
    for(; i  size; ++i)
    PRINT_D(arr)
    }

    int main (int argc, const char * argv[])
    {
    int arr[] = {1, 2, 3};
    print_arr(arr, sizeof(arr) / sizeof(arr[0]));
    return 0;
    }
    因为数组元素是整形的,所以数组地址为整形指针,即为上面的int *, 还有个参数size表示数组的大小。
    Q: 既然数组的个数可以用sizeof(arr) / sizeof(arr[0])来表示,那么参数size不就是不必要的吗,print_arr函数里面直接用这个表达式不就可以得到数组的大小了么? A: 测试下。 [cpp] view plaincopy

    #include stdio.h
    #define PRINT_P(pointer) printf("%10s is %pn", #pointer, (pointer));
    #define PRINT_D(intValue) printf(#intValue" is %dn", (intValue));

    void print_arr(int *arr)
    {
    int i = 0;
    for(; i  sizeof(arr) / sizeof(arr[0]); ++i)
    PRINT_D(arr)
    }

    int main (int argc, const char * argv[])
    {
    int arr[] = {1, 2, 3};
    print_arr(arr);
    return 0;
    }



    0x0000000100000ded print_arr+29: lea 0xa4(%rip),%rdi # 0x100000e98
    0x0000000100000df4 print_arr+36: movslq -0xc(%rbp),%rax
    0x0000000100000df8 print_arr+40: mov -0x8(%rbp),%rcx
    0x0000000100000dfc print_arr+44: mov (%rcx,%rax,4),%esi
    0x0000000100000dff print_arr+47: mov $0x0,%al
    0x0000000100000e01 print_arr+49: callq 0x100000e6e dyld_stub_printf
    0x0000000100000e06 print_arr+54: mov %eax,-0x10(%rbp)
    0x0000000100000e09 print_arr+57: mov -0xc(%rbp),%eax
    0x0000000100000e0c print_arr+60: add $0x1,%eax
    0x0000000100000e0f print_arr+63: mov %eax,-0xc(%rbp)
    0x0000000100000e12 print_arr+66: jmp 0x100000de3 print_arr+19
    0x0000000100000e14 print_arr+68: add $0x10,%rsp
    0x0000000100000e18 print_arr+72: pop %rbp
    0x0000000100000e19 print_arr+73: retq
    可以看到第七行位置的cmp指令和第八行的jae指令,表示如果循环变量i大于或者等于2,那么跳到结束位置。也就是说,这个函数里面,把arr数组大小 看成了2, 这是为什么呢?这是因为编译器编译print_arr函数代码时,根本不知道传入int * arr参数的到底是个数组还是指针,所以sizeof(arr) / sizeof(arr[0])得到的是sizeof(int *) / sizeof(int)的值(笔者平台得到的是2)。其实这也是数组名作为参数的一个可能引发错误的地方。
    Q: 那么如果把参数形式int *arr改为int arr[]就能将arr当成数组了,代码就会正确执行? A: 很可惜,c语言的语法决定了任何代码都会编译成确定指令的东西,和上面说的一样,print_arr依然不知道外部传入参数arr的数组或者指针到底有多大,sizeof(arr) / sizeof(arr[0])最终又被得到一个诡异的数据。
    Q: 那该怎么办? A: 就另外传入一个参数为传入数组的元素个数即可。
    Q: 关于二维数组,经常看到一些很诡异的样式,到底怎么很好地理解? A: 形如如下代码: [cpp] view plaincopy

    #include stdio.h
    #define PRINT_P(pointer) printf("%10s is %pn", #pointer, (pointer));
    #define PRINT_D(intValue) printf(#intValue" is %dn", (intValue));

    int main (int argc, const char * argv[])
    {
    int arr[] = {1, 2};
    int (*p_arr)[2] = &arr;
    PRINT_D(**p_arr)

    return 0;
    }




    #include stdio.h
    #define PRINT_P(pointer) printf("%10s is %pn", #pointer, (pointer));
    #define PRINT_D(intValue) printf(#intValue" is %dn", (intValue));

    int main (int argc, const char * argv[])
    {
    int arr[][2] = {{1, 2}, {3, 4}}; int (*p_arr)[2] = &arr[0];
    PRINT_D(p_arr[1][1])
    p_arr = &arr[1];
    PRINT_D(p_arr[0][1])

    return 0;
    }












    #include stdio.h

    int (*func1)(int *arg);
    int (*func2)(int *arg);
    int (*func3)(int *arg);

    int main (int argc, const char * argv[])
    {
    int (*p[3])(int *arg) = {func1, func2, func3};

    return 0;
    }
    第二个:*func表示func是一个指针,后面的int *p表示,它是一个函数指针,参数为int *p, 左侧一个星号,表示返回值是个指针,右侧[3]表示返回值是个3个元素的数组,每个元素都是指针,最左侧的int表示返回值的数组元素为整形。总 结下:func是个函数指针,参数为int *p, 返回值为包含5个元素的数组,且为指针。 第三个: 类似第一个,不过func多了一个指针类型。总结:func是一个指针,它指向一个数组,数组元素个数为3,每个元素都是一个函数指针,函数指针参数为int *p, 返回值为int.
    #include stdio.h

    int main()
    {
    char buf[] = "hello";
    char *str = "hello";

    buf[0] = 'a';
    str[0] = 'a';
    return 0;
    }



    这个代码 发现一个有趣的现象
    cl编译器对于这个代码可以编译运行成功

    详细讨论见 htt放和谐p://放和谐topic.放和谐csdn.ne放和谐t/t/20060放和谐223/00/45放和谐71769.html

    楼主 2016-03-31 14:39 回复

共有回帖数 0
  • 回 帖
  • 表情 图片 视频
  • 发表

登录直线网账号

Copyright © 2010~2015 直线网 版权所有,All Rights Reserved.沪ICP备10039589号 意见反馈 | 关于直线 | 版权声明 | 会员须知