在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 thechar *name[]
would get deleted after function return if it is notstatic
(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
您可以看到
stackarray
和staticstackarray
之间的内存分配变化:$ 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 functionx
that returns a pointer? But what kind of pointer does it return its just a "" without likeint
orvoid
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 likeint (*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/