c - 指针和指针功能

标签 c arrays function pointers

在C语言中学习K&R书籍时,我遇到了一些有关复杂的指针声明和指针数组关系的问题。

1)两者之间到底有什么区别

char amessage[] = "this is a string";


char *pmessage
pmessage = "this is a string"

您什么时候会使用其中一个?

根据我的理解,第一个根据字符串的大小分配一些内存,然后将字符存储在内存中。然后,当您访问amessage []时,您可以直接访问您要查找的任何字符。对于第二个,您还分配了内存,只是在需要时仅通过指针访问数据。这是正确的观察方式吗?

2)这本书说,将数组传递给函数时,就像将指针赋予数组的第一个索引一样,因此即使您仍然可以像a [i]那样进行语法处理,也可以通过操纵指针来操纵数组。如果您只是在某个地方创建了一个数组并想访问它,这是真的吗?还是仅当您将数组传递给函数时才是真的?例如:
char amessage[]= "hi";
char x = *(amessage + 1); // can I do this?

3)书中说在此特定功能中对static的使用很棒:
/* 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];
}

我不明白为什么这确实是对static的很好使用。是不是因为char * name []不是静态的(因为它是局部变量),在函数返回后会被删除?然后,这是否意味着在c中您无法执行以下操作:
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])()时,它的含义是什么。我不明白如何用数组替换指针名称。

谢谢你的帮助!

凯文

最佳答案

1) What exactly is the difference between

char amessage[] = "this is a string";

and

char *pmessage
pmessage = "this is a string"

and when would you use one or the other?


amessage将始终引用保存this is a string\0的内存。您不能更改其引用的地址。 pmessage可以更新为指向内存中的任何字符,无论它是否是字符串的一部分。如果分配给pmessage,则可能会丢失对this is a string\0的唯一引用。 (这取决于您是否在其他任何地方进行了引用。)

如果我打算就位修改char amessage[]的内容,我将使用amessage[]。您无法修改pmessage指向的内存。试试这个小程序;一次注释掉amessage[0]='H'pmessage[0]='H';一次,看看pmessage[0]='H';导致分段违规:
#include <stdio.h>

int main(int argc, char* argv[]) {
    char amessage[]="howdy";
    char *pmessage="hello";
    amessage[0]='H';
    pmessage[0]='H';
    printf("amessage %s\n", amessage);
    printf("pmessage %s\n", pmessage);
    return 0;
}

很少修改在程序中硬编码的字符串。 char *foo = "literal";可能更常见,字符串的不可变性可能是原因之一。

2) The book says that arrays when passed into functions are treated as if you gave the pointer to the first index of the array and thus you manipulate the array through manipulating the pointer even though you can still do syntax like a[i]. Is this true if you just created an array somewhere and want to access it or is it only true if you pass in an array into a function? For example:

char amessage[]= "hi";
char x = *(amessage + 1); // can I do this?


您可以这样做,但这很不寻常:
$ cat refer.c
#include <stdio.h>

int main(int argc, char* argv[]) {
    char amessage[]="howdy";
    char x = *(amessage+1);
    printf("x: %c\n", x);
    return 0;
}

$ ./refer
x: o
$ 

至少,我从未见过使用字符串执行此操作的“生产”程序。 (而且我很难想到一个使用指针算术而不是在其他类型的数组上进行数组下标的程序。)

3) The book says the use of static is great in this particular function:

/* 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];
}

I don't understand why exactly this is a good use of static. Is it because the char *name[] would get deleted after function return if it is not static (because its a local variable)? Then does that mean in c you can't do stuff like:

void testFunction(){
    int x = 1;
    return x; 
}

Without x being deleted before you use the return value? (Sorry I guess this might not be a pointer question but it was in the pointer chapter).



在这种情况下,我相信static是不必要的;至少GCC能够确定未修改字符串,并将其存储在.rodata只读数据段中。但是,这可能是对字符串文字的优化。您的另一个原始数据类型(int)的示例也可以正常工作,因为C在函数调用和函数返回时均按值传递所有内容。但是,如果您要返回指向分配给堆栈的对象的指针,那么static是绝对必要的,因为它确定了对象在内存中的位置:
$ cat stackarray.c ; make stackarray
#include <stdio.h>

struct foo { int x; };

struct foo *bar() {
    struct foo array[2];
    array[0].x=1;
    array[1].x=2;
    return &array[1];
}

int main(int argc, char* argv[]) {
    struct foo* fp;
    fp = bar();

    printf("foo.x: %d\n", fp->x);
    return 0;
}

cc     stackarray.c   -o stackarray
stackarray.c: In function ‘bar’:
stackarray.c:9:2: warning: function returns address of local variable

如果将array的存储时间更改为static,则返回的地址不会自动分配,即使函数已返回,它也将继续工作:
$ cat staticstackarray.c ; make staticstackarray ; ./staticstackarray
#include <stdio.h>

struct foo { int x; };

struct foo *bar() {
    static struct foo array[2];
    array[0].x=1;
    array[1].x=2;
    return &array[1];
}

int main(int argc, char* argv[]) {
    struct foo* fp;
    fp = bar();

    printf("foo.x: %d\n", fp->x);
    return 0;
}

cc     staticstackarray.c   -o staticstackarray
foo.x: 2

您可以看到stackarraystaticstackarray之间的内存分配变化:
$ readelf -S stackarray | grep -A 3 '\.data'
  [24] .data             PROGBITS         0000000000601010  00001010
       0000000000000010  0000000000000000  WA       0     0     8
  [25] .bss              NOBITS           0000000000601020  00001020
       0000000000000010  0000000000000000  WA       0     0     8
$ readelf -S staticstackarray | grep -A 3 '\.data'
  [24] .data             PROGBITS         0000000000601010  00001010
       0000000000000010  0000000000000000  WA       0     0     8
  [25] .bss              NOBITS           0000000000601020  00001020
       0000000000000018  0000000000000000  WA       0     0     8

不使用.bss的版本中的static节比使用.bss的版本中的static节小8个字节。 .bss节中的那8个字节提供了返回的持久地址。

因此,您可以看到字符串的情况并没有真正改变-至少GCC不在乎-但指向其他类型对象的指针static改变了世界。

但是,大多数在函数本地static存储中返回数据的函数都不受欢迎。例如,strtok(3)从字符串中提取 token ,并且如果随后对strtok(3)的调用包括NULL作为第一个参数,则表明该函数应重新使用在第一次调用中传递的字符串。这很简洁,但是意味着程序永远不能同时标记两个单独的字符串,并且多线程程序无法可靠地使用此例程。因此,有一个可重入版本strtok_r(3),它需要一个附加参数来存储两次调用之间的信息。 man -k _r将显示数量惊人的具有可重入版本的函数,主要的变化是减少了static在函数中的使用。

4) There are some complicated declaration like

char (*(*x())[])()

I'm really confused as to what is going on. So the x() part means a function x that returns a pointer? But what kind of pointer does it return its just a "" without like int or void or w/e. Or does that mean a pointer to a function (but I thought that would be like (*x)())? And then after you add brackets (because I assume brackets have the next precedence)...what is that? An array of functions?

This kind of ties to my confusion with function pointers. If you have something like

int (*func)() 

That means a pointer to a function that returns an int, and the name of that pointer is func, but what does it mean when its like int (*x[3])(). I don't understand how you can replace the pointer name with an array.



首先,不要 panic 。您几乎不需要任何复杂的东西。有时,拥有一个函数指针表并根据状态转换图调用下一个函数指针非常方便。有时您要使用sigaction(2)安装信号处理程序。然后,您将需要一些稍微复杂的函数指针。但是,如果您使用cdecl(1)解密所需内容,则将很有意义:
       struct sigaction {
           void     (*sa_handler)(int);
           void     (*sa_sigaction)(int, siginfo_t *, void *);
           sigset_t   sa_mask;
           int        sa_flags;
           void     (*sa_restorer)(void);
       };
cdecl(1)仅理解C native 类型的子集,因此将siginfo_t替换为void,您可以大致看到所需的内容:
$ cdecl
Type `help' or `?' for help
cdecl> explain void     (*sa_sigaction)(int, void *, void *);
declare sa_sigaction as pointer to function
    (int, pointer to void, pointer to void) returning void

Expert C Programming: Deep C Secrets有一章非常出色,专门用于理解更复杂的声明,甚至包括cdecl版本,以防您希望扩展它以包括更多类型和typedef处理。值得一读。

关于c - 指针和指针功能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7857334/

相关文章:

c++ - 如何使用已删除的复制构造函数初始化类数组(C++11)

jQuery - 在添加和/或删除类名时运行函数

javascript - 要将您的 jQuery 代码分解为函数,您是否采用与 JavaScript 中相同的方式?

c - 如何使用 C 仅从包含数字和字母的数组中读取数字?

c - GLUT不会画画

python - 在python中使用numpy创建一个动态数组

arrays - Swift 内联闭包错误?

c++ - 评估与所涉及的垂直线的交点

c++ - 大型原始数字类型之间的算术 : can I save the overflow?

c - 指向一些基本数据类型的指针的类型转换(内部发生的情况)