我有点困惑,为什么这段代码起作用:
struct product{
double price;
int quantity;
char name[1];
}p2;
printf("%d",p2.quantity);
这不起作用:
struct product *p3=&p2;
printf("%d",p3.quantity);
我的意思是p2
和p3
都是指向同一对象的指针,所以为什么在第二种情况下我们需要编写printf("%d",p3->quantity);
才能使其工作。
答案 0 :(得分:2)
回答您对C“指针”的困惑:
C!= C#。
C没有.Net reference objects。
尽管您可以获取任何C对象(char,int,struct等)的地址,将其转换为指针,但该对象不是' 必然是自身指针。
看这里:
Difference between variable and data object in C language?
在C语言中,对象是占用存储空间的任何东西。 C 2011 online draft。
左值是指定对象的表达式,使得 该对象的内容可以被读取或修改(基本上,任何 可以作为赋值目标的表达式是左值)。 尽管C标准未定义变量变量,但您可以 基本上将其视为指定对象的任何标识符...
指针是任何表达式,其值是对象的位置。 指针变量是一个存储指针值的对象。
答案 1 :(得分:1)
C具有值类型,包括用于function verReceita(id){
var numReceita = id;
$.post('verReceita.php',{postnumReceita: numReceita},
function(data){
$('#result').html(data);
});
}
聚合的值类型。具有某种struct
类型的表达式不是对结构的引用,而是对该结构本身的引用。
在您的程序中,struct
不是指针,而是直接保存整个结构的变量的名称(存储位置)。 p2
的值就是该结构本身。
p2
拥有指向该结构的指针; p3
的值是一种数据类型,指示值在内存中的位置。
C语言使用不同的运算符通过指向p3
的指针(而不是直接指向成员)来引用成员。
并非必须如此。编译器具有足够的信息,因此struct/union
和p2.quantity
可以同样工作。但是,事实并非如此。数十年前,丹尼斯·里奇(Dennis Ritchie)进行了设计,因此,如果p3.quantity
是指向结构的指针,则访问成员需要首先将p
取消引用为p
,以获得{{1} }指向,然后对以下值使用成员选择:*p
。这需要括号,因为后缀p
运算符的优先级比一元(*p).member
高。因为那很冗长,所以Ritchie或其他人发明了一种简写形式:.
运算符。运算符是语法糖:*
表示同一事物->
。该人可能没有意识到p->member
可以正常工作(点应用于(*p).member
指针可以选择该成员),或者更有可能拒绝该设计(可能是由于“指针取消引用应该在代码中清晰可见”)。
通常,当我们在C程序中看到p.member
时,我们不必担心内存安全,但是struct/union
会引发一个危险信号:z = x.y;
在这里是否是有效的指针?因此,设计师有一个观点。 z = x->y
运算符突出显示了指针已被取消引用,这对于具有手动内存管理功能的语言(如C)来说是危险的。
该家族中的其他一些语言也有明确的取消引用。在Pascal中,如果x
是指向记录的指针,则获取成员的语法为->
。您不能只使用ptr
。但是,请注意,在Pascal中,指针取消引用ptr^.memb
是一个后缀运算符,因此这里没有优先级问题。不需要像ptr.memb
那样加上括号。
答案 2 :(得分:0)
在这里,关于什么是指针,什么不是指针,显然存在混淆。我将稍微重写一下代码以突出区别-
struct product{
double price;
int quantity;
char name[1];
};
struct product p2;
struct product *p3;
p3 = &p2;
此代码等同于您的代码。现在注意,p3
是用*
声明的(类型为struct product *
)。这意味着p3
是一个指针。
p2
的声明类型为struct product
(不是指针)。这是一个结构。
您已经知道,使用struct成员时使用.
,从指针中获取成员时使用->
。
来到&p2
。 &
运算符返回指向和对象的指针。因此&p2
的类型为struct product*
(指针),因此可以分配给也是指针的p3
。
因此,您也可以执行(&p2)->quantity
(注意->
),因为(&p2)
是一个指针。
最后,就像&
运算符一样,我们也有*
,它从指针返回对象。
因此您可以使用(*p3).quantity
(请注意,.
代替了->
)。这里的p3
是一个指针,但是*
运算符返回一个结构,现在您可以使用.
。
答案 3 :(得分:0)
我有点困惑,为什么这段代码起作用:
struct product{ double price; int quantity; char name[1]; }p2; printf("%d",p2.quantity);
这不起作用:
struct product *p3=&p2; printf("%d",p3.quantity);
p2
被声明为struct product
。那是一个结构对象本身,而不是一个指针。与第一个代码段中的.
一样,用户使用p2.quantity
运算符访问其成员。
另一方面,p3
被声明为类型为struct product
的 指针,也就是struct product *
。它被初始化为指向p2
所标识的对象,也就是&p2
,即代表p2
地址的指针值。 .
不适用于指针,因此您的第二段代码是错误的,但是您可以将->
运算符与指向结构体或联合体的指针一起使用,以访问指向对象的成员。 / p>
我的意思是p2和p3都是指向同一对象的指针
他们绝对不是。初始化p3 = &p2
将p3
设置为指向 p2
,不等于p2
。这是p2
的类型struct product
和p3
的类型struct product *
之间的本质区别。
答案 4 :(得分:0)
.
成员选择运算符要求其左操作数具有struct
或union
类型。表达式p2
的类型为struct product
,因此它是.
运算符的有效操作数。表达式p3
的类型为struct product *
(指向{{1}的 pointer ),因此不是 {{1 }}运算符。
struct product
成员选择运算符要求其左操作数具有指向.
或->
类型的 pointer 。表达式struct
的类型为union
,因此它是p3
运算符的有效操作数。 struct product *
执行指针的隐式引用-->
等效于写入->
。
表达式a->b
不是指针-它没有指针类型,因此它不是(*a).b
运算符的有效操作数。 p2
没有指向任何东西,它是 。
我写了一些代码来说明->
和p2
之间的区别-p2
是我写的一个小实用程序,用于将对象的内容转储到内存中:
p3
以下是输出:
dumper
#include <stdio.h>
#include "dumper.h"
#include <stdint.h>
int main( void )
{
struct product
{
double price;
int quantity;
char name[1];
};
struct product p2 = {1.00, 1, .name[0] = 'A'};
struct product *p3 = &p2;
char *names[] = {"p2", "p3"};
void *addrs[] = {&p2, &p3};
size_t sizes[] = { sizeof p2, sizeof p3};
dumper( names, addrs, sizes, 2, stdout );
return 0;
}
对象 Item Address 00 01 02 03
---- ------- -- -- -- --
p2 0x7ffeec515a68 00 00 00 00 ....
0x7ffeec515a6c 00 00 f0 3f ...?
0x7ffeec515a70 01 00 00 00 ....
0x7ffeec515a74 41 00 00 00 A...
p3 0x7ffeec515a60 68 5a 51 ec hZQ.
0x7ffeec515a64 fe 7f 00 00 ....
位于地址struct
处,并包含一个p2
对象(0x7ffeec515a68
,它是浮点值{ {1}},从地址double
开始,后跟一个0x3ff0000000000000
对象(1.0
,从地址0x7ffeec515a68
开始,后跟一个int
对象(0x00000001
是0x7ffeec515a70
的字符代码,从地址char
开始)。
指针对象0x41
位于地址'A'
并包含地址0x7ffeec515a74
(p3
)。
答案 5 :(得分:0)
在1974 C参考手册描述的语言中,->
运算符会将左操作数的 value 用作字节地址,并添加由右操作数,并将结果地址处的任何内容都视为该字段类型的对象”,而“。”运算符将执行相同操作,但使用左操作数的 address 。尽管C编译器具有几十年要求.
的左操作数是包含命名字段的结构或联合,->
的左操作数是指向包含命名字段的结构或联合的指针,《 1974 C参考手册》不施加任何此类要求,而是以不依赖于左操作数类型的方式定义这些运算符的行为。根据该手册,如果存在某些结构类型S,其字段为foo
[注意如果多个结构类型是“通用初始序列”的一部分,则它们只能具有名称匹配的字段],然后1234->foo
将以与((S volatile *)1234)->foo
等效的方式进行处理。
请注意,在1974年的语言中,foo.bar
和foo->bar
可能都具有不同的有效含义。在人为的情况之外,这两种方法很少有用,但是,如果两个操作都使用相同的运算符,那么编译器就不可能总是选择有用的方法。