为什么我们有指针类型?例如
int *ptr;
我知道它的类型安全,例如取消引用' ptr',编译器需要知道它解除引用ptr以输入int,而不是char或long等,但是正如其他人所述{ {3}},它也因为"我们应该知道要读取多少字节。取消引用char指针意味着从内存中取一个字节,而对于int,它可能是4个字节。"那讲得通。
但如果我有这样的事情怎么办?
typedef struct _IP_ADAPTER_INFO {
struct _IP_ADAPTER_INFO* Next;
DWORD ComboIndex;
char AdapterName[MAX_ADAPTER_NAME_LENGTH + 4];
char Description[MAX_ADAPTER_DESCRIPTION_LENGTH + 4];
UINT AddressLength;
BYTE Address[MAX_ADAPTER_ADDRESS_LENGTH];
DWORD Index;
UINT Type;
UINT DhcpEnabled;
PIP_ADDR_STRING CurrentIpAddress;
IP_ADDR_STRING IpAddressList;
IP_ADDR_STRING GatewayList;
IP_ADDR_STRING DhcpServer;
BOOL HaveWins;
IP_ADDR_STRING PrimaryWinsServer;
IP_ADDR_STRING SecondaryWinsServer;
time_t LeaseObtained;
time_t LeaseExpires;
} IP_ADAPTER_INFO, *PIP_ADAPTER_INFO;
PIP_ADAPTER_INFO pAdapterInfo = (IP_ADAPTER_INFO *)malloc(sizeof(IP_ADAPTER_INFO));
在这里声明PIP_ADAPTER_INFO类型有什么意义?毕竟,与前面的例子不同,我们已经为指针指定了足够的内存(使用malloc),所以不定义这里冗余的类型吗?我们将从已经分配的内存中读取尽可能多的数据。
另外,旁注:以下4个声明之间是否有任何区别或是否有最佳做法?
PIP_ADAPTER_INFO pAdapterInfo = (IP_ADAPTER_INFO *)malloc(sizeof(IP_ADAPTER_INFO));
或
PIP_ADAPTER_INFO pAdapterInfo = (PIP_ADAPTER_INFO)malloc(sizeof(IP_ADAPTER_INFO));
或
IP_ADAPTER_INFO *pAdapterInfo = (IP_ADAPTER_INFO *)malloc(sizeof(IP_ADAPTER_INFO));
或
IP_ADAPTER_INFO *pAdapterInfo = (PIP_ADAPTER_INFO)malloc(sizeof(IP_ADAPTER_INFO));
答案 0 :(得分:3)
你在这里问两个不同的问题 - 为什么有不同的指针类型,为什么隐藏在typedef后面的指针?
不同指针类型的主要原因来自指针算法 - 如果p
指向类型为T
的对象,则表达式p + 1
指向该类型的下一个对象。如果p
指向4字节int
,则p + 1
指向下一个int
。如果p
指向128字节struct
,则p + 1
指向下一个128字节struct
,依此类推。指针是具有附加类型语义的内存地址的抽象。
至于隐藏在typedef后面的指针......
如果该类型的用户仍然必须知道该类型的“指针”,那么我们中的许多人(包括我自己)都会考虑将typedef后面的指针隐藏为坏样式(即,如果您必须取消引用它,或者如果您将malloc/calloc/realloc
的结果分配给它,等等。如果你试图抽象掉某些东西的“指针”,你需要的不仅仅是声明 - 你需要提供一个完整的API来隐藏所有的指针操作。
至于你的上一个问题,C中的最佳做法是不投出malloc
的结果。 C ++中的最佳实践是根本不使用malloc
。
答案 1 :(得分:1)
我认为这更像是一种类型定义风格而不是动态内存分配的问题。
老派C练习是用标签来描述结构。你说
struct foo {
...
};
然后
struct foo foovar;
或
struct foo *foopointer = malloc(sizeof(struct foo));
但很多人不喜欢不断输入关键字struct
。 (我想我当时不能错; C总是喜欢简洁,有时似乎只是为了减少打字。)所以使用typedef
的形式变得非常受欢迎(并且它受到影响,或者受到C ++的影响) ):
typedef struct {
...
} Foo;
然后
Foo foovar;
或
Foo *foopointer = malloc(sizeof(Foo));
但是,由于原因不太明确,将指针引入typedef变得很受欢迎,如下所示:
typedef struct {
...
} Foo, *Foop;
Foop foopointer = malloc(sizeof(*Foop));
但这完全取决于风格和个人偏好,为人们想象的是清晰,方便或有用。 (但当然,关于清晰度和便利性的观点,如关于风格的观点,可以合法地改变。)我已经看到指针类型deps被贬低为误导或微观实践,但我不确定我是否可以对它们造成错误现在。
您还询问了演员表,我们还可以将sizeof
电话的各种选项作为malloc
的参数进行剖析。
你是否说
并不重要Foop foopointer = (Foop)malloc(sizeof(*Foop));
或
Foop foopointer = (Foo *)malloc(sizeof(*Foop));
第一个可能更清晰,因为您不必返回并检查Foop
和Foo *
是否相同。但他们在C中都是不好的做法,至少在某些圈子里他们自1990年以来就被弃用了。那些演员阵容在C语言中被认为是分散注意力和不必要的 - 虽然它们在C ++中当然是必需的,或者我想如果你使用C ++编译器来编译C代码。 (如果您正在编写直接的C ++代码,当然,您通常使用new
代替malloc
。)
但是你应该把什么放在sizeof()
?哪个更好,
Foop foopointer = malloc(sizeof(*Foop));
或
Foop foopointer = malloc(sizeof(Foo));
同样,第一个可以更容易阅读,因为您不必返回并检查Foop
和Foo *
是否相同。但出于同样的原因,第三种形式可以更加清晰:
Foop foopointer = malloc(sizeof(*foopointer));
现在您知道,无论foopointer
指向何种类型,您都需要为其分配适当的空间。但是,这个成语效果最好,如果它最清楚foopiinter
实际上是一个指向某种类型的指针,这意味着变体
Foo *foopointer = malloc(sizeof(*foopointer));
甚至
struct foo *foopointer = malloc(sizeof(*foopointer));
可以被认为更清晰 - 这可能是人们认为指针typedef不太完全有用的原因之一。
最重要的是,如果你仍然和我在一起:如果你发现PIP_ADAPTER_INFO
没有用,就不要使用它 - 请使用IP_ADAPTER_INFO
(以及明确的)相反,*
,当你需要它们时)。有人认为PIP_ADAPTER_INFO
可能有用,这就是它的原因,但赞成使用它的论据并不太引人注目。
答案 2 :(得分:1)
动态分配内存时“指针类型”有什么意义?
至少在你展示的例子中没有。
因此,如果有typedef
指针有意义的情况,那么后续问题就会出现。
答案是:是的。
如果一个人需要opaque data type,那肯定是有道理的。
一个很好的例子是pthread_t
type which defines a handle to a POSIX thread。
根据实施情况,它被定义为
typedef struct bla pthread_t;
typedef struct foo * pthread_t;
typedef long pthread_t;
并且通过这种方式抽象出实现的类型,因为用户不感兴趣,这可能不是您在问题中显示struct
的意图。
答案 3 :(得分:0)
为什么我们有指针类型?
适应各种类型的大小和编码可能不同的体系结构。 C端口很好地适用于许多平台,甚至是新的平台。
今天,指向函数的指针与指向对象的指针的大小不同,这并不罕见。 对象指针转换为void *
,但函数指针可能不会。
指向char
的指针不一定与指向int
或union
或struct
的指针相同。这在今天并不常见。规范细节如下(我的重点):
指向
void
的指针应具有与a相同的表示和对齐要求 指向字符类型的指针。同样,指向兼容类型的限定或非限定版本的指针应具有相同的表示和对齐要求。所有 指向结构类型的指针应具有相同的表示和对齐要求 彼此相同。所有指向联合类型的指针都应具有相同的表示形式 对齐要求彼此。 指向其他类型的指针不必相同 表示或对齐要求。 C11dr§6.2.528