12

我的 XCode(默认编译器应该是 clang?)在此代码上向我显示警告:

Incompatible pointer types passing 'char *(*)[2]' to parameter of type 'char ***'(调用函数时)

void func (char ***arrayref, register size_t size) {
    /// ...
}

int main()
{
    char *array[2] = {"string", "value"};
    func(&array, 2);
}

虽然这段代码没有问题(=没有警告):

void func (char **arrayref, register size_t size) {
    /// ...
}

int main()
{
    char *array[2] = {"string", "value"};
    func(array, 2);
}

虽然这消除了警告

func((char***)&array, 2);

我仍然不知道为什么第一个发出警告,而后者没有。

此外,当第一个出现问题时,我还希望第一个发出警告,例如:

Incompatible pointer types passing 'char *[2]' to parameter of type 'char **'

4

4 回答 4

15

是时候简要回顾一下数组语义了。

除非它是sizeof或一元运算符的操作数&,或者是用于在声明中初始化另一个数组的字符串文字,否则“N 元素数组T”类型的表达式将被转换(“衰减”)为键入“pointer to T”,表达式的值将是数组中第一个元素的地址。

代码中的表达式array类型为“2 元素数组char *”,或char *[2]. 当您将它作为参数传递给 时func,如

func( array, 2 );

表达式被转换为类型为“pointer to char *”的表达式,或者char **,这是您的函数所期望的类型,表达式的值是第一个元素的地址:array == &array[0]。这就是您没有收到第二个代码片段警告的原因。

在第一个片段中,数组是一元运算&符的操作数,因此不会发生指针类型的转换;相反,表达式的类型是“指向2 元素数组的 char *指针”,或者char *(*)[2],这与char **. 地址相同(数组第一个元素的地址与数组本身的地址相同),但类型不匹配,因此出现警告。

那么这有什么关系呢?指针只是一个地址,所有地址都相同,对吧?嗯,不。指针是地址的抽象,具有相关的类型语义。指向不同类型的指针不必具有相同的大小或表示形式,并且指针算法取决于所指向类型的类型。

例如,如果我将指针声明为char **p;,则表达式p++将指针前进以指向类型的下一个对象char *,或sizeof (char *)从当前地址开始的字节。但是,如果p声明为char *(*p)[2],则表达式p++将前进p以指向 的下一个二元素数组,该数组距当前地址 char *2 *个字节。sizeof (char *)

于 2013-09-06T17:18:59.930 回答
6
char *array[2] = {"string", "value"};

是一个包含 2 个元素的数组char *

使用array作为地址会导致指向第一个元素的指针,即 type char **

使用&arrayresults 指向相同位置的指针,但类型char *(*)[2](不确定拼写是否正确)。

这与 achar ***不同 - 内存中的表示完全不同。

为了更详细,

+++++++++++++++++++++++
+ array[0] + array[1] +
+++++++++++++++++++++++

这是数组。

char ** p1 = array; // is a pointer to the first element, which in turn is a pointer.
char *(*p2)[2] = &array; // is a pointer to the whole array. Same address, but different type, i. e. sizeof(*p1) != sizeof(*p2) and other differences.

char ***p3 = &p1; // Now, p1 is a different pointer variable which has an address itself which has type `char ***`.
于 2013-09-06T16:50:18.763 回答
3

这是一个如何做你想做的事并改变数组指向的例子:

char *array2[] = {"string", "NewValue"};

void func0 (char **arrayref, register size_t size) {
    puts(arrayref[1]);
}

void func1 (char ***arrayref, register size_t size) {
    puts(arrayref[0][1]);
    *arrayref= (char **) array2;

}

int main()
{
    char *array[] = {"string", "value"};
    char **foo = array;
    func0(foo, 2);
    func1(&foo,2);
    func0(foo, 2);
}
于 2013-09-06T17:09:47.130 回答
2

你有一个类型的数组,char *[2]即 2 个指向char. 它是一个具有自动存储持续时间的固定大小的数组。您的函数可以对这种数组做的唯一事情是使用它的元素或更改它们(它不能调整它的大小或取消分配它......因此尝试使更改数组成为可能是没有意义的本身 ~> 换句话说:你真的不需要指向这种数组的指针)。

这是一个简单的例子:

void func (char *arrayref[2]) {
    printf("%s", arrayref[1]);
    arrayref[1] = "new value";
}

int main() {    {
    char *array[2] = {"string", "value"};
    func(array);
    printf(" -> %s", array[1]);
    return 0;
}

或者更改func为采用未指定大小的数组,使其可char *[X]用于 any X,而不仅仅是2(在这种情况下,传递数组的大小已经有意义):

void func (char *arrayref[], size_t size) {
    if (size > 1) {
        printf("%s", arrayref[1]);
        arrayref[1] = "new value";
    }
}

通过一种或另一种方式,该程序将输出value -> new value.

如果您需要您的函数能够调整此数组的大小或以其他方式影响数组本身,则应考虑使用动态分配的数组并以char**.

于 2013-09-06T17:39:31.080 回答