ここから質問投稿すると、最大4000ポイント当たる!!!! >>

C#で構造体の配列を持った構造体を使いたいのですが
Cならば
struct xyz {
struct abc _abc[32];
int index;
};
struct abc {
int a;
int b;
int c;
};

struct xyz _xyz[8];
xyz[0]._abc[3].b = 1;

のような使い方で という感じで やっていた事を C#で 同じような事をやろうとしても うまくいきません
うまくやる方法をどなたかご存知ないでしょうか

Visual Studio 2005行った場合
コンパイルで
構文エラーです。不適切な配列の宣言子です。マネージ配列を宣言するには、次元指定子を変数の識別子の前に指定します。固定サイズ バッファ フィールドを宣言するには、フィールド型の前に fixed キーワードを使用します
となり

fixed をつけると

固定サイズ バッファの型は次のうちの 1 つでなければなりません: bool、byte、short、int、long、char、sbyte、ushort、uint、ulong、float または double

となってしまいます

このQ&Aに関連する最新のQ&A

A 回答 (4件)

C# では、配列は「単なる連続したメモリ領域」ではなくて「添字によってオブジェクトを格納できるオブジェクト」であることに注意しなくてはいけません。

つまり、C では配列は一種の構造体でしたが、C# では配列は参照型のオブジェクトです。
よって、C のように予めサイズを固定しておくということは基本的にできません。配列の大きさは配列のインスタンスが作られるときに動的に決まります。

C# では、参照型のオブジェクトを構造体のメンバにすることはあまりありません。null 値の扱いが面倒だからです。
また、C# では構造体の大きさは大きくとも 20 バイト程度までにします。C# では基本的に「構造体へのポインタ」はありません。巨大な構造体をそのまま扱うのはメモリの使い方の観点からいって非効率的です。

今回の件では、構造体ではなくクラスにするのがよいかと思われます。

この回答への補足

回答ありがとうございます
クラスを用いるようにした場合
クラスがクラスの配列を持つようにしたいわけですが
この場合もやはり 予めサイズを固定できず インスタンスを動的に生成するようにするわけですよね?
クラスにした方が良いのは回答の後半部の理由があるという事でよろしいのでしょうか?
重ねて質問申し訳ありません

補足日時:2006/05/16 15:06
    • good
    • 0

> この場合もやはり 予めサイズを固定できず インスタンスを動的に生成するようにするわけですよね?


クラスにした方が良いのは回答の後半部の理由があるという事でよろしいのでしょうか?

はい、そういうことです
    • good
    • 0
この回答へのお礼

大変参考になりました
ありがとうございました

お礼日時:2006/05/17 15:04

C#の勉強のつもりで書いてみましたが、あんまり美しくなかったり:



public struct Xyz {
public struct Abc { public int a, b, c; };
public int index; public Abc[] _abc;
};

class Program {
static void Main( string[] args ) {
Xyz[] _xyz = new Xyz[8];
for ( int i = 0; i < _xyz.Length; i++ ) { _xyz[i]._abc = new Xyz.Abc[32]; }
_xyz[0]._abc[3].b = 1;
}
}

C#の構造体は、クラスと違って値型なので、new[]した時点でインスタンス化されるけど、その際、引数なしのコンストラクタを構造体は定義できないので、結局、各要素ごとにインスタンス化しないといけないみたい。
いったん作成したものを捨てるのは忍びないので、代入にしてみてみたけど、中身が露呈しているのがなんとも。
    • good
    • 2
この回答へのお礼

大変 参考になりました
同じことは すんなりとは 出来ないようですね
ありがとうございました

お礼日時:2006/05/16 15:05

構造体を宣言している順番はここに書いてある通りですか?


struct abcはstruct xyzより前で宣言しなければいけませんよ。

このエラーはstruct xyzのメンバーの型であるabcが見つからないと警告しているのではないですか?

この回答への補足

構造体を宣言している順番ですが実際は逆です
このコードは 質問を分かりやすくするために
いいかげん書いたコードで 最後に順序は入れ換えるのを忘れてました
申し訳ありません
今回の問題はそれとは無関係のようです

補足日時:2006/05/16 14:56
    • good
    • 0
この回答へのお礼

例えのコードを きちんとチェックしてませんでした
ご指摘ありがとうございました

お礼日時:2006/05/16 15:02

このQ&Aに関連する人気のQ&A

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!

このQ&Aを見た人はこんなQ&Aも見ています

このQ&Aを見た人が検索しているワード

このQ&Aと関連する良く見られている質問

QC#にて別クラスの関数を使いたい

C#にて、別クラスの関数を使用する方法を教えてほしいです。

下記のような、構造体を受け取るメソッドを作りました。

*****************************
private struct MyPoint
{
public int x;
public int y;
}

private void proc1(MyPoint pt)
{
MessageBox.Show("座標:" ; pt.x + "," + pt.y + "実行結果");
}

private void button1_Click(object sender ,System.EventArgs e)
{

MyPoint pt;
pt.x = 10;
pt.y = 20;
proc(pt);
}
*****************************

別のフォームのクラスから、proc1を呼び出したいのですが、やり方がわかりません。
どうか、教えてください。

Aベストアンサー

同じ定義をしたとしても別の名前空間に書いた構造体は同一とはみなされません。

呼び出し先クラスでの構造体を private では無く、public で宣言して下さい。

呼び出し元では、

MyClass.MyPoint pt;

のようにして実体を作ります。

QC# インスタンスの破棄

C#でインスタンスの破棄を明示的に行いたいのですが、
実際の開発現場では、どのように行っているのでしょうか?

自分で調べると「ガベージコレクタ」が暗黙的に行っているようですが明示的には行わないのが普通なのでしょうか?
もしくは、「Dispose」を使用して明示的に行うのが普通なのでしょうか?

実際に開発されている方からすると簡単な事かもしれませんが教えて頂けると助かります。

以上ですが、よろしくお願いいたします。

Aベストアンサー

結論から言うと、どちらでも良いです。
できれば生成から破棄まで考えて開発できると良いですね。

ガベージコレクションが実行されることで自動開放されますから、
一切.close()や.dispose()を使わなかったとしてもプログラムが
不正終了してしまう事はほとんどありません。

実際の開発では使わなくなったものを使わなくなった時点で
明示的に破棄することが多いです。正確には明示的に破棄するもの、
明示的に破棄しないもの、の2種類に分類しています。

例えばファイルを読み書きするストリーム系のオブジェクトや
データベースとのコネクションなどです。これは開発会社や
開発チーム、案件によって若干違っていて、徹底するところや
適当なところもあります。個人的には徹底したい派ですが。。。

ガベージコレクションが発生するとプログラムの実行動作が
遅くなり、また瞬間的に大きな負担がかかることがあります。
そのため全てをガベージコレクタに任せるのではなく、
明示的に開放できるもの、メモリを大量に消費するものは
その都度、適切に開放していくことで処理効率が良くなります。

プログラミング関連の調べ物で、サンプルソース等を見ることが
あるかと思います。この時サンプルで明示的に開放していたら、
それは明示的に開放したほうが良いもの、と思いましょう。
これだけでもステップアップになりますね。

結論から言うと、どちらでも良いです。
できれば生成から破棄まで考えて開発できると良いですね。

ガベージコレクションが実行されることで自動開放されますから、
一切.close()や.dispose()を使わなかったとしてもプログラムが
不正終了してしまう事はほとんどありません。

実際の開発では使わなくなったものを使わなくなった時点で
明示的に破棄することが多いです。正確には明示的に破棄するもの、
明示的に破棄しないもの、の2種類に分類しています。

例えばファイルを読み書きするストリーム...続きを読む

QC#で共有変数の定義をするには

C#で共有変数の定義をするには、どうしたらよいでしょうか。具体的には、起動パラメータで、任意の文字列(3~4バイト)を渡して、C#アプリケーションを、起動し、プログラムの任意の場所で、そのパラメータを参照したいと考えています。起動パラメータの受け取りは、できるのですが、そのパラメータを、プログラムのどこからも参照できる領域(ここの定義方法が不明)に格納するには、どうしたらよいでしょうか。(2重起動を許す前提です。実行時に1つの目のプログラムにはパラメータ"XYZ"を渡し、2つ目のプログラムを起動時にはパラメータ"ABCD"を渡し、その値により、プログラムの動作を制御したいと思っています)
Windows-xp visual-studio2008 C#でwindowsアプリケーションを作成しています。

Aベストアンサー

namespace test1
{
  static class Program
  {
    /// <summary>
    /// アプリケーションのメイン エントリ ポイントです。
    /// </summary>
    [STAThread]
    static void Main(string[] args)
    {
      if (args.Length > 0)
      {
        strArg = args[0];
      }
      Application.EnableVisualStyles();
      Application.SetCompatibleTextRenderingDefault(false);
      Application.Run(new Form1());
    }
    // ここに記述
    static public string strArg = null;
  }
}

メインクラスのメンバーとしてスコープが有効な場所に記述します

namespace test1
{
  static class Program
  {
    /// <summary>
    /// アプリケーションのメイン エントリ ポイントです。
    /// </summary>
    [STAThread]
    static void Main(string[] args)
    {
      if (args.Length > 0)
      {
        strArg = args[0];
      }
      Application.EnableVisualStyles();
      Application.SetCompatibleTextRenderingDefault(false);
      Application.Run(n...続きを読む

QC#「オブジェクト参照が必要です」(初心者)

Visual C# 2008を学習中なんですが、とりあえず何か作ってみようと思って、パラパラマンガに挑戦してみました。
Form1にpictureBox1を作り、waitを入れてイメージを書き換えるという単純なものです。
ところが実行しようと思ったら、「静的でないフィールド、メソッド、またはプロパティ’WindowsFormApplication1.Form1.pictureBox1’でオブジェクト参照が必要です」というエラーが出ます。その際のフォーカスは、main()内の
Form1.pictureBox1.image = Image.FromFile("motion1.png");
”Form1.pictureBox1”に当てられています。

何が原因と考えられるでしょうか。
もし情報不足であればご指摘いただくか、あるいはこの目的においてやらなければいけないこと、を大雑把に教えていただくだけでも幸いです。
よろしくお願いします。

Aベストアンサー

main()内に記述されているとのことですので、たぶん、
Form1.pictureBox1.Image=Image.FromFile("motion1.png");
Application.Run(new Form1());
と書いているのではないでしょうか?

連載 改訂版 C#入門 第3章 クラスとインスタンス
http://www.atmarkit.co.jp/fdotnet/csharp_abc2/csabc2_003/cs2_003_01.html#cs0302

ここであるように、オブジェクト指向にはクラスとインスタンスという概念があります。
今のコードでは、Form1クラスを操作しようとしているのでそのようなエラーが出ます。

具体的な解決方法としては、PictureBoxに初期画像を設定する処理をForm1のコンストラクタでやるのがいいと思います。
その際、redfox63さんがおっしゃられるように
this.pictureBox1
と記述することが必要です。(thisは自分自身のインスタンスを指します)

後は、Windowsフォームで一定間隔での処理をするための「Timerコンポーネント」について調べてみたらいいと思います。

タイマにより一定時間間隔で処理を行うには?(Windowsタイマ編)
http://www.atmarkit.co.jp/fdotnet/dotnettips/372formstimer/formstimer.html

MSDNライブラリ Timerクラス
http://msdn.microsoft.com/ja-jp/library/system.windows.forms.timer.aspx

参考URL:http://www.atmarkit.co.jp/fdotnet/csharp_abc2/index/

main()内に記述されているとのことですので、たぶん、
Form1.pictureBox1.Image=Image.FromFile("motion1.png");
Application.Run(new Form1());
と書いているのではないでしょうか?

連載 改訂版 C#入門 第3章 クラスとインスタンス
http://www.atmarkit.co.jp/fdotnet/csharp_abc2/csabc2_003/cs2_003_01.html#cs0302

ここであるように、オブジェクト指向にはクラスとインスタンスという概念があります。
今のコードでは、Form1クラスを操作しようとしているのでそのようなエラーが出ます。

具...続きを読む

QLNK2019: 未解決の外部シンボルのエラーが出る

Microsoft Visual Studio 2008
Version 9.0.21022.8 RTM
Microsoft .NET Framework
Version 3.5 SP1
----------------------------------------------------------------
新しいプリジェクト→Win32 コンソール アプリケーション(ソリューションのディレクトリを作成 チェック外す)→Windows アプリケーション(空のプロジェクト チェック外す)
----------------------------------------------------------------
 プログラム

 mymain.cpp
#include "myhelper.h"
#include "mymain.h"

//自キャラのデータ
Point2D g_jikipos = {40, 400};//自キャラの座標

//画像ハンドル
int g_jikiimage[11];

//色々なファイルの読み込み
int LoadFiles(){
//画像ファイル読み込み
if(LoadDivGraph("media\\player01.bmp",
11,11,1,64,64,g_jikiimage) == -1) return -1;

return 1;
}


 mymain.h
//他から呼び出させるMyMainの関数
void MyMain();
int LoadFiles();


 myhelper.h(サンプルなので打ちミスはない)
#include "DxLib.h"
#include <limits.h>
#include <math.h>

//構造体宣言
//座標またはベクトルを記録する構造体
struct Vector{
float x,y;
};
typedef Vector Point2D;
//線を記録する構造体
struct Line2D{
Point2D startpos, endpos;
float katamuki;//傾きをラジアン値で記録
Vector speed;//移動している場合は速度をセット
};
//球体を記録する構造体
struct Ball2D{
Point2D position;
float hankei;//半径
};
//四角形を記録する構造体
struct Rect2D{
Point2D lefttop;
Point2D rightbottom;
float width;
float height;
};


//ライブラリ関数
Point2D PosInView(Point2D in);
int XInView(float inx);
int YInView(float iny);
void ScrollToLeft(float jikiposx);
void ScrollToRight(float jikiposx);
void ScrollToUp(float jikiposy);
void ScrollToDown(float jikiposy);
void DrawLineInView(float x1, float y1, float x2, float y2, int Color, int Thickness);
void DrawCircleInView(float x, float y, float r, int Color, int FillFlag);
void DrawAnimation(float x, float y, double ExtRate, double Angle,int TurnFlag,
int *imgarray, int allframe, float fps);
//ベクトル関数
Vector CreateVector(Vector in, float veclen);
Vector AddVector(Vector v1, Vector v2);
Vector SubVector(Vector v1, Vector v2);
Vector AddVectorInFrameTime(Vector pos, Vector speed);
Vector AddVectorInFrameTime2(Vector pos, Vector speed, Vector accel);
Vector Normalize(Vector in);
Vector RotateVector(Vector in, float radian);
float VectorLengthSquare(Vector in);
float DotProduct(Vector v1, Vector v2);
float CrossProduct(Vector v1, Vector v2);
void SetLine2DKatamuki(Line2D *in);
void DrawLine2D(Line2D in, int Color, int Thickness);
void DrawBall2D(Ball2D in, int Color, int Fill);
//当たり判定関数
bool HitTestLineAndBall(Line2D linein, Ball2D ballin);
bool IsPointAtLineFace(Line2D linein, Point2D ptin);
bool HitTestLineAndLine(Line2D line1, Line2D line2);
bool HitTestBallAndBall(Ball2D a, Ball2D b);
bool HitTestPointAndBox(Rect2D rect, Point2D pt);
//タイマー関数
void SetSimpleTimer(int idx, int time);
int GetPassedTime(int idx);


//グローバル変数
extern float g_frametime;
extern Rect2D g_framerect;//画面領域(当たり判定)
extern Point2D g_current_field_pos;//現在の左上座標
extern Rect2D g_stagesize;//ステージサイズ

//定数宣言
const float ZEROVALUE = 1e-10f;
const float PIE = 3.1415926f;
const int SCROLL_LIMIT = 200;
----------------------------------------------------------------
 エラー内容
1>myhelper.obj : error LNK2019: 未解決の外部シンボル "void __cdecl MyMain(void)" (?MyMain@@YAXXZ) が関数 _WinMain@16 で参照されました
1>C:\Documents and Settings\Owner\My Documents\Visual Studio 2008\Projects\my\Debug\my.exe : fatal error LNK1120: 外部参照 1 が未解決です
1>my - エラー 2、警告 0
ビルド: 0 正常終了、1 失敗、0 更新不要、0 スキップ
----------------------------------------------------------------
画像を貼り付けときます
(見えにくい場合→http://www.dotup.org/uploda/www.dotup.org154142.jpg.html)
初心者なのでわかりやすくお願いします

Microsoft Visual Studio 2008
Version 9.0.21022.8 RTM
Microsoft .NET Framework
Version 3.5 SP1
----------------------------------------------------------------
新しいプリジェクト→Win32 コンソール アプリケーション(ソリューションのディレクトリを作成 チェック外す)→Windows アプリケーション(空のプロジェクト チェック外す)
----------------------------------------------------------------
 プログラム

 mymain.cpp
#include "myhelper.h"
#include "mymain.h"

//自...続きを読む

Aベストアンサー

ファイル構成から推測するに
mymain.cpp というファイルに
void MyMain(void) {
// ここに処理を書く
}
という関数が必要なようです。

QC# シリアル通信でデータ受信時の欠損について

Visualstudio 2013 を使用して C# で開発を行っています。

SerialPort Classを使用してデータの送受信をするプログラムを作成しているのですが、
非同期でデータを受信する際にどうしてもうまくデータを取得出来ません。

5Byteのデータは正常に取得できるのですが、
その直後にくる40Byteのデータは、真ん中あたりの10数Byteや最後の10数Byteしか取れません。


serialPort.DataReceived に登録したイベント関数の中身です。

--------------------------------------------------------------------------------------
private void _serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
bytesRead = 0;
// Initialize a buffer to hold the received data
byte[] buffer = new byte[this.serialPort.ReadBufferSize];

try
{
bytesRead = this.serialPort.Read(buffer, 0, buffer.Length);
if (true == serialPort.IsOpen)
{
serialPort.DiscardInBuffer();//受信バッファをクリアする
}

}
catch (Exception ex)
{
DataLog.Exception(ex);
}

//派生クラス用の処理
DeviceClassEventArgs _DeviceClassEventArgs = new DeviceClassEventArgs(buffer, bytesRead);
DeviceClassEvent(this, _DeviceClassEventArgs);
}
--------------------------------------------------------------------------------------

ネットの情報を参考に、
ReceivedBytesThreshold の値を期待するデータ量に逐一変えることで
とりあえず正常に取ることが出来たのですが、これでいいのでしょうか?
期待するデータ量がわからなかった場合は使えないのかなとも思います。

データが欠損してしまう理由、
上記の対処法以外の一般的な対処法など有りましたら教えて下さい。

その他参考になるページ等ありましたら教えていただけると大変助かります。

Visualstudio 2013 を使用して C# で開発を行っています。

SerialPort Classを使用してデータの送受信をするプログラムを作成しているのですが、
非同期でデータを受信する際にどうしてもうまくデータを取得出来ません。

5Byteのデータは正常に取得できるのですが、
その直後にくる40Byteのデータは、真ん中あたりの10数Byteや最後の10数Byteしか取れません。


serialPort.DataReceived に登録したイベント関数の中身です。

-------------------------------------------------------------------------------...続きを読む

Aベストアンサー

DataReceivedイベントが発生したときでも、
シリアルポートへの受信はまだ継続している可能性があるので
不用意にバッファクリアしてはいけない。
非同期の受信処理は、何かと難しいのです。

private void _serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
  bytesRead = 0;
  // Initialize a buffer to hold the received data
  byte[] buffer = new byte[this.serialPort.ReadBufferSize];

  try
  {
    //bytesRead = this.serialPort.Read(buffer, 0, buffer.Length);
    //if (true == serialPort.IsOpen)
    //{
    // serialPort.DiscardInBuffer();//受信バッファをクリアする
    //}

    // 受信バッファにデータがなくなるまで繰り返し読込む
    while (true)
    {
      if (0 == serialPort.BytesToRead)
      {
        break;
      }
      buffer[bytesRead] = (byte)serialPort.ReadByte();
      bytesRead++;
      System.Threading.Thread.Sleep(0);

      // シリアルポートの受信バッファには、
      // ・必要なブロックの途中から受信している。
      // ・次のブロックの先頭部分も受信されている。
      // 可能性があるので、ここで必要なブロックだけRead()できたことを確認する。
      if (必要なブロックが正常に読めたか確認する関数())
      {
        break;
      }
    }
  }
  catch (Exception ex)
  {
    DataLog.Exception(ex);
  }

  //派生クラス用の処理
  DeviceClassEventArgs _DeviceClassEventArgs = new DeviceClassEventArgs(buffer, bytesRead);
  DeviceClassEvent(this, _DeviceClassEventArgs);
}

DataReceivedイベントが発生したときでも、
シリアルポートへの受信はまだ継続している可能性があるので
不用意にバッファクリアしてはいけない。
非同期の受信処理は、何かと難しいのです。

private void _serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
  bytesRead = 0;
  // Initialize a buffer to hold the received data
  byte[] buffer = new byte[this.serialPort.ReadBufferSize];

  try
  {
    //bytesRead = this.serialPort.Read(buffer, 0, buffer.Le...続きを読む

QC#でint型をIntPtr型に変換したいです。

C#でint型をIntPtr型に変換したいです。

こちらのサイト↓
http://msdn.microsoft.com/ja-jp/library/a0f1byxz.aspx
は見たのですが、いまいち理解できず困っております。。
sampleがあれば良かったのですが。。

どなたかご教授お願いいたします。
よろしくお願いいたします。

Aベストアンサー

int 元データ;
IntPtr 新データ = new IntPtr(元データ);

QC#でほかのファイルにある自作クラスを使用したい場合

最近Javaから移ってきたばかりのC#初心者です。
依然作ったことのあるほかのファイルにある自作クラスを
新しいファイルの自作クラスで使用したい場合、どうすれば
よいのでしょうか。
Form1.cs(8,7): error CS0246: 型または名前空間名 'Calc1' が見つかりませんでした。using ディレクティブまたはアセンブリ参照が不足しています。
Form1.cs(12,19): error CS0246: 型または名前空間名 'Calc' が見つかりませんでした。using ディレクティブまたはアセンブリ参照が不足しています。
上記のようなエラーが発生してしまいます。
なにとぞご助力をお願いいたします。

Aベストアンサー

#2>しかし、やはりうまくいきませんでした。
#1で言われているような、namespace, using 指定と参照設定でうまくいくと思うんですけど・

自作クラスを補足することはできますか?

QDWORDの実際の型は何でしょうか

VC++.NETの環境です。
DOWRD dw1 = 1;
int i = 2; と定義し
ここで
if ( i > dw1 ){
何かの処理;
}
とコーディングすると
warning C4018: '>' : signed と unsigned の数値を比較しようとしました。
のワーニングがでます。
これは、DWORDがint型でなくunsigned int型のようにも見えます。
ある本によれば(VC++.V.NET逆引き大全500の極意)
DWORD はint型であると記述されています。
もし、int型ならこのワーニングはでないはずなのですが、
なぜでるのでしょうか。又、DWORDの実際の型は何なのでしょうか。ご存じのかたおりましたら、教えていただけませんでしょうか。

Aベストアンサー

型定義が知りたいのならば、宣言ファイルを見れば疑問を挟む余地もありません。
DWORD型はwindef.hで
"typedef unsigned long DWORD;"
と宣言されています。

Visual Studioを使っているのならば、知りたい型の上にマウスポインタを置いて右クリック、ポップアップメニューの「定義へ移動」または「宣言へ移動」で簡単に知ることが出来ます。

QC#.net Define文

C#.net でプログラムを作成中です。

C++では定数等を#defineで定義していましたが、C#ではDefineは無いとの事です。

C#でC++の#Define に相当する事は出来ないのでしょうか。
やりたい事は 長い同じコマンドを別の文字で代替する事です。

もしC#に#Define文があったとして、以下の様な事を行いたいのです。

#Define NL printf("\n");

NL

このコーディングが展開した時、
 pritf("\n");
と展開される。

この様な事が出来る文をご存知の方、若しくはこの様な事は出来ないと言う事をご存知の方、よろしく御回答下さい。

よろしくお願い致します。

Aベストアンサー

定数の定義では無く、どちらかというと「マクロ」としての機能をC#で使いたいと言うことですね。

結論から言うと、C++の#defineを使ったマクロは、C#では利用できません。
このようなマクロはバグの元になりやすいため、意図的にC#から削られています。

もし、汎用的に使えるような引数を渡したい…という事であれば、ジェネリックなどを使うと良いですよ。


ちなみに、単純な定数の定義であればconst、列挙体であればenumを使って定義できます。


このQ&Aを見た人がよく見るQ&A

人気Q&Aランキング