構造体の何が特別なのですか?


81

Cでは、関数から配列を返すことはできませんが、配列へのポインターを返すことができます。しかしstructs、配列が含まれている場合でも、関数によって返されるようにする特別な点は何ですか。

structラッピングによって次のプログラムが有効になるのはなぜですか?

#include <stdio.h>

struct data {
    char buf[256];
};

struct data Foo(const char *buf);

int main(void)
{
    struct data obj;
    obj = Foo("This is a sentence.");
    printf("%s\n", obj.buf);
    return 0;
}

struct data Foo(const char *buf)
{
    struct data X;
    strcpy(X.buf, buf);
    return X;
}

1
で同じことを行うことができますunion。組合の何が特別なのですか?
user253751 2016

19
配列はC.にとても奇妙である理由あなたは、むしろ問うべき
CodesInChaos

構造体を返すときに、構造体がいくつかのレジスタに収まらない場合、コンパイラによって割り当てられた「隠し」メモリ、構造体は(memcpy()を介して)隠しメモリにコピーされ、次に(memcpy(を介して)再びコピーされます。 ))呼び出し元の構造体変数。その「隠された」メモリは他のすべての機能に失われます。memcpy()'hidden'メモリへの2つの余分な呼び出しと損失は、構造体passed toreturned from関数であってはならない主な理由です。最良のポリシーは、構造体へのポインターを渡すことです。
user3629249 2016

3つの答えのいずれも、構造体の受け渡しに対処していません(むしろ、配列の受け渡しについてのみ説明しています)が、質問には答えていません。
user3629249 2016

1
@ user3629249-質問は理解の欠如を前提としているため、質問に答えることはできません。質問に答える唯一の方法は、質問をすることができない理由を説明しようとすることです。「なぜ青が赤と同じ色なのか」と尋ねたら想像してみてください。質問に答えられない理由をすぐに説明できます。
ホーガン

回答:


100

同じ質問をするより良い方法は、「配列の何が特別なのか」です。これは、配列ではなく、特別な処理が付加されている配列だからstructです。

ポインタによる配列の受け渡しと戻りの動作は、Cの元の実装にまでさかのぼります。配列はポインタに「減衰」し、特に言語に不慣れな人々の間でかなりの混乱を引き起こします。一方、構造体は、ints、doublesなどの組み込み型と同じように動作します。これには、コピーされない柔軟な配列メンバーstructを除いて、に埋め込まれたすべての配列が含まれます。


4
確かに「かなりの混乱を引き起こしている」。「x」と「&x」が同じ値/アドレスであるのは非常識です。初心者が間接参照を間違えるのは当然のことです:(
Martin James

1
私の記憶は私をだましているかもしれませんがstruct、値でsを渡すことができなかった時はありませんでしたか?
アルク

5
@alk構造体が最初に言語に追加されたとき、最初は関数への構造体の受け渡しにいくつかの制限があったと思いますが、これらはコンパイラーの欠陥として明確にマークされ、すぐに修正されました。それらを渡し、返却したいのに何か問題があったこと。
スティーブサミット

8
@jamesqf :struct Point {short x, y, z;};。あなたは本当にポインタを使ってそれらを動かしたいですか?あなたは確かにこの方法でスペースを節約していません。
マークVY 2016

6
@jamesqfこれが応答に値するかどうかはわかりません。Cがアセンブリにすぎないと信じている場合、ポインタを使用しない理由がないと信じている場合は、構造体を渡すことは役に立たないと考えるかもしれません。しかし、Cを高水準言語として扱い(HLLのように低水準言語ではありますが)、Cの型システムを一般的なものとして扱う残りの人にとっては(配列の第2クラスのステータスを除いて) )、なぜ構造体を渡したり返したりしたくないのですか?(BTW、IIRC、K&R1によると、構造体の受け渡しが進行中であり、本が出版されるまでにV7 ccで機能していました。)
Steve Summit

38

まず第一に、引用するためにC11、章§6.8.6.4、returnステートメント、(強調鉱山

return式を含むステートメントが実行されると、式の値が関数呼び出し式の値として呼び出し元に返されます。

構造体のが返されるため、構造体の変数を返すことは可能です(そして正しいです)。これは、プリミティブデータ型を返すのと似ています(intたとえば、を返す)。

一方、を使用して配列を返す場合、配列NOTEの最初の要素のアドレスがreturn <array_name>基本的に返されます。これは、配列が呼び出された関数に対してローカルである場合、呼び出し元では無効になります。したがって、そのように配列を返すことはできません。

だから、TL; DRは、何もあり、特別なstructsが、専門はである配列


注意:

C11もう一度引用すると、§6.3.2.1章(私の強調

sizeof演算子、_Alignof演算子、または単項演算&子のオペランドである場合、または配列を初期化するために使用される文字列リテラルである場合を除き、の配列」タイプの式は、「」式に変換されます。配列オブジェクトの最初の要素を指し、左辺値ではない''型へのポインタ。[...]


OTOHとは正確には何ですか?!

1
@Suklこれは「一方」の略語です:)
Sourav Ghosh

1
@Suklこれらの略語は、インターネット自体とほぼ同じくらい古いと思います。それらは確かにUsenetの栄光の時代に多く使用され、ほとんどのフォーラムで今でも生き残っています。ありがたいことに、気づいていない場合でも、Googleはそれらをデコードできます;-)そして今日頻繁に使用されているのはほんの一握りです(最も一般的なもの)
chi

@chiはい。グーグルはそれらをデコードすることができる非常に素晴らしいです。

1
@Sukl:頭字語を追跡するのにAcronymFinderが役立つかもしれません。
ジョナサンレフラー

11

structタイプについて特別なことは何もありません。配列型には、関数から直接返されるのを防ぐ特別なものがあるということです。

struct発現は、他の非配列型の式のように扱われます。のに評価されますstruct。だからあなたは次のようなことをすることができます

struct foo { ... };

struct foo func( void )
{
  struct foo someFoo;
  ...
  return someFoo;
}

式はオブジェクトsomeFooに評価されstruct fooます。オブジェクトの内容は関数から返されます(それらの内容に配列が含まれている場合でも)。

配列式の扱いは異なります。それがsizeof単項演算&子または単項演算子のオペランドではない場合、または宣言内の別の配列を初期化するために使用されている文字列リテラルでない場合、式は「配列T」から「ポインタ」へと変換(「減衰」)されますT。 、式の値は最初の要素のアドレスです。

したがって、配列式への参照は自動的にポインタ値に変換されるため、関数から値で配列を返すことはできません。


-5

構造体にはデフォルトでデータメンバーが公開されているため、構造体の場合はメインのデータにアクセスできますが、クラスの場合はできません。したがって、構造体の折り返しは有効です。


1
質問がCについてであるのを見たことがありますか?Cには、public/のprivate区別はなく、classesもありません。問題はstruct、配列の値を返すためにCでaが必要な理由についてです。
PJTraill 2016年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.