V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
raingolee
V2EX  ›  C

[不懂就问] C 语言字符串指针的问题^-^

  •  2
     
  •   raingolee · 2015-10-28 20:47:58 +08:00 · 1885 次点击
    这是一个创建于 3312 天前的主题,其中的信息可能已经有所发展或是发生改变。

    最近在看 head first c ,发现了书上有个歧义的地方,

    因此上来 v2ex.py 请教各位大兄弟,感谢各位现身教学

    void testChar(char msg[])
    {
        printf("the msg is %s", msg);
        prinff("the msg cost %s bytes", sizeof(msg));
    }
    
    char quote[] = "hi iv2ex bro";
    testChar(quote);
    

    第二行输出的到底是 4 个字节还是字符串的长度呢?

    第 1 条附言  ·  2015-10-28 21:20:41 +08:00

    刚刚忘了补充完整,和着急写错了两个地方,现更正一下, 32bit 系统

    #include <stdio.h> 
    
    void testChar(char msg[]) 
    { 
        printf("the msg is %s", msg); 
        printf("the msg cost %d bytes", sizeof(msg)); 
    } 
    
    int main() 
    { 
        char quote[] = "hi iv2ex bro"; 
        testChar(quote); 
    }
    
    第 2 条附言  ·  2015-10-29 20:43:59 +08:00

    综合大家的回答,和提供的资料数据,楼主小白终于搞清楚问题,谢谢大家的热心帮助,

    总结一下:
    1. 数组变量可以被用作指针
    2. 数组变量指向数组中第一个元素
    3. 如果把函数参数声明为数组,它会被当做指针处理
    4. sizeof 是一个运算符,而不是函数,可以对某种数据类型使用

    52 条回复    2015-10-29 21:21:21 +08:00
    Smilecc
        1
    Smilecc  
       2015-10-28 20:52:16 +08:00
    这里的 quote 实际上是个字符数组, sizeof 会返回这个数组所占的字节数
    raingolee
        2
    raingolee  
    OP
       2015-10-28 20:56:47 +08:00
    @Smilecc 我的编译器同样是返回这个数组所占的字节数,但是 head first c 上 53 页写着返回编译器一个 int 的大小
    zhicheng
        3
    zhicheng  
       2015-10-28 20:58:54 +08:00 via Android
    会 Crash
    11
        4
    11  
       2015-10-28 20:59:32 +08:00
    嗯 会 crash
    SErHo
        5
    SErHo  
       2015-10-28 21:00:02 +08:00
    @raingolee printf 写成 prinff 了, sizeof 用 %lu 格式,返回指针的大小, 32 位是 4 , 64 位是 8 。
    xufang
        6
    xufang  
       2015-10-28 21:01:54 +08:00
    唉,”小学生“太多了,而楼上这种只给个答案的也是毛病。楼主我建议你自学下汇编,然后把这段代码看下汇编就明白了。记得用 32 位平台。
    xufang
        7
    xufang  
       2015-10-28 21:02:15 +08:00
    ...,被插 2 楼
    SErHo
        8
    SErHo  
       2015-10-28 21:03:10 +08:00
    @xufang 啥问题都扯上汇编?这是 C 语言的基本常识,和汇编有什么关系。

    $ clang -o test test.c
    test.c:6:44: warning: sizeof on array function parameter will return size of
    'char *' instead of 'char []' [-Wsizeof-array-argument]
    printf("the msg cost %lu bytes", sizeof(msg));
    ^
    test.c:3:20: note: declared here
    void testChar(char msg[])
    ^
    1 warning generated.

    不知道楼主用的是什么编译器。。。
    Smilecc
        9
    Smilecc  
       2015-10-28 21:04:16 +08:00
    误人子弟了,是我想当然了,说声对不起。
    WalkingEraser
        10
    WalkingEraser  
       2015-10-28 21:05:55 +08:00
    数组名作参数会退化为指针,即 4 或 8 。会 crash 是因为第二个格式化输出有问题,应该是%d
    raingolee
        11
    raingolee  
    OP
       2015-10-28 21:05:59 +08:00
    @zhicheng
    @11
    你们两个顽皮了,

    不好意思呀,写错了函数名

    sed -i 's/prinff/printf/g'
    raingolee
        12
    raingolee  
    OP
       2015-10-28 21:07:55 +08:00
    @SErHo
    感谢回答
    不好意思呀,写错了函数名
    sed -i 's/prinff/printf/g'
    --
    用的是 gcc ,但是我返回的却是字符串的长度,难道是我姿势错了?
    11
        13
    11  
       2015-10-28 21:08:53 +08:00
    @raingolee 写错函数名是不能编译,然后改了后,因为你用的 %s 所以会 crash 。。
    xufang
        14
    xufang  
       2015-10-28 21:09:15 +08:00
    @SErHo 说这话的时候,你先看看自己的答案有没有问题。。。
    0ver1oad
        15
    0ver1oad  
       2015-10-28 21:10:17 +08:00
    如果是 32bit 系统就是 4 ,因为性能考虑,数组作为参数会变为指针
    raingolee
        16
    raingolee  
    OP
       2015-10-28 21:13:46 +08:00
    @WalkingEraser
    @11
    @xufang
    @0ver1oad

    ``` c
    #include <stdio.h>

    void testChar(char msg[])
    {
    printf("the msg is %s", msg);
    printf("the msg cost %d bytes", sizeof(msg));
    }

    int main()
    {
    char quote[] = "hi iv2ex bro";
    testChar(quote);
    }
    ```

    不好意思各位,刚刚着急了一下,现在重新编译了, 32bit 系统,返回的却是字符串的长度,怎么破?
    handelxh
        17
    handelxh  
       2015-10-28 21:20:14 +08:00
    sizeof() 出来的是 char 指针的大小,这个问题有什么可讨论的?
    raingolee
        18
    raingolee  
    OP
       2015-10-28 21:22:27 +08:00
    @handelxh 不是讨论,是我不懂,那答案是?
    raingolee
        19
    raingolee  
    OP
       2015-10-28 21:26:22 +08:00
    @0ver1oad
    head first c 上 59 页写着返回的字符串的大小,到底孰对孰错啊
    WalkingEraser
        20
    WalkingEraser  
       2015-10-28 21:29:20 +08:00
    @raingolee vc 和 GCC 都返回的是指针长度的说…
    11
        21
    11  
       2015-10-28 21:29:42 +08:00
    @raingolee 不科学啊

    ➜ ~ gcc -m32 test.c
    ➜ ~ ./a.out
    the msg is hi iv2ex brothe msg cost 4 bytes#


    gcc version 4.8.2 (Ubuntu 4.8.2-19ubuntu1)
    zhicheng
        22
    zhicheng  
       2015-10-28 21:33:17 +08:00
    @raingolee
    main 函数里 sizeof 是字符串长度, testChar 里 sizeof 是 char * 的长度。
    如果有其它的结果或者可能,请贴出完整的代码。
    aheadlead
        23
    aheadlead  
       2015-10-28 21:45:36 +08:00
    参见《 C 专家编程》 Chapter 4 The Shocking Truth: C Arrays and Pointers Are NOT the Same!
    raingolee
        24
    raingolee  
    OP
       2015-10-28 21:45:44 +08:00
    raingolee
        25
    raingolee  
    OP
       2015-10-28 21:47:16 +08:00
    @aheadlead 非常有帮助的回答,我去查看一下,谢谢
    zhicheng
        26
    zhicheng  
       2015-10-28 21:51:17 +08:00
    @raingolee
    书中内容完全正确。
    raingolee
        27
    raingolee  
    OP
       2015-10-28 21:52:59 +08:00
    @zhicheng
    那大伙回答的却是返回编译器 int 的大小?
    SErHo
        28
    SErHo  
       2015-10-28 21:54:29 +08:00   ❤️ 1
    @raingolee 哎,你截的图上不是说的很明白吗?当数组通过函数参数后,在函数体内就是指针了,此时 sizeof 返回指针的大小,不经过函数参数, sizeof 就会是数组长度。其实 C 语言根本就没有数组参数这一说法。看看这个帖子: https://lkml.org/lkml/2015/9/3/428
    zhicheng
        29
    zhicheng  
       2015-10-28 21:55:20 +08:00
    @raingolee

    warning: sizeof on array function parameter will return size of 'char *' instead of 'char []'

    你继续看书,不要再问了。
    SErHo
        30
    SErHo  
       2015-10-28 21:55:46 +08:00
    @raingolee 因为在 32 位系统上,指针的大小就是 int 类型的大小, 4 个字节。
    raingolee
        31
    raingolee  
    OP
       2015-10-28 22:00:56 +08:00
    @SErHo
    因为在 32 位系统上,指针的大小就是 int 类型的大小, 4 个字节
    这个我知道
    ---
    好的,我继续看书去,打扰各位了,谢谢
    billlee
        32
    billlee  
       2015-10-28 22:04:48 +08:00   ❤️ 2
    C 语言中,函数参数中的数组会退化为指针,所以 sizeof(msg) 返回的是一个指针的长度
    从实现上理解, C 语言不会在运行时记录数组的长度信息, sizeof 在编译时由编译器计算结果,作为一个函数参数,在编译时编译器不可能知道传进来的数组是什么长度,只能把它当成指针处理。这就是典型的静态语言。
    raingolee
        33
    raingolee  
    OP
       2015-10-28 22:07:43 +08:00
    @SErHo
    哎,你截的图上不是说的很明白吗?当数组通过函数参数后,在函数体内就是指针了,此时 sizeof 返回指针的大小,不经过函数参数, sizeof 就会是数组长度。其实 C 语言根本就没有数组参数这一说法。看看这个帖子: https://lkml.org/lkml/2015/9/3/428

    ---
    我想明白了,你回答的真棒,真心感谢!!!
    qian19876025
        34
    qian19876025  
       2015-10-28 22:08:03 +08:00   ❤️ 1
    @raingolee 不是返回 INT 大小 而是返回 编译器所支持的指针 所占内存的大小
    在 C 里面 指针大小是固定的 不关是什么类型的指针 其占用的内存都一样
    为啥要这样 因为 这个指针存的是一个地址 什么地址 内存的地址 这个内存地址字长是固定的
    为啥要固定 实现起来方便 简单
    那为传参的时候对数组降级 其目的就是为了节省内存 方便编译器实现起来容易
    使用 KISS 来理解 C 编译器的行为 以上是我对指针的理解
    raingolee
        35
    raingolee  
    OP
       2015-10-28 22:08:40 +08:00
    @billlee

    真心感谢认真回答的大胸弟!!!
    我明白了
    suikator
        36
    suikator  
       2015-10-28 22:12:15 +08:00
    #include <stdio.h>

    int main() {
    int a[] = {1, 2, 3};
    printf("%d\n", sizeof(a));

    char b[] = {'1', '2', '3'};
    printf("%d\n", sizeof(b));

    char c[] = "123";
    printf("%d\n", sizeof(c));
    return 0;
    }


    哈哈
    suikator
        37
    suikator  
       2015-10-28 22:13:41 +08:00
    我已经习以为常了,哈哈
    batstying
        38
    batstying  
       2015-10-28 22:22:31 +08:00   ❤️ 1
    10 楼说的对呀,补充一句,其实在编译的时候,编译器就已经生成了 4 或者 8 来替换 sizeof(msg),而这个 sizeof(msg)表示指针这种类型占用的字节数~~,如果要求字符串可以用库函数 lstrlen ,记得字符串 0 结尾,长度+1 哦~
    suikator
        39
    suikator  
       2015-10-28 22:27:09 +08:00   ❤️ 1
    #include <stdio.h>

    int main() {
    int a[] = {1, 2, 3};
    printf("%d,%d,%d=>%d\n", a[0],a[1],a[2],sizeof(a));

    char b[] = {'1', '2', '3'};
    printf("%c,%c,%c=>%d\n", b[0],b[1],b[2],sizeof(b));

    char c[] = "123";
    printf("%c,%c,%c=>%d\n", c[0],c[1],c[2],sizeof(c));

    int temp_d = 1;
    int *d = &temp_d;
    *(d+1)=2;
    *(d+2)=3;
    printf("%d,%d,%d=>%d\n", d[0],d[1],d[2],sizeof(d));

    char temp_e = '1';
    char *e = &temp_e;
    *(e+1)='2';
    *(e+2)='3';
    printf("%c,%c,%c=>%d\n", e[0],e[1],e[2],sizeof(e));

    char *f = "123";
    printf("%c,%c,%c=>%d\n", f[0],f[1],f[2],sizeof(f));

    return 0;
    }
    差不多就这些了
    0ver1oad
        40
    0ver1oad  
       2015-10-28 23:22:19 +08:00
    @raingolee 额, OSX 和 Linux 特地试了都是返回 8(64 位操作系统)
    ChiangDi
        41
    ChiangDi  
       2015-10-28 23:27:57 +08:00
    就是你把数组名作为参数传进去再用 sizeof 就不是返回数组元素的个数了。
    fractal314
        42
    fractal314  
       2015-10-28 23:53:07 +08:00 via Android   ❤️ 1
    函数参数是数组 /结构体 /类的时候,实际传递进去的是他们的地址
    raingolee
        43
    raingolee  
    OP
       2015-10-29 00:06:20 +08:00
    @fractal314
    恩,最简单实用的回答了
    xieyudi1990
        44
    xieyudi1990  
       2015-10-29 09:44:16 +08:00
    @fractal314 不对. 我刚刚实际调试了下, 至少结构体是通过寄存器 /栈来传参数的. 而且 C 里边没有类这种东西 (当然你可以自己实现).

    比如 x86_64 下这段代码
    -------------------------------
    #include <stdio.h>

    typedef struct {
    int i;
    } S_t;

    int test(S_t S)
    {
    printf("%d\n", S.i);
    }

    int main(int argc, char *argv[])
    {
    S_t S = {1};
    test(S);
    return 0;
    }
    -------------------------------
    用 x86_64-unknown-linux-gnu-gcc 编译之后
    0000000000400528 <main>:
    push rbp
    mov rbp,rsp
    sub rsp,0x20
    mov DWORD PTR [rbp-0x14],edi
    mov QWORD PTR [rbp-0x20],rsi
    mov DWORD PTR [rbp-0x10],0x1
    mov eax,DWORD PTR [rbp-0x10]
    mov edi,eax
    call 400506 <test>
    mov eax,0x0
    leave
    ret
    nop

    在 call test 的时候是直接传的值, 而不是引用 (地址). 因为 x86_64 前 6 个参数是通过 rdi, rsi, rdx, rcx, r8, and r9 来传参的. 这时的 info reg:
    ...
    rdi 0x1 1
    ...

    而 test 里边显然也是将 rdi 作为 printf 的第二个参数 (rsi):
    push rbp
    mov rbp,rsp
    sub rsp,0x10
    mov DWORD PTR [rbp-0x10],edi
    mov eax,DWORD PTR [rbp-0x10]
    mov esi,eax
    mov edi,0x4005d4
    mov eax,0x0
    call 4003e0 <printf@plt>
    nop
    leave
    ret
    erenno1
        45
    erenno1  
       2015-10-29 10:53:50 +08:00   ❤️ 1
    1. sizeof 是运算符,不是函数
    2. sizeof 如果和被 sizeof 的对象处于同一作用域,例如在 main 函数里对 quota 执行 sizeof ,是编译时可以预见的结果,编译器能判断出 quota 是数组
    3. 函数调用属于运行时的行为,对函数传入的指针 /数组参数执行 sizeof ,统一被认定为对指针执行 sizeof ;
    4. void ff ( char *quota ) 和 void ff (char quota[]) 是等同的,编译器将后者归至前者的语意,同 3
    erenno1
        46
    erenno1  
       2015-10-29 10:56:56 +08:00
    更正下:
    3. 函数调用属于运行时的行为,因此在编译期,对函数传入的指针 /数组参数执行 sizeof ,统一被认定为对指针执行 sizeof ;

    总之, sizeof 操作应该是编译时已经搞定的事情
    fxxkgw
        47
    fxxkgw  
       2015-10-29 14:29:07 +08:00
    能真正搞明白指针和地址的 C 语言一般不会差
    ybh37
        48
    ybh37  
       2015-10-29 14:37:37 +08:00
    C 语言的学习,不能死记所谓的概念。
    指针这些东西,记概念是不对的,看过汇编代码后一般会大悟。
    ybh37
        49
    ybh37  
       2015-10-29 14:40:32 +08:00
    @xufang 懂的,自然懂。 不懂的,…… 呵呵~
    e2real
        50
    e2real  
       2015-10-29 16:29:45 +08:00
    论调试技能的重要性。
    Keyes
        51
    Keyes  
       2015-10-29 17:39:47 +08:00
    @xufang
    @ybh37

    懂的自然懂+1
    学 C 语言同时看着汇编是不会有错的,代码往调试器里一丢什么秘密都出来了,建议楼主不要光盯着 C 语言看,有的时候汇编比 C 更清晰,而且会让你更清晰的认识到 C 里面的某些特性“到底为什么是这样的”
    handelxh
        52
    handelxh  
       2015-10-29 21:21:21 +08:00
    @raingolee 就是 sizeof(char *)的大小
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   968 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 21:40 · PVG 05:40 · LAX 13:40 · JFK 16:40
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.