指针和指针功能

时间:2011-10-22 04:08:59

标签: c arrays function pointers

在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])()的含义是什么。我不明白你如何用数组替换指针名称。

感谢您的帮助!

凯文

4 个答案:

答案 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。

的数组

为了解释复杂的声明符,以下规则适用:

  
      
  1. 首先应用星号运算符。
  2.   
  3. 应用&#34;基类型&#34;(())和&#34;返回基类型数组&#34; ([])&gt;运算符之后,从右到左。   当然,括号可以附上声明者以改变评估顺序。
  4.   

这就是用字母w改变字母x的相同例子:

我如何解析&#39; this:char(*(* w())[])();

在我遵循上述2条规则之后,我会从括号外部进入内部。步骤进行:

  1. 在任何括号之外,我们找到函数声明符。然后,到目前为止,我们有一个返回char的函数。
  2. 现在,我们输入括号并处理先前的指针和数组之后。
  3. 这样的指针,是&#34;上基类型&#34;的指针,我们说是一个函数 返回一个char。然后我们得到函数的指针返回一个char,到目前为止。
  4. 在数组之后,它是&#34;上基类型&#34;的数组。并且&#34;上基类型&#34; =返回char的函数指针。
  5. 现在,进入最深的括号,我们找到一个指针和一个函数。同样的方式,第一个指针,在函数之后。
  6. 我们处理指针=&gt;指向返回char的函数的指针数组的指针。
  7. 最后是函数声明符,我们得到了:函数返回指向返回char的函数的指针数组的指针。
  8. 我希望现在它非常清楚。

    但是你需要一些时间和练习才能真正理解和掌握这一点,但是一旦你明白了,它就很容易;)