C 指针语法

发布于 2024-12-22 18:08:13 字数 193 浏览 2 评论 0原文

这两行代码有什么区别?

int *ptr = &x;

void* q = &x;
int* p = q;

对 C 和指针的概念非常陌生 - 主要是在 Java 中教授的 - 所以有点困惑。

提前致谢。

What is the difference between these two lines of code?

int *ptr = &x;

and

void* q = &x;
int* p = q;

I'm very new to C and the concept of pointers - having been taught primarily in Java - so just a little confused.

Thanks in advance.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

像你 2024-12-29 18:08:13

void * 用于表示 C 中的通用指针。
这意味着它可以指向任何类型。

因此,在第一种情况 int *ptr = &x; 中,您使用指向 int 的指针,因此任何使用它的人都知道它正在操作一个整数。

在第二种情况下 void* q = &x; 您通过一个 Gereric 指针指向一个整数地址。
问题是不清楚这个指针指向什么类型。

因此,第一个和第二个示例具有相同的效果(在您的具体示例中),但是像这样使用 void * 并不安全。

void * is used to note a generic pointer in C.
Meaning it can point to any type.

So in the first case int *ptr = &x; you use a pointer to an int so anyone using it knows it is manipulating an integer.

In the second case void* q = &x; you point to an integer address via a gereric pointer.
The problem is that it is not clear what type this pointer is refering to.

So the first and 2 examples have the same effect (in your specific example) but the void * is not safe to be used like this.

世界和平 2024-12-29 18:08:13

void* q = &x;

了解事物在内存中的表示方式可能是值得的,这样您就可以理解 void* 的含义。指针是内存中的一个位置,其中包含内存中的另一个位置。对于实际示例,请考虑以下代码:

int x = 4;
int* y = &x;
void* z = &x;

在我的 64 位系统上,我可以使用 gdb 来检查内存。结果如下表所示:

Address         | Memory Value                               | C expression
================================================================================
0x7fffffffdf8c  | 0x04 0x00 0x00 0x00                        | int x = 4;
0x7fffffffdf98  | 0x8c 0xdf 0xff 0xff 0xff 0x7f 0x00 0x00    | int* y = &x;
0x7fffffffdf90  | 0x8c 0xdf 0xff 0xff 0xff 0x7f 0x00 0x00    | void* z = &x;

我们在这里看到了什么?好吧,0x7fffffffdf8c0x7fffffffdf90 由值 0x04 和全零占据 - 我的平台上的整数是 4 字节宽,顺序是小端字节序,因此字节与人类期望读取的字节相反。然后,我们看到接下来的 8 个字节被 x 的地址占用,第二个指针也是如此。

因此,使用指针的过程是读取某个地址处的地址并使用它。听起来您可能对这个概念相当满意,所以,继续:

指针的类型不会影响指针的大小。这是关键。查看上面的两个指针值,它们实际上都是相同的大小。这里的大小值涉及目标内存 - 它指示编译器加载并操作一定数量(字节数 - 类型的大小/宽度)的内存。

void*,正如其他人所说,是“无类型的”。相反,它只是一个指针,编译器/C 语言将无法支持您取消引用它,因为没有类型信息 - 无法安全地知道目标有多少内存您想阅读的地址。

然而,这个事实有时是有用的。使用类型的想法是为了在代码中提供一致性 - 如果函数需要 64 位整数,则使用类型会强制执行此要求,这样就不会引入错误。然而,有时您并不介意获得什么类型。在这些情况下,您的要求是“一些内存。任何内存!” - 我能想到的最好的例子是 memcpy - 它的工作原理可能有点像这样:

void *memcpy(void * s1, const void* s2, size_t n)
{
    char *r1 = s1;
    const char *r2 = s2;
    while (n) {
        *r1++ = *r2++;
        -n;
    }
    return s1;
}

改编自 uclibc。在这里,变量类型根本不重要 - 在内部,函数决定以 sizeof(char) 类型操作内存(char 通常但不总是字节宽)但它同样可以对 uint64_t 或其他值进行操作。类型在这里所做的只是控制从起始地址开始有多少字节被视为类型的一部分。

为了给您另一个表格,这里是一些类型大小的比较:

Address of ptr  | Type in code    | Memory it "sees" when dereferenced
===========================================================================
0x7fffffffdf90  | unsigned 64-bit | 0x8c 0xdf 0xff 0xff 0xff 0x7f 0x00 0x00
0x7fffffffdf90  | unsigned 32-bit | 0x8c 0xdf 0xff 0xff 
0x7fffffffdf90  | unsigned 16-bit | 0x8c 0xdf 
0x7fffffffdf90  | unsigned  8-bit | 0x8c 
0x7fffffffdf90  | void*           | Doesn't know how wide it is.

您可能想知道为什么 libc 函数中没有强制转换 - 好吧,没有必要。所有指针的大小相同,因此无需执行任何其他操作。

void* q = &x;

It's probably worth understanding how things are represented in memory so you understand the meaning of void*. A pointer is a position in memory containing another position in memory. For a real-world example, consider the following code:

int x = 4;
int* y = &x;
void* z = &x;

On my 64-bit system, I can use gdb to examine the memory. The results are as you see in the following table:

Address         | Memory Value                               | C expression
================================================================================
0x7fffffffdf8c  | 0x04 0x00 0x00 0x00                        | int x = 4;
0x7fffffffdf98  | 0x8c 0xdf 0xff 0xff 0xff 0x7f 0x00 0x00    | int* y = &x;
0x7fffffffdf90  | 0x8c 0xdf 0xff 0xff 0xff 0x7f 0x00 0x00    | void* z = &x;

What do we see here? Well, 0x7fffffffdf8c to 0x7fffffffdf90 is taken up by the value 0x04 and all zeros - integers on my platform are 4-bytes wide and the order is little endian, so the bytes are the opposite of what a human would expect to read. Then, we see the next 8-bytes are taken up by none other than the address of x, and likewise again for the second pointer.

So the process of using a pointer is to read the address at an address and work with that. It sounds like you might be fairly happy with that concept as is, so, moving on:

The type of the pointer does not affect the size of the pointer. This is key. Looking at both your pointer values above, they are actually both the same size. The size value here talks about the target memory - it instructs the compiler to load and operate on a certain amount (number of bytes - the size/width of a type) of memory.

void*, as others have said, is "typeless". Rather, it is just a pointer and the compiler/C language will not be able to support you dereferencing it, since there is no type information - there is no way to safely tell how much memory at the target address you want to read.

However, this fact is sometimes useful. The idea of using types is to provide consistency in your code - if a function expects a 64-bit integer, using types enforces this requirement so that you don't introduce errors. Sometimes, however, you don't mind what type you get. In these cases, your requirements are "some memory. any memory!" - the best example of this I can think of is memcpy - which might work a little like this:

void *memcpy(void * s1, const void* s2, size_t n)
{
    char *r1 = s1;
    const char *r2 = s2;
    while (n) {
        *r1++ = *r2++;
        -n;
    }
    return s1;
}

Adapted from uclibc. Here, the variable types do not matter at all - internally, the function decides to manipulate memory in sizeof(char) types (char is usually but not always a byte wide) but it could equally have operated on uint64_t or some other value. All the type does here is control how many bytes are being considered from the start address as part of the type.

To give you another table, here's a comparison of some type sizes:

Address of ptr  | Type in code    | Memory it "sees" when dereferenced
===========================================================================
0x7fffffffdf90  | unsigned 64-bit | 0x8c 0xdf 0xff 0xff 0xff 0x7f 0x00 0x00
0x7fffffffdf90  | unsigned 32-bit | 0x8c 0xdf 0xff 0xff 
0x7fffffffdf90  | unsigned 16-bit | 0x8c 0xdf 
0x7fffffffdf90  | unsigned  8-bit | 0x8c 
0x7fffffffdf90  | void*           | Doesn't know how wide it is.

You might wonder why there are no casts in that libc function - well, there is no need. All pointers are the same size so nothing else needs to be done.

一口甜 2024-12-29 18:08:13

ptrp 的效果在这两种情况下是相同的,因为 void * 指针保证在强制转换之间可转换。

void *q = &x 只是将x的地址存储在q中,而不关心x指向什么到。 int *p = q 然后将此地址分配给一个实体,该实体的语义描述为它指向 int

请注意,在这种情况下,操作是安全的,因为 x 的类型为 int,而 p 的类型为 int * 。如果您将 q 分配给类型为 double * 的指针,情况就不会如此。

The effect for ptr and p is the same in both cases, since a void * pointer is guaranteed to be convertible between casts.

void *q = &x simply stores the address of x in q, without caring what x is pointing to. int *p = q then assigns this address to an entity whose semantics describe that it is pointing to an int.

Note that in this case the operation is safe because x is of type int and p is of type int *. It wouldn't be so if you assigned q to a pointer of type, say, double *.

ㄟ。诗瑗 2024-12-29 18:08:13

两个语句是相同的,但是第一个语句是首选,因为在第二个语句中您将摆脱危险的类型。

类型的主要目的是防止您分配不兼容的变量,例如苹果和汽车。

因此,

void* q = &x;

摆脱了 x 的类型

int* p = q;

,并将未知类型转换为整数上的指针。您最好将 q 转换为 (int *) 以表示您意识到危险。

Both statements are the same, but the first one is preferred as in the second one you are getting rid of the type which is dangerous.

The principal purpose of types is that they prevent you from assigning the variables which are not compatible, like apples and cars.

Thus,

void* q = &x;

gets rid of the type of x

int* p = q;

and this one casts an unknown type to the pointers on integers. You should best cast q to (int *) to signify that you are aware of the danger.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文