英文:
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 <stdio.h>
int main()
{
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);
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 <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;
}
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 <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;
}
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 &pCtrl->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 &pCtrl->buffer
to be the address of the string as well, if pCtrl->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 = &p_foo
是指向 p_foo
的指针。p_foo
是一个指针,指向 foo
。通过 *a
,你试图打印指针 p_foo
的值,也就是 foo
本身的地址,作为一个字符串。
如果你真的想要这样做,你可以解引用 a
来获取 p_foo
,然后再解引用 p_foo
来获取 foo
的地址,该地址等于 foo.bar
的地址,其中保存了要打印的字符串的地址:
char ***a = &p_foo;
printf("%s", **a);
但实际上,你的代码违反了语言约束,是无效的。你应该使用正确的类型,并正确访问结构成员:
struct_t **a = &p_foo;
printf("%s", (*a)->bar);
英文:
> Why is that happening?
a = &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 = &p_foo;
printf("%s", **a);
But really, your code breaks language constraints and is invalid. You should use correct types and properly access structure members:
struct_t **a = &p_foo;
printf("%s", (*a)->bar);
答案2
得分: 1
以下是翻译好的部分:
- 初始声明中存在一个结构声明的拼写错误
typedef struct {
char * bar
^^^^^^^^^^
} struct_t;
你需要在数据成员声明后加上一个分号
typedef struct {
char * bar;
} struct_t;
- 在这个声明中的初始化
struct_t foo = {};
在C23标准之前是无效的。如果您的编译器不按照C23标准编译程序,则不能使用空花括号。您可以写成
struct_t foo = { NULL };
或者
struct_t foo = { .bar = NULL };
或者简单地写成
struct_t foo = { .bar = "test" };
- 对于这个声明
char ** a = &p_foo;
编译器应该会发出警告,因为指针类型char **
和struct_t **
之间没有隐式转换。
- 至于您的问题,对于
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 = "test" };
For this declaration
char ** a = &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("%s %s",*a,*b);
you get the pointer p_foo
that points to the object of the type
struct_t *
declared like.
struct_t * p_foo = &foo;
The pointer does not contain the address of the first character of the string literal "test"
. Instead it contains the address of the object foo
.
On the other hand the pointer p
char * p = p_foo->bar;
was assigned by the value of the pointer p_foo->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 = "test";
struct_t * p_foo = &foo;
char * p = p_foo->bar;
In fact the last declaration
char * p = p_foo->bar;
looks like
char * p = "test";
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("%p %p\n", ( void * )*a,( void * )*b);
To get the expected result you need to write
char **a = ( char ** )p_foo;
instead of
char ** a = &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 = "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);
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论