C语言中静态数组和动态数组的混淆

C语言中静态数组和动态数组的混淆,c,pointers,C,Pointers,我想知道为什么我需要两种不同的方式来打印字符串。在函数f中,(数据+偏移量)实际指向什么?它不是指向arr,它是指向字符串第一个元素的字符指针吗?但是在函数g中,(数据+偏移量)也指向一个字符指针。那么为什么必须使用两种不同的方法来完成相同的任务呢?您必须向编译器提供有关函数f和g中指针类型的信息,您不能对无效指针执行点运算 将数据转换为字符指针,printf将起作用 typedef struct mystruct{ int a; char arr[10]; char *str; }

我想知道为什么我需要两种不同的方式来打印字符串。在函数f中,(数据+偏移量)实际指向什么?它不是指向arr,它是指向字符串第一个元素的字符指针吗?但是在函数g中,(数据+偏移量)也指向一个字符指针。那么为什么必须使用两种不同的方法来完成相同的任务呢?

您必须向编译器提供有关函数
f
g
中指针类型的信息,您不能对无效指针执行点运算

数据
转换为字符指针,printf将起作用

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;
}

在这两种情况下,
data+offset
都指向结构的一个成员

但让我们看看结构的结构。它包括

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);
}
内存中的其他地方是一个分配有
malloc
的100字节块

请注意,
test.arr
的数据存储在分配给
test
的内存中,但是
test
中存储的
test.str
是另一个内存块的地址。

在结构中,偏移量4处,是一个10字节的列表,包含构成“你好”的字母;因此(data+4)是指向char的指针,需要相应地进行dcoded(即
char*

但是,在这10个字节之后,有几个字节构成了某个缓冲区的地址,也就是说,这些字节是“char*”(您这样定义的),所以data+offset有一个指针指向char指针或
char**

可能令人困惑的是,两者

strcpy(test.arr,“你好”)
strcpy(test.str,“世界!”)

工作

这是一个令人困惑的特性(但C/C++的有用特性)。当数组的名称用于需要指向数组元素类型的指针的位置时,编译器会将其视为指向数组第一个元素的指针

所以
test.str
显然是指向char的指针(因为您是这样定义的)
test.arr
可以用作指向测试第一个元素的指针(如果情况表明)

当您编写strcpy时(test.arr,“Hello”)编译器假定您的意思是
strcpy(&test.arr[0],“Hello”)

在函数f中,(数据+偏移量)实际指向什么

它指向structure对象的“arr”成员(如果您假定GCC的语义用于void指针算法)

它不是指向arr吗?arr是指向字符串第一个元素的字符指针

arr是字符数组,不是字符指针

但在函数g中,(data+offset)也指向一个char指针

在本例中,它指向一个char指针,是的

那么,为什么必须使用两种不同的方法来完成相同的任务呢

指针指向两个不同的对象-一个指向char指针(它本身指向char数组),另一个指向char数组。在第一种情况下,还有一个间接层次

在函数
f
中,
(数据+偏移量)
实际上指向什么?不是吗 指向
arr
,它是指向 绳子

这是你困惑的主要根源
arr
不是指针,而是数组。数组不是指针。同时,
str
是一个指针。“数组”和“指针”是两个完全不同的东西。他们几乎没有共同之处。这就是为什么你必须以不同的方式与他们合作

数组和指针在所谓的值上下文(即用作右值)中的行为非常相似,但这纯粹是表面上的相似。它们立即揭示了它们在所谓的对象上下文中的主要差异(即当用作左值时)。在您的特定示例中,访问代码的成员是对象上下文的示例,这就是为什么您必须仔细观察
arr
str
之间的差异

数组和指针之间的差异问题已经讨论过很多次了。我看不出有任何理由在这里重复。只需搜索“数组指针差异”就可以了。或者阅读必要的常见问题解答


另请注意,C语言不支持
void*
指针上的指针算法。所有
(数据+偏移量)
表达式都无效。您真正想要做的是
((char*)数据+偏移量)
。需要将
数据
void*
转换为
char*
,以便能够使用字节偏移量执行点运算。

此代码对
void*
类型执行运算,这在C中是不允许的。据说从void*到….不需要强制转换,您的代码已经无效<代码>数据+偏移量
表达式已经不是C,因为C语言不支持
void*
指针上的指针算法。您使用的是某些特定编译器提供的非标准编译器扩展。@davmac:但许多世界上最优秀的C开发人员将其用作编译器扩展,就好像它的类型是
char*
:)@安德烈,我对你的说法感到很困惑。我很确定Linux内核。。。是用C编写的。它有一些特定于平台的汇编语言,但大多数代码都是C语言。下面是关于这一主题的讨论:关于在正式C语言中转换为
char*
的必要性,您是正确的。然而,OP可能使用了大量扩展的编译器(如GCC),它支持
void*
指针上的指针算法。他的代码已经运行了,他没有问题。
+-----+--------------------------------------------+
| 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        |
+-----+--------------------------------------------+