[不懂就问] C 语言字符串指针的问题^-^ - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
raingolee
V2EX    C

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

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

    最近在看 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     Solana     4012 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 27ms UTC 00:08 PVG 08:08 LAX 17:08 JFK 20:08
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86