简体   繁体   English

在C中强制转换为指针类型时会发生什么情况?

[英]What is happening when casting to a pointer type in C?

I have seen that NULL is the equivalent of (void*)0 . 我已经看到NULL等于(void*)0 But I don't understand why zero needs to be typecasted to void* . 但是我不明白为什么需要将零强制转换为void* And what is really happening under the hood when we do something like this 当我们做这样的事情时,幕后真正发生的是什么

int *p = (int*)10;

Is (int*) extending the address space of integer 10(which take 4 bytes) to 8 bytes in the above statement? (int*)是否在上述语句中将整数10(占4个字节)的地址空间扩展到8个字节?

There are a couple of ways of answering this. 有两种解决方法。

We say that a pointer value is the address of a memory location. 我们说一个指针值是一个存储单元的地址。 But different computers have used different addressing schemes for memory. 但是,不同的计算机对内存使用了不同的寻址方案。 C is a higher-level language, portable across many kinds of computers. C是一种高级语言,可跨多种计算机移植。 C does not mandate a particular memory architecture. C不要求特定的内存体系结构。 As far as the C programming language is concerned, memory addresses could literally be things like "123 Fourth Ave.", and it's hard to imagine converting back and forth between an integer and an address like that. 就C编程语言而言,内存地址的字面意思可能是“ 123 Fourth Ave.”之类的东西,很难想象在整数和这样的地址之间来回转换。

Now, for any machine you're likely to use, memory is actually linearly addressed in a reasonably straightforward and unsurprising way. 现在,对于您可能使用的任何计算机,实际上都以合理直接且毫不奇怪的方式线性寻址了内存。 If your program has 1,000 bytes of memory available to it, the addresses of those bytes might range from 0 up to 999. So if you say 如果您的程序有1,000个字节的可用内存,则这些字节的地址范围可能从0到999。所以如果您说

char *cp = (char *)10;

you're just setting up a pointer to the byte located at address 10 (or, that is, the 11th byte in your program's address space). 您只是在建立指向地址10的字节的指针(即程序地址空间中的第11个字节)。

Now, in C, a pointer is not just the raw address of some location in memory. 现在,在C语言中,指针不仅仅是内存中某个位置的原始地址。 In C, a pointer is also declared to specify what type of data it points to. 在C中,还声明了一个指针以指定其指向的数据类型。 So if we say 所以如果我们说

int *ip = (int *)10;

we're setting up a pointer to one int's worth of data located at address 10. It's the same point in memory as cp pointed to, but since it's an int pointer, it's going to access an int's worth of bytes, not one byte like cp did. 我们正在建立一个指向位于地址10的int值的数据的指针。它与cp指向内存中的同一点,但是由于它是一个int指针,因此它将访问int的值的字节,而不是像cp做到了。 If we're on an old 16-bit machine, and int is two bytes, we could think of ip as pointing at the fifth int in our address space. 如果我们在旧的16位计算机上,并且int是两个字节,我们可以认为ip指向地址空间中的第五个int。

A cast in C can actually do two things: (1) convert a value ("change the bits"), or (2) change the interpretation of a value. 使用C进行强制转换实际上可以做两件事:(1)转换值(“更改位”),或(2)更改值的解释。 If we say float f = (float)3; 如果说float f = (float)3; , we're converting between the integer representation of 3 and a floating-point representation of 3, which is likely to be quite different. ,我们正在3的整数表示形式和3的浮点表示形式之间进行转换,这可能会大不相同。 If we go in the other direction, with something like int i = (int)3.14; 如果我们朝另一个方向前进,则类似int i = (int)3.14; , we're also throwing away the fractional part, so there's even more conversion going on. ,我们也舍弃了小数部分,因此还有更多的转换正在进行中。 But if we say int *ip = (int *)10; 但是如果我们说int *ip = (int *)10; , we're not really doing anything with the value 10, we're just reinterpreting it as a pointer. ,我们实际上并没有对值10做任何事情,我们只是将其重新解释为指针。 And if we say char *cp = (char *)ip , we're again not changing anything, we're just reinterpreting to a different kind of pointer. 如果我们说char *cp = (char *)ip ,我们再次没有改变任何东西,我们只是在重新解释为另一种指针。

I hasten to add, though, that everything I've said here about pointer conversions is (a) very low-level and machine-dependent, and (b) not the sort of thing that ordinary C programmers are supposed to have to think about during ordinary programming tasks, and (c) not guaranteed by the C language. 不过,我要赶紧补充一点,我在这里所说的关于指针转换的所有内容都是(a)非常底层且与机器相关,并且(b)并非普通C程序员应该考虑的那种事情在普通的编程任务中,并且(c)不受C语言的保证。

In particular, even when programming for a computer with a conventional, linearly-addressed memory model, it's likely that your program doesn't have access to address 10, so these pointers ( cp and ip ) might be pretty useless, might generate exceptions if you try to use them. 特别是,即使为具有传统的线性寻址内存模型的计算机编程时,您的程序也很可能无法访问地址10,因此这些指针( cpip )可能几乎毫无用处,如果您尝试使用它们。 (Also, when we have a pointer like ip that points at more than 1 byte, there's the question of which bytes it points to. If ip is 10, it probably points at bytes 10 and 11 on a 16-bit, byte-addressed machine, but which of those two bytes is the low-order half of the int and which is the high-order half? It depends on whether it's a "big endian" or "little endian" machine.) (此外,当我们有一个类似ip的指针指向一个以上的字节时,存在一个问题,即它指向哪个字节。如果ip为10,则它可能指向16位字节寻址的字节10和11。机器,但是这两个字节中的哪一个是int的低阶一半,哪个是高阶一半呢?这取决于它是“ big endian”还是“ little endian”机器。)

But then we come to null pointers. 但是接下来我们来谈谈空指针。 When you use a constant "0" as a pointer value, things are a little different. 当使用常量“ 0”作为指针值时,情况会有所不同。 If you say 如果你说

void *p = (void *)0;

you are not, strictly speaking, saying "make p point to address 0 ". 严格来讲,您不是在说“使p指向地址0 ”。 Instead, you are saying "make p be a null pointer". 而是说“使p为空指针”。 But it turns out this has nothing to do with the cast, it's because of a special case in the language: in a pointer context, the constant 0 represents a null pointer constant . 但事实证明,这与转换无关,这是由于该语言中的一种特殊情况:在指针上下文中,常量0表示空指针常量

A null pointer is a special pointer value that's defined to point nowhere. 空指针是一种特殊的指针值,定义为无指向。 It might be represented internally as a pointer to address 0, or it might be represented some other way. 它可能在内部表示为指向地址0的指针,或者可能以其他方式表示。 (If it is in fact represented as a pointer to address 0, your compiler will be careful to arrange that there's never any actual data at address 0, so that it's still true that the pointer "points nowhere" even though it points to address 0. This is sort of confusing, sorry about that.) (如果实际上将它表示为指向地址0的指针,则编译器将谨慎安排地址0处永远没有任何实际数据,因此,即使指针指向地址0也是“毫无指向”的事实, 。这有点令人困惑,对此表示抱歉。)

Although pointers to raw addresses like 10 are low-level and dangerous and machine-dependent, null pointers are well-defined and perfectly fine. 尽管指向10类的原始地址的指针是低级的且危险且与机器相关,但空指针是定义良好且完全正确的指针。 For example, when you call malloc and it can't give you the memory you asked for, it returns a null pointer to tell you so. 例如,当您调用malloc却无法为您提供所需的内存时,它将返回一个空指针来告诉您。 When you test malloc 's return value to see if it succeeded or failed, you just check to see if it gave you a null pointer or not, and there's nothing low-level or nonportable or discouraged about doing so. 当测试malloc的返回值以查看它是成功还是失败时,您只需检查它是否给了您一个空指针,就没有低级的,不可移植的或不鼓励这样做的了。

See http://c-faq.com/null/index.html for much more on all this. 有关所有这些的更多信息,请参见http://c-faq.com/null/index.html

A pointer is a short arrangement of bytes. 指针是字节的简短排列。 Via a cast, C allows to pretend these bytes represent an integer. 通过强制转换,C允许假装这些字节代表整数。

Type* p = ...;
intptr_t i = (intptr_t)p;

This is occasionally (but rarely) useful when you need to pass a pointer to an interface expecting an integer. 当您需要将指针传递给期望整数的接口时,这偶尔(但很少)有用。 To recover the pointer, one just reverses the cast. 要恢复指针,只需反转转换即可。

Type* recovered_p = (Type*)i;

This does not allocate any memory. 这不会分配任何内存。 You can only deference recovered_p if i contain bytes that, if treated as a Type* , references a previously allocated Type value. 你只能顺从recovered_p如果i包含字节,如果当作Type* ,引用以前分配的Type值。 This means that the following doesn't produce a usable pointer: 这意味着以下代码不会产生可用的指针:

int *p = (int*)10;

An example that uses an integer to store a pointer. 一个使用整数存储指针的示例。

typedef void (*Visitor)(intptr_t, ListNode*);

void List_visit(List* list, Visitor visitor, intptr_t arg) {
   for (ListNode* node = list->head; node; node=node->next) {
      visitor(arg, node);
   }
}

void printer(intptr_t arg, ListNode* node) {
   State* state = (intptr_t)arg;
   printf("%*s%s\n", ( state->count++ )*2, "", node->value);
}

int main(void) {
   List* list = ...;
   State* state = ...;
   List_visit(list, printer, (intptr_t)state);
   List_free(list);
   State_free(state);
   return 0;
}

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

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