从C中的函数返回'struct'
今天我在教几个朋友如何使用C从C中的函数返回'struct',c,C,今天我在教几个朋友如何使用Cstructs。其中一位问您是否可以从函数返回struct,我回答说:“不!您应该返回指向动态mallocedstructs的指针。” 来自主要从事C++的人,我预期不能通过值返回结构> /Calp>S。在C++中,可以为对象超载运算符=,并使其具有完全意义上的功能,以按值返回对象。然而,在C语言中,您没有这个选项,因此它让我思考编译器实际上在做什么。考虑以下事项: struct MyObj{ double x, y; }; struct MyObj foo
struct
s。其中一位问您是否可以从函数返回struct
,我回答说:“不!您应该返回指向动态malloc
edstruct
s的指针。”
来自主要从事C++的人,我预期不能通过值返回<代码>结构> /Calp>S。在C++中,可以为对象超载<代码>运算符=<代码>,并使其具有完全意义上的功能,以按值返回对象。然而,在C语言中,您没有这个选项,因此它让我思考编译器实际上在做什么。考虑以下事项:
struct MyObj{
double x, y;
};
struct MyObj foo(){
struct MyObj a;
a.x = 10;
a.y = 10;
return a;
}
int main () {
struct MyObj a;
a = foo(); // This DOES work
struct b = a; // This does not work
return 0;
}
我理解为什么
structb=a代码>不应工作--不能为数据类型重载运算符=
。怎么会是a=foo()代码>编译好吗?它是否意味着除了struct b=a代码>?可能要问的问题是:语句与=
符号一起返回的具体功能是什么?您可以从函数返回结构(或使用=
操作符),而不会出现任何问题。它是语言中定义明确的一部分。struct b=a的唯一问题是没有提供完整的类型<代码>结构MyObj b=a将正常工作。您也可以将结构传递给函数-结构与任何内置类型完全相同,用于参数传递、返回值和赋值
下面是一个简单的演示程序,它执行所有三个操作:将结构作为参数传递,从函数返回结构,并在赋值语句中使用结构:
#include <stdio.h>
struct a {
int i;
};
struct a f(struct a x)
{
struct a r = x;
return r;
}
int main(void)
{
struct a x = { 12 };
struct a y = f(x);
printf("%d\n", y.i);
return 0;
}
进行调用时,例如a=foo()编译器可能会将结果结构的地址推送到堆栈上,并将其作为指向foo()
函数的“隐藏”指针传递。实际上,它可能会变成:
void foo(MyObj *r) {
struct MyObj a;
// ...
*r = a;
}
foo(&a);
然而,这种方法的具体实现取决于编译器和/或平台。正如Carl Norum所指出的,如果结构足够小,它甚至可以在寄存器中完全传回。您可以在C中分配结构。a=b
是有效的语法
您只是在行中省略了部分类型--struct标记--不起作用。struct b
行不起作用,因为它是一个语法错误。如果您将其展开以包含类型,它将正常工作
struct MyObj b = a; // Runs fine
C在这里所做的基本上是从源结构到目标结构的memcpy
。对于struct
值的赋值和返回(以及C中的其他所有值)就我所记得的,C的第一个版本只允许返回一个
可以装入处理器寄存器,这意味着您只能返回指向的指针
结构。同样的限制也适用于函数参数
较新的版本允许传递更大的数据对象,如结构。
我认为这一特点在80年代或90年代初已经很普遍
但是,数组仍然只能作为指针传递和返回。是的,我们也可以传递结构和返回结构。您是对的,但实际上没有传递数据类型,该数据类型应该类似于struct MyObj b=a
实际上,当我试图找到一个更好的解决方案,在不使用指针或全局变量的情况下为函数返回多个值时,我也知道了这一点。
下面是同样的例子,它计算了一个学生平均分的偏差
#include<stdio.h>
struct marks{
int maths;
int physics;
int chem;
};
struct marks deviation(struct marks student1 , struct marks student2 );
int main(){
struct marks student;
student.maths= 87;
student.chem = 67;
student.physics=96;
struct marks avg;
avg.maths= 55;
avg.chem = 45;
avg.physics=34;
//struct marks dev;
struct marks dev= deviation(student, avg );
printf("%d %d %d" ,dev.maths,dev.chem,dev.physics);
return 0;
}
struct marks deviation(struct marks student , struct marks student2 ){
struct marks dev;
dev.maths = student.maths-student2.maths;
dev.chem = student.chem-student2.chem;
dev.physics = student.physics-student2.physics;
return dev;
}
#包括
结构标记{
智力数学;
国际物理学;
国际化学;
};
结构分数偏差(结构分数学生1,结构分数学生2);
int main(){
学生成绩结构;
学生:数学=87;
student.chem=67;
学生:物理=96;
结构标记平均值;
平均数学=55;
平均化学=45;
平均物理=34;
//结构标记开发;
结构分数偏差=偏差(学生,平均);
printf(“%d%d%d”,发展数学,发展化学,发展物理);
返回0;
}
结构分数偏差(结构分数学生,结构分数学生2){
结构标记开发;
dev.math=student.mathematics-student2.mathematics;
dev.chem=student.chem-student2.chem;
dev.physics=student.physics-student2.physics;
返回dev;
}
传回结构没有问题。它将按值传递
但是,如果结构包含任何具有局部变量地址的成员,该怎么办
struct emp {
int id;
char *name;
};
struct emp get() {
char *name = "John";
struct emp e1 = {100, name};
return (e1);
}
int main() {
struct emp e2 = get();
printf("%s\n", e2.name);
}
现在,这里的e1.name
包含函数get()
的本地内存地址。
一旦get()
返回,name的本地地址就会被释放。
因此,在调用方中,如果我们尝试访问该地址,可能会导致分段错误,因为我们正在尝试释放地址。那太糟糕了
其中,e1.id
将完全有效,因为其值将复制到e2.id
因此,我们应该始终避免返回函数的本地内存地址
任何malloced都可以在需要时归还
struct emp {
int id;
char *name;
};
struct emp get() {
char *name = "John";
struct emp e1 = {100, name};
return (e1);
}
int main() {
struct emp e2 = get();
printf("%s\n", e2.name);
}
适用于较新版本的编译器。
与id一样,名称的内容也会被复制到指定的结构变量。struct var e2 address作为arg推送到被调用方堆栈,并在那里指定值。事实上,get()在eax reg中返回e2的地址。这类似于引用调用。struct b=a代码>是一个语法错误。如果您尝试struct MyObj b=a代码>?@GregHewgill:你完全正确。然而,非常有趣的是,struct MyObj b=a代码>似乎可以工作:)这完全取决于实现。例如,armcc将在常规参数传递(或返回值)寄存器中传递足够小的结构。这不是返回一个指向局部变量的指针吗?返回结构的内存不能是foo的一部分
struct emp {
int id;
char *name;
};
struct emp get() {
char *name = "John";
struct emp e1 = {100, name};
return (e1);
}
int main() {
struct emp e2 = get();
printf("%s\n", e2.name);
}