2014-06-26 86 views
0

说我希望使用void**作为我的类型,在C.然后,而不是每次都重新写入指针算术逻辑动态地分配一个数组,可以保存任何数据,即,我要简单的函数,返回一个元素的地址。为什么我不能在赋值中使用这个函数(即,我可以设置,以及获取元素的值)?使用函数返回指针作为左值

下面是一些示例代码:

#include <stdio.h> 
#include <stdlib.h> 

void printIntArray(void** array, size_t length) { 
    printf("Array at %p\n", array); 
    while (length--) { 
    printf(" [%zu] at %p -> %p", length, array + length, *(array + length)); 
    if (*(array + length)) { 
     printf(" -> %d", *(int*)*(array + length)); 
    } 
    printf("\n"); 
    } 
} 

void* getElement(void** array, size_t index) { 
    return *(array + index); 
} 

int main(int argc, char** argv) { 
    const size_t n = 5; 
    size_t i; 

    /* n element array */ 
    void** test = malloc(sizeof(void*) * n); 
    i = n; 
    while (i--) { 
    *(test + i) = NULL; 
    } 

    /* Set element [1] */ 
    int testData = 123; 
    printf("testData at %p -> %d\n", &testData, testData); 
    *(test + 1) = (void*)&testData; 

    printIntArray(test, n); 

    /* Prints 123, as expected */ 
    printf("Array[1] = %d\n", *(int*)getElement(test, 1)); 

    /* Something new in [2] */ 
    /* FIXME lvalue required as left operand of assignment */ 
    int testThing = 456; 
    getElement(test, 2) = (void*)&testThing; 
    printIntArray(test, n); 

    return 0; 
} 

这不会是拳头时间这个问题已经被问,但得到的答复通常是沿着“您要的东西分配给一个函数中的行,而不是函数的返回值,这是不允许的“。够公平的,但可以解决这个问题吗?我有点困惑!

+1

你为什么想这样做? – nightshade

+0

@nightshade一个简单的例子,为什么你可能想要这样做是在这个stackoverflow的答案之一中提到[是errno线程安全](http://stackoverflow.com/questions/1694164/is-errno-thread-安全的),它有一个'errno'实现,由于使用'errno'是一个使用函数的线程安全的线程,该函数返回一个指向特定线程'errno'内存的int指针。 –

+0

@nightshade就像一个练习,我正在编写一个动态数组库,并进行边界检查。上面的示例代码被简化以说明问题。 – Xophmeister

回答

2

实现getElement()这样的:

void ** getElement(void ** ppv, size_t index) { 
    return ppv + index; 
} 

并使用它像这样:

*getElement(test, 2) = &testThing; 

如果要使用宏,你甚至可以去你想要的方式:

#define GET_ELEMENT(ppv, index) \ 
    (*(ppv + index)) 

使用方法如下:

GET_ELEMENT(test, 2) = &testThing; 
0

C使用指针什么其他语言呼叫参考...这是你正在尝试做一个例子:

typedef struct 
{ 
    int x; 
} st; 

st f() 
{ 
    st p; 
    p.x = 20; 
    return p; 
} 

int main() 
{ 
    f().x = 9; 
} 

你从函数返回一个值...你在使用它之前需要将该值分配给某些东西。是的,一个指针遵循相同的规则,所不同的是,它的价值是什么地址...所以你必须给你的函数的一个空隙返回指定*变量,你想要做这个变量是什么。

在我的例子,使它工作,你需要这样的:

int main() 
{ 
    st v = f(); 
    v.x = 9; 
} 

同样的逻辑也适用于您的指针

1
getElement(test, 2) = (void*)&testThing; 

这永远不会工作。你不能给一个函数赋值。使用此:

test[2] = (void*)&testThing; 

注意getElement(test, 2)回报NULL,它不是一个指向你的阵列test的元素索引2,这是它的价值(这也是一个指针,而不是你需要的那个)。不管你用getElement(test, 2)为您提供的信息做什么,你不能用它来改变test[2]

假设呼叫者无法访问test的情况,您必须编写setElement()函数。

void setElement(void** array, size_t index, void* value) { 
    array[index] = value; 
} 
... 
setElement(test, 2, (void*)&testThing); 
2

分配给一个空不允许,所以你需要将空指针转换为其他类型的指针用于任何种类的分配。

换句话说,

void *vPtr = malloc(35); 

*((int *)vPtr) = 5; // legal since cast void * to int * 
*vPtr = 5;   // illegal since size of the memory location pointed to is unknown. 

相同的原理适用于一个返回空指针的函数。

你只需要投函数返回一个类型,允许转让的指针。

,编译在Visual Studio 2005是一个简单的例子。

void *xPtr (int *pp) 
{ 
    return pp; 
} 

void jj() 
{ 
    int jjj; 
    *((int *)xPtr(&jjj)) = 6; 
} 

哪里,如果你改变功能jj()

void jj() 
{ 
    int jjj; 
    *(xPtr(&jjj)) = 6; 
} 

你会看到编译错误,如

1>c:\fileobject.c(191): error C2100: illegal indirection 
1>c:\fileobject.c(191): warning C4047: '=' : 'void *' differs in levels of indirection from 'int' 
1>c:\fileobject.c(191): error C2106: '=' : left operand must be l-value 
+0

OP指的是'void **'而不是'void *'。可以很容易地解除引用,然后得到适合的东西,即另一个'void *'。 – alk