最近在看 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 个字节还是字符串的长度呢?
刚刚忘了补充完整,和着急写错了两个地方,现更正一下, 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);
}
综合大家的回答,和提供的资料数据,楼主小白终于搞清楚问题,谢谢大家的热心帮助,
总结一下:
1. 数组变量可以被用作指针
2. 数组变量指向数组中第一个元素
3. 如果把函数参数声明为数组,它会被当做指针处理
4. sizeof 是一个运算符,而不是函数,可以对某种数据类型使用
1
Smilecc 2015-10-28 20:52:16 +08:00
这里的 quote 实际上是个字符数组, sizeof 会返回这个数组所占的字节数
|
2
raingolee OP @Smilecc 我的编译器同样是返回这个数组所占的字节数,但是 head first c 上 53 页写着返回编译器一个 int 的大小
|
3
zhicheng 2015-10-28 20:58:54 +08:00 via Android
会 Crash
|
4
11 2015-10-28 20:59:32 +08:00
嗯 会 crash
|
5
SErHo 2015-10-28 21:00:02 +08:00
@raingolee printf 写成 prinff 了, sizeof 用 %lu 格式,返回指针的大小, 32 位是 4 , 64 位是 8 。
|
6
xufang 2015-10-28 21:01:54 +08:00
唉,”小学生“太多了,而楼上这种只给个答案的也是毛病。楼主我建议你自学下汇编,然后把这段代码看下汇编就明白了。记得用 32 位平台。
|
7
xufang 2015-10-28 21:02:15 +08:00
...,被插 2 楼
|
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. 不知道楼主用的是什么编译器。。。 |
9
Smilecc 2015-10-28 21:04:16 +08:00
误人子弟了,是我想当然了,说声对不起。
|
10
WalkingEraser 2015-10-28 21:05:55 +08:00
数组名作参数会退化为指针,即 4 或 8 。会 crash 是因为第二个格式化输出有问题,应该是%d
|
11
raingolee OP |
12
raingolee OP |
15
0ver1oad 2015-10-28 21:10:17 +08:00
如果是 32bit 系统就是 4 ,因为性能考虑,数组作为参数会变为指针
|
16
raingolee OP @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 系统,返回的却是字符串的长度,怎么破? |
17
handelxh 2015-10-28 21:20:14 +08:00
sizeof() 出来的是 char 指针的大小,这个问题有什么可讨论的?
|
20
WalkingEraser 2015-10-28 21:29:20 +08:00
@raingolee vc 和 GCC 都返回的是指针长度的说…
|
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) |
22
zhicheng 2015-10-28 21:33:17 +08:00
|
23
aheadlead 2015-10-28 21:45:36 +08:00
参见《 C 专家编程》 Chapter 4 The Shocking Truth: C Arrays and Pointers Are NOT the Same!
|
24
raingolee OP |
28
SErHo 2015-10-28 21:54:29 +08:00 1
@raingolee 哎,你截的图上不是说的很明白吗?当数组通过函数参数后,在函数体内就是指针了,此时 sizeof 返回指针的大小,不经过函数参数, sizeof 就会是数组长度。其实 C 语言根本就没有数组参数这一说法。看看这个帖子: https://lkml.org/lkml/2015/9/3/428
|
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 []' 你继续看书,不要再问了。 |
31
raingolee OP |
32
billlee 2015-10-28 22:04:48 +08:00 2
C 语言中,函数参数中的数组会退化为指针,所以 sizeof(msg) 返回的是一个指针的长度
从实现上理解, C 语言不会在运行时记录数组的长度信息, sizeof 在编译时由编译器计算结果,作为一个函数参数,在编译时编译器不可能知道传进来的数组是什么长度,只能把它当成指针处理。这就是典型的静态语言。 |
33
raingolee OP @SErHo
哎,你截的图上不是说的很明白吗?当数组通过函数参数后,在函数体内就是指针了,此时 sizeof 返回指针的大小,不经过函数参数, sizeof 就会是数组长度。其实 C 语言根本就没有数组参数这一说法。看看这个帖子: https://lkml.org/lkml/2015/9/3/428 --- 我想明白了,你回答的真棒,真心感谢!!! |
34
qian19876025 2015-10-28 22:08:03 +08:00 1
@raingolee 不是返回 INT 大小 而是返回 编译器所支持的指针 所占内存的大小
在 C 里面 指针大小是固定的 不关是什么类型的指针 其占用的内存都一样 为啥要这样 因为 这个指针存的是一个地址 什么地址 内存的地址 这个内存地址字长是固定的 为啥要固定 实现起来方便 简单 那为传参的时候对数组降级 其目的就是为了节省内存 方便编译器实现起来容易 使用 KISS 来理解 C 编译器的行为 以上是我对指针的理解 |
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; } 哈哈 |
37
suikator 2015-10-28 22:13:41 +08:00
我已经习以为常了,哈哈
|
38
batstying 2015-10-28 22:22:31 +08:00 1
10 楼说的对呀,补充一句,其实在编译的时候,编译器就已经生成了 4 或者 8 来替换 sizeof(msg),而这个 sizeof(msg)表示指针这种类型占用的字节数~~,如果要求字符串可以用库函数 lstrlen ,记得字符串 0 结尾,长度+1 哦~
|
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; } 差不多就这些了 |
41
ChiangDi 2015-10-28 23:27:57 +08:00
就是你把数组名作为参数传进去再用 sizeof 就不是返回数组元素的个数了。
|
42
fractal314 2015-10-28 23:53:07 +08:00 via Android 1
函数参数是数组 /结构体 /类的时候,实际传递进去的是他们的地址
|
43
raingolee OP @fractal314
恩,最简单实用的回答了 |
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 |
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 |
46
erenno1 2015-10-29 10:56:56 +08:00
更正下:
3. 函数调用属于运行时的行为,因此在编译期,对函数传入的指针 /数组参数执行 sizeof ,统一被认定为对指针执行 sizeof ; 总之, sizeof 操作应该是编译时已经搞定的事情 |
47
fxxkgw 2015-10-29 14:29:07 +08:00
能真正搞明白指针和地址的 C 语言一般不会差
|
48
ybh37 2015-10-29 14:37:37 +08:00
C 语言的学习,不能死记所谓的概念。
指针这些东西,记概念是不对的,看过汇编代码后一般会大悟。 |
50
e2real 2015-10-29 16:29:45 +08:00
论调试技能的重要性。
|
51
Keyes 2015-10-29 17:39:47 +08:00
|