C 使用函数返回指针作为左值
假设我想动态分配一个可以保存任何数据的数组,即使用C语言中的C 使用函数返回指针作为左值,c,pointers,void-pointers,lvalue,C,Pointers,Void Pointers,Lvalue,假设我想动态分配一个可以保存任何数据的数组,即使用C语言中的void**作为我的类型。然后,我想要一个返回元素地址的简单函数,而不是每次重新写入指针算术逻辑。为什么我不能在赋值中使用此函数(即,这样我可以设置并获取元素的值) 下面是一些示例代码: #include <stdio.h> #include <stdlib.h> void printIntArray(void** array, size_t length) { printf("Array at %p\n"
void**
作为我的类型。然后,我想要一个返回元素地址的简单函数,而不是每次重新写入指针算术逻辑。为什么我不能在赋值中使用此函数(即,这样我可以设置并获取元素的值)
下面是一些示例代码:
#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;
}
#包括
#包括
无效打印阵列(无效**阵列,大小\u t长度){
printf(“位于%p\n的数组”,数组);
while(长度--){
printf(“[%zu]位于%p->%p”,长度,数组+长度,*(数组+长度));
如果(*(数组+长度)){
printf(“->%d”,*(int*)*(数组+长度));
}
printf(“\n”);
}
}
void*getElement(void**数组,大小\u t索引){
返回*(数组+索引);
}
int main(int argc,字符**argv){
常数大小n=5;
尺寸i;
/*n元阵列*/
void**test=malloc(sizeof(void*)*n);
i=n;
而(我--){
*(测试+i)=空;
}
/*集合元素[1]*/
int testData=123;
printf(“位于%p->%d\n的testData,&testData,testData);
*(测试+1)=(无效*)和测试数据;
打印阵列(测试,n);
/*按预期打印123*/
printf(“数组[1]=%d\n”,*(int*)getElement(test,1));
/*[2]中的新事物*/
/*作为赋值的左操作数所需的FIXME左值*/
int testThing=456;
getElement(test,2)=(void*)&testThing;
打印阵列(测试,n);
返回0;
}
这不是第一次问这个问题,但答案通常是“您试图给函数赋值,而不是函数的返回值,这是不允许的”。这很公平,但我们能绕开它吗?我有点困惑 C使用指向其他语言称之为引用的伪的指针。。。以下是您正在尝试做的一个示例:
typedef struct
{
int x;
} st;
st f()
{
st p;
p.x = 20;
return p;
}
int main()
{
f().x = 9;
}
您正在从函数返回一个值。。。在使用之前,您需要将该值分配给某个对象。是的,指针遵循相同的规则,区别在于它的值是某个对象的地址。。。因此,您必须将函数的返回赋值给void*变量,以及对该变量执行的操作
在我的示例中,要使其工作,您需要:
int main()
{
st v = f();
v.x = 9;
}
同样的逻辑也适用于指针C使用指向伪的指针,其他语言称之为引用。。。以下是您正在尝试做的一个示例:
typedef struct
{
int x;
} st;
st f()
{
st p;
p.x = 20;
return p;
}
int main()
{
f().x = 9;
}
getElement(test, 2) = (void*)&testThing;
您正在从函数返回一个值。。。在使用之前,您需要将该值分配给某个对象。是的,指针遵循相同的规则,区别在于它的值是某个对象的地址。。。因此,您必须将函数的返回赋值给void*变量,以及对该变量执行的操作
在我的示例中,要使其工作,您需要:
int main()
{
st v = f();
v.x = 9;
}
同样的逻辑也适用于指针
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);
这是行不通的。不能为函数赋值。使用以下命令:
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);
不允许向void赋值,因此对于任何类型的赋值,都需要将void指针强制转换为其他类型的指针 换句话说,
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.
同样的原理也适用于返回空指针的函数
您只需将函数返回的指针强制转换为允许赋值的类型
在VisualStudio2005中编译的一个简单示例是
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
不允许向void赋值,因此对于任何类型的赋值,都需要将void指针强制转换为其他类型的指针 换句话说,
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.
同样的原理也适用于返回空指针的函数
您只需将函数返回的指针强制转换为允许赋值的类型
在VisualStudio2005中编译的一个简单示例是
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
实现getElement()
如下:
void ** getElement(void ** ppv, size_t index) {
return ppv + index;
}
*getElement(test, 2) = &testThing;
GET_ELEMENT(test, 2) = &testThing;
然后像这样使用它:
void ** getElement(void ** ppv, size_t index) {
return ppv + index;
}
*getElement(test, 2) = &testThing;
GET_ELEMENT(test, 2) = &testThing;
如果使用宏,您甚至可以按照预期的方式:
#define GET_ELEMENT(ppv, index) \
(*(ppv + index))
像这样使用它:
void ** getElement(void ** ppv, size_t index) {
return ppv + index;
}
*getElement(test, 2) = &testThing;
GET_ELEMENT(test, 2) = &testThing;
实现getElement()
如下:
void ** getElement(void ** ppv, size_t index) {
return ppv + index;
}
*getElement(test, 2) = &testThing;
GET_ELEMENT(test, 2) = &testThing;
然后像这样使用它:
void ** getElement(void ** ppv, size_t index) {
return ppv + index;
}
*getElement(test, 2) = &testThing;
GET_ELEMENT(test, 2) = &testThing;
如果使用宏,您甚至可以按照预期的方式:
#define GET_ELEMENT(ppv, index) \
(*(ppv + index))
像这样使用它:
void ** getElement(void ** ppv, size_t index) {
return ppv + index;
}
*getElement(test, 2) = &testThing;
GET_ELEMENT(test, 2) = &testThing;
为什么要这样做?@nightshade在这个stackoverflow的一个答案中提到了一个简单的例子,它有一个
errno
实现,是线程安全的,因为errno
是一个宏,它使用一个函数返回一个指向特定线程的int指针errno
memory.@nightshade作为练习,我正在编写一个带有边界检查的动态数组库。T