简体   繁体   English

关于不透明指针,C 中的链接如何工作?

[英]How does linking work in C with regards to opaque pointers?

So, I've been having a bit of confusion regarding linking of various things.因此,我对各种事物的链接有些困惑。 For this question I'm going to focus on opaque pointers.对于这个问题,我将专注于不透明的指针。

I'll illustrate my confusion with an example.我将用一个例子来说明我的困惑。 Let's say I have these three files:假设我有这三个文件:

main.c main.c

#include <stdio.h>
#include "obj.h"            //this directive is replaced with the code in obj.h

int main()
{
    myobj = make_obj();
    setid(myobj, 6);

    int i = getid(myobj);
    printf("ID: %i\n",i);

    getchar();
    return 0;
}

obj.c obj.c

#include <stdlib.h>

struct obj{
    int id;
};

struct obj *make_obj(void){
    return calloc(1, sizeof(struct obj));
};

void setid(struct obj *o, int i){
    o->id = i;
};

int getid(struct obj *o){
    return o->id;
};

obj.h对象.h

struct obj;

struct obj *make_obj(void);

void setid(struct obj *o, int i);

int getid(struct obj *o);

struct obj *myobj;

Because of the preprocessor directives, these would essentially become two files:由于预处理器指令,它们本质上将成为两个文件:

(I know technically stdio.h and stdlib.h would have their code replace the preprocessor directives, but I didn't bother to replace them for the sake of readability) (我知道从技术上讲 stdio.h 和 stdlib.h 会让他们的代码替换预处理器指令,但为了可读性我没有费心去替换它们)

main.c main.c

#include <stdio.h>

//obj.h
struct obj;
struct obj *make_obj(void);
void setid(struct obj *o, int i);
int getid(struct obj *o);
struct obj *myobj;

int main()
{
    myobj = make_obj();
    setid(myobj, 6);

    int i = getid(myobj);
    printf("ID: %i\n",i);

    getchar();
    return 0;
}

obj.c obj.c

#include <stdlib.h>

struct obj{
    int id;
};

struct obj *make_obj(void){
    return calloc(1, sizeof(struct obj));
};

void setid(struct obj *o, int i){
    o->id = i;
};

int getid(struct obj *o){
    return o->id;
};

Now here's where I get a bit confused.现在这里是我有点困惑的地方。 If I try to make a struct obj in main.c, I get an incomplete type error, even though main.c has the declaration struct obj;如果我尝试在 main.c 中创建一个 struct obj,我会收到一个不完整的类型错误,即使 main.c 有声明struct obj; . .

Even if I change the code up to use extern , It sill won't compile:即使我将代码更改为使用extern ,它仍然无法编译:

main.c main.c

#include <stdio.h>

extern struct obj;

int main()
{
    struct obj myobj;
    myobj.id = 5;

    int i = myobj.id;
    printf("ID: %i\n",i);

    getchar();
    return 0;
}

obj.c obj.c

#include <stdlib.h>

struct obj{
    int id;
};

So far as I can tell, main.c and obj.c do not communicate structs (unlike functions or variables for some which just need a declaration in the other file).据我所知, main.c 和 obj.c 不通信结构(与某些只需要在另一个文件中声明的函数或变量不同)。

So, main.c has no link with struct obj types, but for some reason, in the previous example, it was able to create a pointer to one just fine struct obj *myobj;因此, main.c 与 struct obj 类型没有链接,但由于某种原因,在前面的示例中,它能够创建一个指针,指向一个很好的struct obj *myobj; . . How, why?怎么,为什么? I feel like I'm missing some vital piece of information.我觉得我错过了一些重要的信息。 What are the rules regarding what can or can't go from one.c file to another?关于 go 从一个.c 文件到另一个文件的规则是什么?

ADDENDUM附录

To address the possible duplicate , I must emphasize, I'm not asking what an opaque pointer is but how it functions with regards to files linking.为了解决可能的重复,我必须强调,我不是在问什么是不透明指针,而是它在文件链接方面的作用。

Converting comments into a semi-coherent answer.将评论转换为半连贯的答案。

The problems with the second main.c arise because it does not have the details of struct obj ;第二个main.c出现问题是因为它没有struct obj的详细信息; it knows that the type exists, but it knows nothing about what it contains.它知道该类型存在,但它对它包含的内容一无所知。 You can create and use pointers to struct obj ;您可以创建和使用指向struct obj的指针; you cannot dereference those pointers, not even to copy the structure, let alone access data within the structure, because it is not known how big it is.你不能取消引用这些指针,甚至不能复制结构,更不用说访问结构内的数据,因为它不知道它有多大。 That's why you have the functions in obj.c .这就是为什么你有obj.c中的功能。 They provide the services you need — object allocation, release, access to and modification of the contents (except that the object release is missing; maybe free(obj); is OK, but it's best to provide a 'destructor').它们提供您需要的服务——object 分配、释放、访问和修改内容(除了缺少 object 版本;也许free(obj);可以,但最好提供一个“析构函数”)。

Note that obj.c should include obj.h to ensure consistency between obj.c and main.c — even if you use opaque pointers.注意obj.c应该包含obj.h以确保obj.cmain.c之间的一致性——即使你使用了 opaque 指针。

I'm not 100% what you mean by 'ensuring consistency';我不是 100% 你所说的“确保一致性”。 what does that entail and why is it important?这意味着什么,为什么它很重要?

At the moment, you could have struct obj *make_obj(int initializer) { … } in obj.c , but because you don't include obj.h in obj.c , the compiler can't tell you that your code in main.c will call it without the initializer — leading to quasi-random (indeterminate) values being used to 'initialize' the structure.目前,你可以在 obj.c 中有struct obj *make_obj(int initializer) { … } ,但是因为你没有在obj.cobj.c obj.h ,编译器无法告诉你你的代码在main.c将在没有初始化程序的情况下调用它——导致使用准随机(不确定)值来“初始化”结构。 If you include obj.h in obj.c , the discrepancy between the declaration in the header and the definition in the source file will be reported by the compiler and the code won't compile.如果将obj.h包含在obj.c中,则编译器会报告 header 中的声明与源文件中的定义之间的差异,并且代码将无法编译。 The code in main.c wouldn't compile either — once the header is fixed. main.c中的代码也无法编译——一旦 header 被修复。 The header files are the 'glue' that hold the system together, ensuring consistency between the function definition and the places that use the function (references). header 文件是将系统粘合在一起的“粘合剂”,确保 function 定义和使用 function 的地方之间的一致性(参考)。 The declaration in the header ensures that they're all consistent. header 中的声明确保它们都是一致的。

Also, I thought the whole reason why pointers are type-specific was because the pointers need the size which can vary depending on the type.另外,我认为指针是特定于类型的全部原因是因为指针需要的大小可能会因类型而异。 How can a pointer be to something of unknown size?指针怎么可能指向未知大小的东西?

As to why you can have pointers to types without knowing all the details, it is an important feature of C that provides for the interworking of separately compiled modules.至于为什么你可以在不知道所有细节的情况下拥有指向类型的指针,它是 C 的一个重要特性,它提供了单独编译的模块的互通。 All pointers to structures (of any type) must have the same size and alignment requirements .所有指向结构(任何类型)的指针必须具有相同的大小和 alignment 要求 You can specify that the structure type exists by simply saying struct WhatEver;您可以通过简单地说struct WhatEver; where appropriate.在适当情况下。 That's usually at file scope, not inside a function;这通常在文件 scope 中,而不是在 function 中; there are complex rules for defining (or possibly redefining) structure types inside functions.在函数内部定义(或可能重新定义)结构类型有复杂的规则。 And you can then use pointers to that type without more information for the compiler.然后,您可以使用指向该类型的指针,而无需为编译器提供更多信息。

Without the detailed body of the structure ( struct WhatEver { … }; , where the braces and the content in between them are crucial), you cannot access what's in the structure, or create variables of type struct WhatEver — but you can create pointers ( struct WhatEver *ptr = NULL; ).如果没有结构的详细主体( struct WhatEver { … }; ,大括号和它们之间的内容至关重要),您将无法访问结构中的内容,或创建struct WhatEver类型的变量 - 但您可以创建指针 ( struct WhatEver *ptr = NULL; )。 This is important for 'type safety'.这对于“类型安全”很重要。 Avoid void * as a universal pointer type when you can, and you usually can avoid it — not always, but usually.尽可能避免将void *作为通用指针类型,并且通常可以避免它——并非总是如此,但通常如此。

Oh okay, so the obj.h in obj.c is a means of ensuring the prototype being used matches the definition, by causing an error message if they don't.哦,好吧,所以obj.h中的obj.c是一种确保正在使用的原型与定义匹配的方法,如果它们不匹配则会导致错误消息。

Yes.是的。

I'm still not entirely following in terms of all pointers having the same size and alignment.我仍然没有完全遵循所有具有相同大小和 alignment 的指针。 Wouldn't the size and alignment of a struct be unique to that particular struct?结构的大小和 alignment 对于该特定结构来说不是唯一的吗?

The structures are all different, but the pointers to them are all the same size.结构都是不同的,但指向它们的指针都是相同的大小。

And the pointers can be the same size because struct pointers can't be dereferenced, so they don't need specific sizes?并且指针可以是相同的大小,因为结构指针不能被取消引用,所以它们不需要特定的大小?

If the compiler knows the details of the structure (there's a definition of the structure type with the { … } part present), then the pointer can be dereferenced (and variables of the structure type can be defined, as well as pointers to it, of course).如果编译器知道结构的详细信息(存在带有{ … }部分的结构类型的定义),则可以取消引用指针(并且可以定义结构类型的变量以及指向它的指针,当然)。 If the compiler doesn't know the details, you can only define (and use) pointers to the type.如果编译器不知道细节,你只能定义(和使用)指向类型的指针。

Also, out of curiosity, why would one avoid void * as a universal pointer?另外,出于好奇,为什么要避免将void *作为通用指针?

You avoid void * because you lose all type safety.您避免使用void * ,因为您失去了所有类型安全性。 If you have the declaration:如果您有声明:

extern void *delicate_and_dangerous(void *vptr);

then the compiler can't complain if you write the calls:那么如果您编写调用,编译器就不会抱怨:

bool *bptr = delicate_and_dangerous(stdin);
struct AnyThing *aptr = delicate_and_dangerous(argv[1]);

If you have the declaration:如果您有声明:

extern struct SpecialCase *delicate_and_dangerous(struct UnusualDevice *udptr);

then the compiler will tell you when you call it with a wrong pointer type, such as stdin (a FILE * ) or argv[1] (a char * if you're in main() ), etc. or if you assign to the wrong type of pointer variable.那么编译器会在您使用错误的指针类型调用它时告诉您,例如stdin (a FILE * )或argv[1] (如果您在main()中,则为char * )等,或者如果您分配给错误类型的指针变量。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM