指针和指针功能

Mat*_*att 9 c arrays pointers function

在CI中学习K&R书有一些关于复杂指针声明和指针数组关系的问题.

1)究竟有什么区别

char amessage[] = "this is a string";
Run Code Online (Sandbox Code Playgroud)

char *pmessage
pmessage = "this is a string"
Run Code Online (Sandbox Code Playgroud)

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

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

2)书中说,传递给函数时的数组被视为指向数组的第一个索引的指针,因此即使你仍然可以执行类似[i]的语法,你也可以通过操纵指针来操作数组.如果您刚刚在某处创建了一个数组并且想要访问它,或者只有将数组传入函数时才是真的吗?例如:

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

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];
}
Run Code Online (Sandbox Code Playgroud)

我不明白为什么这是一个很好的静态使用.是因为char*name []会在函数返回后被删除,如果它不是静态的(因为它是一个局部变量)?这是否意味着你不能做的事情:

void testFunction(){
    int x = 1;
    return x; 
}
Run Code Online (Sandbox Code Playgroud)

在使用返回值之前不删除x?(对不起,我想这可能不是一个指针问题,但它在指针章节中).

4)有一些复杂的声明

char (*(*x())[])()
Run Code Online (Sandbox Code Playgroud)

对于发生的事情,我真的很困惑.那么x()部分是指一个返回指针的函数x?但它是什么样的指针它只返回一个" "没有像int或void或w/e.或者这是否意味着指向函数的指针(但我认为这就像(*x)())?然后添加括号后(因为我假设括号具有下一个优先级)......那是什么?一系列功能?

这种与函数指针混淆的联系.如果你有类似的东西

int (*func)() 
Run Code Online (Sandbox Code Playgroud)

这意味着指向返回int的函数的指针,该指针的名称是func,但它与int(*x [3])()的含义是什么.我不明白你如何用数组替换指针名称.

谢谢你的帮助!

凯文

sar*_*old 7

1)究竟有什么区别

char amessage[] = "this is a string";
Run Code Online (Sandbox Code Playgroud)

char *pmessage
pmessage = "this is a string"
Run Code Online (Sandbox Code Playgroud)

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

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;
}
Run Code Online (Sandbox Code Playgroud)

修改程序中硬编码的字符串相对较少; char *foo = "literal";可能更常见,字符串的不变性可能是其中一个原因.

2)书中说,传递给函数时的数组被视为指向数组的第一个索引的指针,因此即使你仍然可以执行类似[i]的语法,你也可以通过操纵指针来操作数组.如果您刚刚在某处创建了一个数组并且想要访问它,或者只有将数组传入函数时才是真的吗?例如:

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

你可以这样做,但这很不寻常:

$ 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
$ 
Run Code Online (Sandbox Code Playgroud)

至少,我从未见过使用字符串完成此操作的"生产"程序.(而且我很难想到一个程序使用指针算法而不是其他类型的数组上的数组下标.)

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];
}
Run Code Online (Sandbox Code Playgroud)

我不明白为什么这是一个很好的用途static.是因为char *name[]如果不是函数返回会被删除static(因为它是一个局部变量)?这是否意味着你不能做的事情:

void testFunction(){
    int x = 1;
    return x; 
}
Run Code Online (Sandbox Code Playgroud)

在使用返回值之前不删除x?(对不起,我想这可能不是一个指针问题,但它在指针章节中).

在这个具体案例中,我认为这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
Run Code Online (Sandbox Code Playgroud)

如果更改的存储时间arraystatic,那么被返回的地址自动分配,并会继续在函数返回后还要工作:

$ 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
Run Code Online (Sandbox Code Playgroud)

你能看到的内存分配的变化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
Run Code Online (Sandbox Code Playgroud)

.bss在没有版本部static是比小8个字节.bss与版本区段static.该.bss部分中的那8个字节提供了返回的持久地址.

所以你可以看到字符串的情况并没有真正有所作为 - 至少GCC并不关心 - 但指向其他类型的对象,这static在全世界都有所不同.

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

4)有一些复杂的声明

char (*(*x())[])()
Run Code Online (Sandbox Code Playgroud)

对于发生的事情,我真的很困惑.那么该x()部分是指一个x返回指针的函数?但是,什么样的指针它返回它只是一个""不喜欢intvoid或W/E.或者这是否意味着指向函数的指针(但我认为会是这样的 (*x)())?然后在你添加括号后(因为我假设括号具有下一个优先级)...那是什么?一系列函数?

这种与函数指针混淆的联系.如果你有类似的东西

int (*func)() 
Run Code Online (Sandbox Code Playgroud)

这意味着指向返回int的函数的指针,以及该指针的名称func,但它的含义是什么意思int (*x[3])().我不明白你如何用数组替换指针名称.

首先,不要惊慌.你会几乎从来没有什么需要这种复杂的.有时,拥有一个函数指针表并根据状态转换图调用下一个函数指针非常方便.有时您正在安装信号处理程序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);
       };
Run Code Online (Sandbox Code Playgroud)

cdecl(1)只能理解C本机类型的一个子集,所以替换siginfo_tvoid,您可以大致看到所需的内容:

$ 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
Run Code Online (Sandbox Code Playgroud)

专家C编程:Deep C Secrets有一个很好的章节,专门用于理解更复杂的声明,甚至还包括一个版本cdecl,以防您希望扩展它以包含更多类型和typedef处理.这非常值得一读.