结构体中的地址

huangapple go评论86阅读模式
英文:

Addresses in structures

问题

下面是我目前遇到的问题的抽象版本。

#include <stdio.h>

int main()
{
    typedef struct {
        char * bar;
    } struct_t;
    
    struct_t foo = { .bar = "test" };
    
    // works
    struct_t * p_foo = &foo;
    char * p_bar = &p_foo->bar[0];
    char ** a = &p_bar;
    
    // does not work
    char ** b = &p_foo->bar;
    
    printf("%s %s", *a, *b);

    return 0;
}

然而,这个版本实际上会打印两次 test,这意味着抽象版本不能重现我的问题,所以我必须再试一次。

#include <stdio.h>

void callback(void ** ppContext)
{
    char * pString = (char *) *(ppContext);
    printf("%s", pString);
}

int main()
{
    typedef struct {
        char buffer[5];
    } struct_t;
    
    struct_t ctrl = { .buffer = "test" };
    struct_t * pCtrl = &ctrl;
    
    // works
    char * pString = &pCtrl->buffer[0];
    callback((void **) &pString);
    
    // does not work
    callback((void **) &pCtrl->buffer);

    return 0;
}

这是实际的代码,我只删除了与问题无关的部分。名为 //works 的部分按预期打印 test。名为 //does not work 的部分则不起作用。

我期望 &pCtrl->buffer[0] 是缓冲区中第一个字符的地址,它存储在 pString 中。由于回调以 pString 的地址调用,回调中的 (char *) *(ppContext) 使它成为实际的字符串,即缓冲区中第一个字符的指针。

然而,我会期望 &pCtrl->buffer 也是字符串的地址,如果 pCtrl->buffer 是字符串本身,即缓冲区中第一个字符的地址。但显然情况并非如此,我不明白为什么。

英文:

The following is an abstract version of a problem I am currently having.

#include &lt;stdio.h&gt;

int main()
{
    typedef struct {
        char * bar
    } struct_t;
    
    struct_t foo = {};
    foo.bar = &quot;test&quot;;
    struct_t * p_foo = &amp;foo;
    
    char * p = p_foo-&gt;bar;
    char ** a = &amp;p_foo;
    char ** b = &amp;p_foo-&gt;bar;
    
    printf(&quot;%s %s&quot;,*a,*b);

    return 0;
}

I expect the code to print test twice, which it doesn't. Why is that happening?

******************************* EDIT *******************************

The abstraction I WANTED to post is the following, it's basically what you guys pointed out to be wrong in my first attempt - thanks for that.

#include &lt;stdio.h&gt;

int main()
{
    typedef struct {
        char * bar;
    } struct_t;
    
    struct_t foo = { .bar = &quot;test&quot; };
    
    // works
    struct_t * p_foo = &amp;foo;
    char * p_bar = &amp;p_foo-&gt;bar[0];
    char ** a = &amp;p_bar;
    
    // does not work
    char ** b = &amp;p_foo-&gt;bar;
    
    printf(&quot;%s %s&quot;,*a,*b);

    return 0;
}

However, this version does actually print test twice, meaning that the abstraction does not reproduce my problem, so I have to try once again:

#include &lt;stdio.h&gt;

void callback(void ** ppContext)
{
    char * pString = (char *) *(ppContext);
    printf(&quot;%s&quot;,pString);
}

int main()
{
    typedef struct {
        char buffer[5];
    } struct_t;
    
    struct_t ctrl = { .buffer = &quot;test&quot; };
    struct_t * pCtrl = &amp;ctrl;
    
    // works
    char * pString = &amp;pCtrl-&gt;buffer[0];
	callback((void **) &amp;pString);
    
    // does not work
    callback((void **) &amp;pCtrl-&gt;buffer);

    return 0;
}

This is the actual code, I just deleted everything non-related. The section named //works prints test as expected. The section named //does not work does not.
(I commented out one of them respectively to test it, an fflush inbetween them would make testing them simultaneously possible.)

I expect &amp;pCtrl-&gt;buffer[0] to be the address of the first char in the buffer which is stored in pString. Since the callback is called with the address of pString, the (char *) *(ppContext) in the callback makes it the actuall string again, i.e., a pointer to the first char in the buffer.

However, I would expect &amp;pCtrl-&gt;buffer to be the address of the string as well, if pCtrl-&gt;buffer is the string itself, i.e., the address of the first char in the buffer. But apparantly that is not the case and I don't understand why.

答案1

得分: 2

> 为什么会发生这种情况?

a = &amp;p_foo 是指向 p_foo 的指针。p_foo 是一个指针,指向 foo。通过 *a,你试图打印指针 p_foo 的值,也就是 foo 本身的地址,作为一个字符串。

如果你真的想要这样做,你可以解引用 a 来获取 p_foo,然后再解引用 p_foo 来获取 foo 的地址,该地址等于 foo.bar 的地址,其中保存了要打印的字符串的地址:

char ***a = &amp;p_foo;
printf("%s", **a);

但实际上,你的代码违反了语言约束,是无效的。你应该使用正确的类型,并正确访问结构成员:

struct_t **a = &amp;p_foo;
printf("%s", (*a)->bar);
英文:

> Why is that happening?

a = &amp;p_foo is a pointer to p_foo. p_foo is a pointer and points to foo. By doing *a you are trying to print the value of the pointer p_foo, i.e. the address of foo itself, as a string.

If you really want to do that, you might dereference a to get p_foo and then dereference p_foo to get the address of foo which is equal to the address of foo.bar which holds the address of the string to be printed:

char ***a = &amp;p_foo;
printf(&quot;%s&quot;, **a);

But really, your code breaks language constraints and is invalid. You should use correct types and properly access structure members:

struct_t **a = &amp;p_foo;    
printf(&quot;%s&quot;, (*a)-&gt;bar);

答案2

得分: 1

以下是翻译好的部分:

  1. 初始声明中存在一个结构声明的拼写错误
typedef struct {
    char * bar
    ^^^^^^^^^^
} struct_t;

你需要在数据成员声明后加上一个分号

typedef struct {
    char * bar;
} struct_t;
  1. 在这个声明中的初始化
struct_t foo = {};

在C23标准之前是无效的。如果您的编译器不按照C23标准编译程序,则不能使用空花括号。您可以写成

struct_t foo = { NULL };

或者

struct_t foo = { .bar = NULL };

或者简单地写成

struct_t foo = { .bar = "test" };
  1. 对于这个声明
char ** a = &p_foo;

编译器应该会发出警告,因为指针类型char **struct_t **之间没有隐式转换。

  1. 至于您的问题,对于printf调用中指针a的解引用
printf("%s %s", *a, *b);

您会得到指向类型为struct_t *的对象的指针p_foo,该对象声明如下:

struct_t * p_foo = &foo;

该指针不包含字符串字面值"test"的第一个字符的地址,而是包含对象foo的地址。

另一方面,指针p

char * p = p_foo->bar;

通过指针p_foo->bar的值分配了一个值,该值包含字符串字面值的地址。因此,在printf调用中使用表达式*b会产生与使用指针表达式*a相反的预期结果。

只需比较一下指针的初始化值是如何的:

foo.bar = "test";
struct_t * p_foo = &foo;

char * p = p_foo->bar;

实际上,最后一个声明

char * p = p_foo->bar;

看起来像

char * p = "test";

当然,前提是匹配的字符串字面值由编译器存储为一个字符串字面值。

您可以通过以下方式检查指针表达式*a*b产生的地址值:

printf("%p %p\n", (void *)*a, (void *)*b);

为了得到预期的结果,您需要这样写:

char **a = (char **)p_foo;

而不是

char ** a = &p_foo;

现在指针a指向结构的对象foo。解引用指针后,您会得到数据成员(指针)bar的地址,该地址与包含数据成员bar的对象foo的地址相同。

英文:

For starters at least there is a typo in the structure declaration

typedef struct {
    char * bar
    ^^^^^^^^^^
} struct_t;

you need to place a semicolon after the data member declaration

typedef struct {
    char * bar;
} struct_t;

The initialization in this declaration

struct_t foo = {};

is invalid before the C23 Standard. You may not use empty braces if your compiler does not compile the program according to the C23 Standard. You could write for example

struct_t foo = { NULL };

or

struct_t foo = { .bar = NULL };

or simply

struct_t foo = { .bar = &quot;test&quot; };

For this declaration

char ** a = &amp;p_foo;

the compiler should issue a message because there is no implicit conversion between the pointer types char ** and struct_t **.

As for your question then dereferencing the pointer a in the call of printf

printf(&quot;%s %s&quot;,*a,*b);

you get the pointer p_foo that points to the object of the type
struct_t * declared like.

struct_t * p_foo = &amp;foo;

The pointer does not contain the address of the first character of the string literal &quot;test&quot;. Instead it contains the address of the object foo.

On the other hand the pointer p

char * p = p_foo-&gt;bar;

was assigned by the value of the pointer p_foo-&gt;bar that contains the address of the string literal. So using the expression *b in the call of printf yields the expected result opposite to using the the pointer expression *a.

Just compare how the pointers that is with which values they were initialized

foo.bar = &quot;test&quot;;
struct_t * p_foo = &amp;foo;

char * p = p_foo-&gt;bar;

In fact the last declaration

char * p = p_foo-&gt;bar;

looks like

char * p = &quot;test&quot;;

of course provided that matching string literals are stored by the compiler as one string literal.

You can check the values of addresses produced by the pointer expressions *a and *b the following way

 printf(&quot;%p %p\n&quot;, ( void * )*a,( void * )*b);

To get the expected result you need to write

char **a = ( char ** )p_foo;

instead of

char ** a = &amp;p_foo;

That is now the pointer a points to the object foo of the structure. Dereferencing the pointer you get the data member (pointer) bar address of which coincides with the address of the object foo that contains the data member bar.

答案3

得分: 0

问题出在变量b的声明上:char ** b = &p_foo->bar;。在这里,您正在将p_foo->bar的地址,即char*,赋给char**变量(b)。

b的正确类型应该是char*,而不是char**。这是因为p_foo->bar已经是char*类型。将其地址赋给char**会导致错误的间接引用级别。

请参考下面的代码:

typedef struct {
    char * bar;
} struct_t;

struct_t foo = {};
foo.bar = "test";
struct_t * p_foo = &foo;

char * p = p_foo->bar;
char ** a = &p_foo;
char * b = p_foo->bar;

printf("%s %s", *a, b);
英文:

The problem occurs with the b variable declaration: char ** b = &p_foo->bar;. Here, you are assigning the address of p_foo->bar, which is a char*, to a char** variable (b).

The correct type for b should be char*, not char**. This is because p_foo->bar is already of type char*. Assigning its address to a char** will lead to an incorrect level of indirection.

Please refer to the below code:

typedef struct {
    char * bar;
} struct_t;

struct_t foo = {};
foo.bar = &quot;test&quot;;
struct_t * p_foo = &amp;foo;

char * p = p_foo-&gt;bar;
char ** a = &amp;p_foo;
char * b = p_foo-&gt;bar;

printf(&quot;%s %s&quot;, *a, b);

huangapple
  • 本文由 发表于 2023年7月12日 23:55:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/76672440.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定