typedef struct mystruct{
int a;
char arr[10];
char *str;
}mystruct;
void f(void *data, int offset){
char *s = (char *)(data + offset);
printf("%s", s);
}
void g(void *data, int offset){
char *s = *(char **)(data+offset);
printf("%s", s);
}
int main(){
mystruct test;
test.a = 2;
test.str = malloc(100);
strcpy(test.arr, "Hello ");
strcpy(test.str, "World!");
f(&test, offsetof(mystruct,arr));
g(&test, offsetof(mystruct,str));
return 0;
}
我想知道为什么我需要两种不同的方式来打印字符串。在函数 f 中,实际指向的是什么(数据+偏移量)?是不是指向 arr 这是指向字符串第一个元素的char指针?但是在函数 g 中,(data + offset)也指向了char指针。那么为什么必须使用两种不同的方法来完成同样的任务呢?
答案 0 :(得分:3)
在这两种情况下data+offset
都指向结构的成员。
但是让我们看看结构的结构。它由
组成+-----+--------------------------------------------+
| a | sizeof(int) probably 4 or 8 bytes |
+-----+--------------------------------------------+
| possible padding of unknown size (probably zero) |
+-----+--------------------------------------------+
| arr | 10 bytes |
+-----+--------------------------------------------+
| possible padding of unknown size (maybe 2 bytes) |
+-----+--------------------------------------------+
| str | sizeof(char*) probably 4 or 8 bytes |
+-----+--------------------------------------------+
和内存中的其他位置是使用malloc
分配的100字节块。
请注意,test.arr
的数据存储在中分配给test
的内存中,但test
中test.str
存储的内容是另一块内存的地址。
答案 1 :(得分:1)
您必须向编译器提供有关函数f
和g
中指针类型的信息,不能对void指针进行点运算。
将data
转换为char指针,printf将起作用
void f(void *data, int offset){
char *s = (char *)(( char*)data + offset);
printf("%s", s);
}
void g(void *data, int offset){
char *s = *(char **)((char*)data+offset);
printf("%s", s);
}
答案 2 :(得分:1)
在函数
f
中,(data + offset)
实际指向的是什么?不是吗 指向arr
,它是指向第一个元素的char指针 字符串?
这是您混淆的主要原因。 arr
不是指针,而是数组。数组不是指针。同时,str
是一个指针。 “数组”和“指针”是两个完全不同的东西。他们几乎没有任何共同之处。这正是你必须以不同方式与他们合作的原因。
在所谓的值上下文中(即用作 rvalues 时),数组和指针的行为非常相似,但这纯粹是肤浅的相似性。他们立即揭示了他们在所谓的对象上下文中的主要差异(即当用作 lvalues 时)。在您的特定示例中,您的成员访问代码是对象上下文的示例,这就是您必须仔细观察arr
和str
之间差异的原因。
阵列和指针之间的差异问题已经多次讨论过了。我认为没有理由在此重复。只需在SO上搜索“数组指针差异”即可。或阅读必要的常见问题
http://c-faq.com/aryptr/index.html
P.S。另请注意,C语言不支持void *
指针上的指针算法。您的所有(data + offset)
表达式均无效。你想要做的是((char *) data + offset)
。要将data
从void *
转换为char *
,才能使用字节偏移执行点算术。
答案 3 :(得分:0)
在你的结构中,在(推测)偏移量4处,是一个一个接一个的10个字节的列表,包含使得' Hello&#39 ;;所以(data + 4)是char的指针,需要相应地进行dcoded(即char *
)。
然而,在这10个字节之后,出现一些字节使得缓冲区的地址在某处,即那些字节是' char *' (你这样定义了它们),所以数据+偏移有一个指针指向一个char指针或char **
。
可能令人困惑的是两者都
strcpy(test.arr," Hello");
strcpy(test.str," World!");
的工作。
这是一个令人困惑的(但很有用的C / C ++特性)。当数组的名称在需要指向数组元素类型的指针的地方使用时,编译器将把它视为指向数组第一个元素的指针。
所以test.str
显然是一个指向char的指针(因为你这样定义)。如果情况表明,test.arr
可以用作指向第一个测试元素的指针。
编写strcpy(test.arr, "Hello ");
编译器假定您的意思是strcpy(&test.arr[0], "Hello ");
答案 4 :(得分:0)
在函数f中,实际指向的是什么(数据+偏移量)?
它指向结构对象的'arr'成员(如果你假设GCC的void指针算术的语义)。
它是否指向arr,它是指向字符串第一个元素的char指针?
arr是一个char数组,而不是char指针。
但是在函数g中,(data + offset)也指向了char指针。
在这种情况下,它指向一个char指针,是的。
那么为什么必须使用两种不同的方法来完成同样的任务呢?
指针指向两个不同的东西 - 一个指向char指针(它本身指向一个char数组),另一个指向char数组。在第一种情况下还有一个间接层次。