在C中学习K& R书籍我有一些关于复杂指针声明和指针数组关系的问题。
1)
之间究竟有什么区别char amessage[] = "this is a string";
和
char *pmessage
pmessage = "this is a string"
你何时会使用其中一个?
根据我的理解,第一个根据字符串的大小分配一些内存,然后将字符存储在内存中。然后,当您访问amessage []时,您只需直接访问您正在寻找的任何字符。对于第二个,您还可以分配内存,只需在需要时通过指针访问数据。这是正确的看待方式吗?
2)书中说,传入函数时的数组被视为指向数组的第一个索引的指针,因此即使你仍然可以像[i]那样操作指针,你也可以操纵指针]。如果您刚刚在某处创建了一个数组并且想要访问它,或者只有将数组传入函数时才是真的吗?例如:
char amessage[]= "hi";
char x = *(amessage + 1); // can I do this?
3)书中说静态的使用在这个特定的功能中是很好的:
/* month_name: return name of n-th month */
char *month_name(int n)
{
static char *name[] = {
"Illegal month",
"January", "February", "March",
"April", "May", "June",
"July", "August", "September",
"October", "November", "December"
};
return (n < 1 || n > 12) ? name[0] : name[n];
}
我不明白为什么这是一个很好的静态使用。是因为char * name []会在函数返回后被删除,如果它不是静态的(因为它是一个局部变量)?那么这是否意味着你不能做像这样的事情:
void testFunction(){
int x = 1;
return x;
}
在使用返回值之前不删除x? (对不起,我想这可能不是指针问题,但它在指针章节中。)
4)有一些复杂的声明,如
char (*(*x())[])()
我对发生的事情感到很困惑。那么 x()部分是指一个返回指针的函数x?但是什么样的指针只返回它只是一个“”而没有像int或void或w / e那样。或者这是否意味着指向函数的指针(但我认为这就像(* x)())?然后添加括号后(因为我假设括号具有下一个优先级)......那是什么?一系列功能?
这与我与函数指针的混淆有关。如果您有类似
的内容int (*func)()
这意味着指向返回int的函数的指针,并且该指针的名称是func,但它与int(* x [3])()的含义是什么。我不明白你如何用数组替换指针名称。
感谢您的帮助!
凯文
答案 0 :(得分:7)
答案 1 :(得分:2)
这与第3部分有关,是对sarnold评论的回复/补充。无论是否有静态,他都是正确的,字符串文字总是分开 .data .rodata段,基本上只创建一次。但是,如果不使用static这个词,实际的数组,即 char指针数组,实际上每次调用函数时都会在堆栈上创建。
使用使用static:
Dump of assembler code for function month_name:
0x08048394 <+0>: push ebp
0x08048395 <+1>: mov ebp,esp
0x08048397 <+3>: cmp DWORD PTR [ebp+0x8],0x0
0x0804839b <+7>: jle 0x80483a3 <month_name+15>
0x0804839d <+9>: cmp DWORD PTR [ebp+0x8],0xc
0x080483a1 <+13>: jle 0x80483aa <month_name+22>
0x080483a3 <+15>: mov eax,ds:0x8049720
0x080483a8 <+20>: jmp 0x80483b4 <month_name+32>
0x080483aa <+22>: mov eax,DWORD PTR [ebp+0x8]
0x080483ad <+25>: mov eax,DWORD PTR [eax*4+0x8049720]
0x080483b4 <+32>: pop ebp
0x080483b5 <+33>: ret
没有使用static:
Dump of assembler code for function month_name:
0x08048394 <+0>: push ebp
0x08048395 <+1>: mov ebp,esp
0x08048397 <+3>: sub esp,0x40
0x0804839a <+6>: mov DWORD PTR [ebp-0x34],0x8048514
0x080483a1 <+13>: mov DWORD PTR [ebp-0x30],0x8048522
0x080483a8 <+20>: mov DWORD PTR [ebp-0x2c],0x804852a
0x080483af <+27>: mov DWORD PTR [ebp-0x28],0x8048533
0x080483b6 <+34>: mov DWORD PTR [ebp-0x24],0x8048539
0x080483bd <+41>: mov DWORD PTR [ebp-0x20],0x804853f
0x080483c4 <+48>: mov DWORD PTR [ebp-0x1c],0x8048543
0x080483cb <+55>: mov DWORD PTR [ebp-0x18],0x8048548
0x080483d2 <+62>: mov DWORD PTR [ebp-0x14],0x804854d
0x080483d9 <+69>: mov DWORD PTR [ebp-0x10],0x8048554
0x080483e0 <+76>: mov DWORD PTR [ebp-0xc],0x804855e
0x080483e7 <+83>: mov DWORD PTR [ebp-0x8],0x8048566
0x080483ee <+90>: mov DWORD PTR [ebp-0x4],0x804856f
0x080483f5 <+97>: cmp DWORD PTR [ebp+0x8],0x0
0x080483f9 <+101>: jle 0x8048401 <month_name+109>
0x080483fb <+103>: cmp DWORD PTR [ebp+0x8],0xc
0x080483ff <+107>: jle 0x8048406 <month_name+114>
0x08048401 <+109>: mov eax,DWORD PTR [ebp-0x34]
0x08048404 <+112>: jmp 0x804840d <month_name+121>
0x08048406 <+114>: mov eax,DWORD PTR [ebp+0x8]
0x08048409 <+117>: mov eax,DWORD PTR [ebp+eax*4-0x34]
0x0804840d <+121>: leave
0x0804840e <+122>: ret
正如您在第二个示例中所看到的(没有静态),每次都在堆栈上分配数组:
0x08048397 <+3>: sub esp,0x40
并将指针加载到数组中:
0x0804839a <+6>: mov DWORD PTR [ebp-0x34],0x8048514
0x080483a1 <+13>: mov DWORD PTR [ebp-0x30],0x8048522
...
因此,如果您决定不使用静态函数,那么每次调用函数时都会显示更多内容。
答案 2 :(得分:1)
3)它与此无关 - static
创建数组一次,而不是每次函数运行时创建它。由于数组中的数据永远不会改变,因此每次重新创建数据更有效。您的示例函数每次都可以正常工作。这是一个价值。在您返回之前不会删除它。那将非常不直观。
答案 3 :(得分:0)
4)在4)点的回复中添加更多信息:
我正在按照下一本书来学习诺曼·兰迪斯的帕斯卡程序员的C:C。它已经很老了,它被认为是从帕斯卡到C的桥梁;但我觉得它在机器的最低层是如此有用,完成和解释。对我来说这是一本很棒的书。
附录A中的第5.3.1章准确地谈到了这一点。 (Blockquotes是从书中提取的内容)
基类型的定义:
包含声明符的声明中出现的类型说明符称为&gt;基本类型
基本上,bool x
=&gt; bool是基本类型,在int x[]
=&gt;中数组的基类型是int,x的基类型是int。
为了解释复杂的声明符,以下规则适用:
- 首先应用星号运算符。
- 应用&#34;基类型&#34;(())和&#34;返回基类型数组&#34; ([])&gt;运算符之后,从右到左。 当然,括号可以附上声明者以改变评估顺序。
醇>
这就是用字母w改变字母x的相同例子:
在我遵循上述2条规则之后,我会从括号外部进入内部。步骤进行:
我希望现在它非常清楚。
但是你需要一些时间和练习才能真正理解和掌握这一点,但是一旦你明白了,它就很容易;)