【问题标题】:Reference to Array vs reference to array pointer引用数组与引用数组指针
【发布时间】:2015-01-03 13:36:02
【问题描述】:
void check(void* elemAddr){
    char* word = *((char**)elemAddr);
    printf("word is %s\n",word);
}

int main(){
    char array[10] = {'j','o','h','n'};
    char * bla = array;
    check(&bla);
    check(&array);
}

输出:

word is john

RUN FINISHED; Segmentation fault; core dumped;

第一个有效,但第二个无效。我不明白为什么会这样。

【问题讨论】:

  • 我还添加了 '\0' 字符,但这并没有改变任何东西。
  • &array 的类型不是 char**
  • 我看到一个问得很好的问题,为什么投反对票?
  • &array == &array[0]。分享和享受。
  • @BobJarvis 这违反了约束(比较不兼容的类型)

标签: c arrays pointers


【解决方案1】:

问题是,当我们执行&array 时,我们会从char [10] 获得char (*)[10],而不是char **

在进行实验之前,我要强调的是,当我们将数组作为参数传递给函数时,C 实际上将数组转换为指针。大桶数据没有被复制。

因此,int main(int argc, char **argv) 与 C 中的 int main(int argc, char *argv[]) 相同。

这使我们可以使用简单的printf 打印数组的地址。

我们来做实验:

char array[] = "john";
printf("array:  %p\n", array);
printf("&array: %p\n", &array);

// Output:
array:  0x7fff924eaae0
&array: 0x7fff924eaae0

了解了这一点后,让我们深入研究您的代码:

char array[10] = "john";
char *bla = array;
check(&bla);
check(&array);

blachar *&blachar **

但是,arraychar [10]&arraychar (*)[10] 而不是 char **

因此,当您将&array 作为参数传递时,char (*)[10] 在作为参数传递时就像char *,如上所述。

因此**(char **) &bla == 'j'*(char *) &array == 'j'。做一些简单的实验,你就会证明这一点。

你将void *elemAddr 转换为char ** 并尝试尊重它。这仅适用于&bla,因为它是char **&array 将导致段错误,因为“john”在您进行强制转换时被解释为地址。

【讨论】:

  • 正确的表述是,当传递给一个函数时,一个数组(或数组的地址)衰减到一个指针 - 你对一个简单的指针是正确的: -)。被调用的函数接收该指针。
【解决方案2】:

对于check(&bla);,您发送的是pointer to pointer

void check(void* elemAddr){
    char* word = *((char**)elemAddr);  // works fine for pointer to pointer
    printf("word is %s\n",word);
}

这工作正常。

但是,对于check(&array);,您只传递指针

void check(void* elemAddr){
    char* word = *((char**)elemAddr);  // This is not working for pointer 

    char* word = *(char (*)[10])(elemAddr);   // Try this for [check(&array);]

    printf("word is %s\n",word);
}

完整代码--

check(array); 的代码:

void check(void* elemAddr){
    char* word = *(char (*)[10])(elemAddr);
    printf("word is %s\n",word);
}

int main() {
   char array[10] = {'j','o','h','n'};
    check((char*)array);
  return 0;
}

check(&bla); 的代码:

void check(void* elemAddr){
    char* word = *((char**)elemAddr);
    printf("word is %s\n",word);
}

int main() {
   char array[10] = {'j','o','h','n'};
   char* bla = array;
   check(&bla);
  return 0;
}

【讨论】:

  • char* word = ((char*)elemAddr); // Try this for [check(&array);] 是未定义的行为。
  • @AerofoilKite 2501 声明将 char (*)[10] 转换为 char * 在 C 中是“未定义的行为”。请理解您不能使用 C 编译器来测试未定义的行为(如“未定义”包括“翼型风筝预期”)。另一个编译器可能会产生不同的结果。
  • 我投了反对票,因为在你的回答中,你只是声称一个是这个,另一个是那个,但你没有解释区别。
  • 一个用于指向指针的指针,另一个仅用于指针。我错了吗?请让我清楚不要投票..
  • 传入数组就像check(array) 一样好,但在函数中你应该有char* word = (char*)elemAddr; 或只是char* word = elemAddr;(来自void* 的强制转换在C 中是可选的)。原因:当你传入array 时,它decays 指向一个char* 类型的指针,其值为&array[0]。这就是为什么你需要在函数中转换为char*
【解决方案3】:

C 规范说 array 和 &array 是相同的指针地址。

在将数组传递给函数时使用数组的名称会自动将参数转换为 C 规范中的指针(强调我的)。

6.3.2.1-4

除非它是 sizeof 运算符的操作数或一元 & 运算符,或者是用于初始化数组的字符串文字,an 具有“类型数组”类型的表达式被转换为 类型为“pointer to type”的表达式,指向初始 数组对象的元素 并且不是左值。如果数组对象 有注册存储类,行为未定义。

所以调用 func(array) 会导致一个指向 char[] 的指针被传递给函数。但是在数组上使用 address-of 运算符有一种特殊情况。由于数组具有“类型数组”类型,因此它属于规范的“其他”类别(强调我的)。

6.5.3.2-3

一元 & 运算符产生其操作数的地址。如果操作数 类型为“type”,结果类型为“pointer to type”。如果 操作数是一元 * 运算符的结果,既不是该运算符也不是 & 运算符被评估,结果就像两者都被省略了, 除了对运营商的限制仍然适用并且 结果不是左值。同样,如果操作数是 [] 运算符,既不是 & 运算符,也不是由 [] 被评估,结果就像 & 运算符是 删除并且 [] 运算符更改为 + 运算符。 否则, 结果是指向其指定的对象或函数的指针 操作数

所以调用 func(&array) 仍然会导致将单个指针传递给函数,就像调用 func(array) 一样,因为 array 和 &array 都是相同的指针值。

常识会让你相信 &array 是一个指向数组第一个元素的双指针,因为使用 & 运算符的行为通常是这样的。但是数组不同。因此,当您将传递的数组指针作为指向数组的双指针取消引用时,您会遇到分段错误。

【讨论】:

  • C 规范的答案。谢谢!
【解决方案4】:

这不是对您问题的直接回答,但将来可能对您有所帮助。

数组不是指针:


  • type arr[10]:

    • 使用了sizeof(type)*10字节数

    • arr&arr 的值必然相同

    • arr 指向一个有效的内存地址,但不能设置为指向另一个内存地址


  • type* ptr = arr:

    • 使用了额外数量的sizeof(type*) 字节

    • ptr&ptr 的值通常不同,除非您设置 ptr = (type*)&ptr

    • ptr 可以设置为指向有效和无效的内存地址,次数不限


关于您的问题:&bla != bla == array == &array,因此是&bla != &array

【讨论】:

    【解决方案5】:

    一个问题是您的 char 数组不一定会以空值结尾。由于array 是在堆栈上本地分配的自动变量,因此不能保证将内存清零。因此,即使您正在初始化前 4 个字符,后 6 个字符也未定义。

    然而……

    您的问题的简单答案是&bla != &array,因此您的 check() 函数假设它将在 2 个不同的地址找到以空字符结尾的字符数组。

    下列等式为真:

    array == &array    // while not the same types exactly, these are equivalent pointers
    array == bla
    &array == bla
    *bla == array[0]
    

    &bla 永远不会等于您想要的任何内容,因为该语法引用了本地堆栈上 bla 变量的 地址,并且与它的值(或它指向的内容)无关到)。

    希望对您有所帮助。

    【讨论】:

    • 不是我的 DV,但是当您为数组的某些元素指定初始值设定项时,其余元素将初始化为 0。此外,您的等式(例如 array == &array)可能会产生误导,因为它们是不同的类型。
    • 啊,关于数组初始化,你是对的——我一直忽略了这一点,为了清楚起见,我总是进行显式初始化。谢谢提醒。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多