【问题标题】:Setting Variables in a C struct just like in a C# class constructor就像在 C# 类构造函数中一样在 C 结构中设置变量
【发布时间】:2017-07-14 22:15:32
【问题描述】:

在 C# 中,在类中,我们可以在类型的构造函数中设置变量,如下所示:

class ComplexNumber
{
 public double real {get; set;}
 public double imag {get; set;}
 public double abs {get;}
 public Complex Number(double real, double imag)
 {
  this.real = real;
  this.imag = imag;
  this.abs = Math.Pow(real*real + imag*imag , 0.5f);
 }
}

(如果 C# 代码有误,我深表歉意,我已经有一段时间没有编写 C#了,我纯粹是为了类比而使用它) 在构造函数期间,'abs' 的值被设置,所以我想知道是否在 C 结构中,即使没有构造函数也可以这样做

typedef struct Comp
{
 double real;
 double imag;
 double abs;
} Comp;

int main(void)
{
 Comp z;
 z.real = 2.0f;
 z.imag = 5.3f;
 printf("%f\n" , z.abs);
}

所以我希望能够在那里运行代码并让它返回 z.abs 的值,而无需实际将其设置为任何值(尽管它在 z.real 和 z.imag 之后都设置为某个值已设置)。这在 C 中是否可行?

编辑:我已按照建议将Default values in a C Struct 视为Default values in a C Struct,在那个问题中,他们似乎想要自己类型的默认值,因此是预设的类型,但就我而言,我希望能够要做的是,每次我设置自定义类型时,它内部的属性之一,'z.abs' 将使用'z.real'和'z.imag'设置,而不是将自定义类型设置为默认值而不是具有默认值,例如:

Comp z;
z.real = 4.0f;
z.imag = 3.0f;

写完这段代码后,z被定义为{ 4.0f, 3.0f, null/undefined},但它的最后一部分,'abs'可以使用前两个作为 sqrt(z.real^2 + z.imag^2) 所以它的值为 5.0f。所以我想在设置前两个数字后,让第三个数字像 C# 中的构造函数一样自动计算,第一个示例。

【问题讨论】:

  • 使用cabs<complex.h>
  • @EdJaras 这个问题是关于硬编码默认值的。在这里,他希望一个成员的默认值成为其他成员实际值的函数。
  • 我认为 C# 示例不能满足您的要求。它在创建结构时初始化abs,但您似乎希望它在分配realimag 时自动更新。
  • 为什么不直接将abs 设为成员函数而不是变量?如果想避免每次都重新计算,可以缓存在成员变量中。

标签: c# c struct


【解决方案1】:

鉴于您的 C# 代码,您拥有的是 readonly 自动属性。你可以像这样编写你的 C# 代码:

class ComplexNumber
{
 public double real;
 public double imag;
 public readonly double abs;
 public ComplexNumber(double real, double imag)
 {
  this.real = real;
  this.imag = imag;
  this.abs = Math.Pow(real*real + imag*imag , 0.5f);
 }
}

暂时忘记这不是 C# 中最好的风格。相关的变化是,你的类现在只有 fields 而不是 properties,你可以在 C 中做一些非常相似的事情:

#include <stdio.h>
#include <math.h>

struct ComplexNumber
{
    double real;
    double imag;
    const double abs;
};

#define ComplexNumber_init(r,i) { \
    .real=(r), .imag=(i), .abs=pow((r)*(r)+(i)*(i), .5) }

int main(void)
{
    struct ComplexNumber cnum = ComplexNumber_init(4.0, 2.0);
    printf("%f\n", cnum.abs);
}

这里的关键组成部分是一个 struct 成员上的 const 限定符。有了这个,您可以在初始化时设置值,以后再也不可以了。效果与C#的readonly相当。 C# 也有const,但使用readonly,您可以在构造函数中额外设置值。


如果您想要与原始 C# 代码等效的代码,则必须注意 C# 中的 properties 始终转换为方法 -- getter 和 setter。在您的示例中,您使用的是 自动属性,这意味着,由于您没有为 getter 和 setter 指定函数体,因此会自动为您创建默认主体,只需访问私有字段.

C 中没有这样的东西,但是您可以手动创建相同的东西,下面是一个示例。

请注意,我稍微更改了语义,因为在不更新 abs 的情况下让 realimag 可以更改,就像您的 C# 代码中的情况一样,这可能不是最明智的做法。

另外请注意,对于这个小例子来说,这完全是矫枉过正,我只是添加它来展示如何在 C 中编写“类”的可能性。

compnum.h:

#ifndef COMPNUM_H
#define COMPNUM_H

typedef struct ComplexNumber ComplexNumber;

// "constructor":

ComplexNumber *ComplexNumber_create(double real, double imag);

// getters and setters:

double ComplexNumber_real(const ComplexNumber *self);
void ComplexNumber_setReal(ComplexNumber *self, double real);

double ComplexNumber_imag(const ComplexNumber *self);
void ComplexNumber_setImag(ComplexNumber *self, double imag);

double ComplexNumber_abs(const ComplexNumber *self);

// "destructor":

void ComplexNumber_destroy(ComplexNumber *self);

#endif

compnum.c:

#include <math.h>
#include <stdlib.h>

#include "compnum.h"

// the struct itself is completed here and not in compnum.h -- this way, its
// members are *really* "private". They can't be seen by other translation
// units just including compnum.h.
struct ComplexNumber
{
    double real;
    double imag;
    // don't need abs here, it's calculated
};

ComplexNumber *ComplexNumber_create(double real, double imag)
{
    ComplexNumber *self = malloc(sizeof(*self));
    if (!self) return 0;
    self->real = real;
    self->imag = imag;
    return self;
}

double ComplexNumber_real(const ComplexNumber *self)
{
    return self->real;
}

void CompexNumber_setReal(ComplexNumber *self, double real)
{
    self->real = real;
}

double ComplexNumber_imag(const ComplexNumber *self)
{
    return self->imag;
}

void ComplexNumber_setImag(ComplexNumber *self, double imag)
{
    self->imag = imag;
}

double ComplexNumber_abs(const ComplexNumber *self)
{
    return pow(self->real*self->real + self->imag*self->imag, .5);
}

void ComplexNumber_destroy(ComplexNumber *self)
{
    free(self);
}

ma​​in.c:

#include <stdio.h>

#include "compnum.h"

int main(void)
{
    ComplexNumber *cnum = ComplexNumber_create(4.0, 2.0);
    printf("%f\n", ComplexNumber_abs(cnum));
    ComplexNumber_destroy(cnum);
}

【讨论】:

  • abs 是一个常量,除了不能设置它之外,abs 是一个常量还有其他区别吗?例如更高/更低的内存分配/处理时间或任何其他将其保留为变量但仍在使用#define ComplexNumber_init 在创建时定义abs 的因素?此外,使用宏#define 或将 ComplexNumber_init 设为 David 所回答的函数之间有什么区别吗?
  • @FilipeRodrigues const 确保它只能在初始化时设置,因此无法使用函数进行设置。这是最接近原始 C# 代码的方法。我仍在进行编辑,向您展示使用 OOP 来解决此问题的方法。
  • 我在这里考虑了一个宏,但似乎捕获的机会较少,例如,以这种方式溢出错误。 constness 可能会在某些用途中弥补它....
  • @FilipeRodrigues 忘记这一点,如果您的函数返回 struct 本身,当然这也可以使用函数。虽然它有一点开销,但它需要将结构从函数复制到调用者。
  • 因此,通过使用#define 来创建函数,我放弃了捕获错误(例如溢出)的可能性,从而可能略微提高性能。我想我可以在不同的场景中使用这两种方法,所以我会记住这两种方法。
【解决方案2】:

如果您没有在结构中显式设置字段,则其内容是未指定的。

您可以按如下方式初始化结构的值:

Comp z = { 0.0, 0.0, 0.0 };

【讨论】:

  • 我知道它们是未指定的,但由于第三个值“abs”完全取决于前 2 个并且不应该被编辑,我想知道是否只设置前 2 个值,“真实”和 'imag' 可以告诉结构 'abs' 应该等于 sqrt(real^2 + imag^2) 而不必正式编写 z.abs = pow(pow(z.real,2)+ pow(z.imag,2),0.5f);因为当涉及到很多变量时它会变得非常混乱
  • @FilipeRodrigues 不,不是那样的。您需要创建一个函数,给定实部和虚部将同时设置“abs”。
  • 您可以像这样简单地将所有字段设置为零:Comp z = {0};
【解决方案3】:

如果你想将一个新的struct“初始化”为一些预先确定的值,其中一些必须从其他成员的值中计算出来,你可以使用一个返回struct的函数:

#include <stdio.h>
#include <math.h>

typedef struct Comp
{
 double real;
 double imag;
 double abs;
} Comp;

Comp new_Comp(double re, double im);

int main(void)
{
    Comp my_comp = new_Comp(3.0, 4.0);

    printf("my_comp.real = %f, my_comp.imag = %f, my_comp.abs = %f\n",
           my_comp.real, my_comp.imag, my_comp.abs);

    return 0;
}

Comp new_Comp(double re, double im)
{
    Comp new = { .real = re, .imag = im };
    new.abs = hypot(re, im);

    return new;
}

【讨论】:

  • 这似乎工作得很好,它减少了每次创建新变量时必须设置“z.abs”或每次必须计算“abs(z)”的处理能力我需要它,谢谢。
  • @FilipeRodrigues-- 关于返回 sqrt(x**2 + y**2)hypot() 函数的一件好事是,它保证不会在计算中溢出,但如果结果太大可能会设置范围错误。
猜你喜欢
  • 2023-03-24
  • 2011-07-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-03
  • 2011-11-03
  • 2010-11-10
相关资源
最近更新 更多