利用規約の変更について

現在、いわゆる組み込み制御の技術者としてC言語によるマイコン制御のソフトウェア開発を行っていて、スイッチの出力を制御する仕組みを考えています。

状態(2状態):S00、S01
スイッチ(2スイッチ):SW1、SW2
出力:ON、OFF

実際には状態もスイッチも出力もこれより多く複雑なのですが、簡単に説明させていただきます。

自分としては簡単にswitch文で制御すればいいだろうと思い、設計していたのですが、それでは無駄にコーディングが必要になってしまうのでダメだと上司に言われ、ヘッダ(sw_out.h)でテーブルを持って、Cソース(sw_out.c)では簡単なコードにできないか?と言われました。その方が後のメンテナンスが簡単だと言われました。

上司の言っていることは分かるのですが、実際にコードとしてどのように記述したらいいのか分かりません。

そこで、このような制御を実現するC言語(*.h,*.c)の記述の方法を分かる方、御教授よろしくお願いします。

以下に、スイッチ出力の判定を行うテーブルと自分の最初の考え方のソースコードを記します。

******S00****S01**
SW0*SW_ON**SW_OFF*
SW1*SW_OFF*SW_ON**

[sw_out.h]
#define S00 0x00 /* 状態0 */
#define S01 0x01 /* 状態1 */
#define SW0 0x00 /* スイッチ0 */
#define SW1 0x01 /* スイッチ1 */
#define SW_OFF 0x00 /* 出力OFF */
#define SW_ON 0x01 /* 出力ON */

[sw_out.c]
#include "sw_out.h"
int sw_out(int state, int sw)
{
int out;
out = SW_OFF;
switch(state){
case S00:
switch(sw){
case SW0:
out = SW_ON;
break;
case SW1:
out = SW_OFF;
break;
}
break;
case S01:
switch(sw){
case SW0:
out = SW_OFF;
break;
case SW1:
out = SW_ON;
break;
}
break;
}
return out;
}

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

A 回答 (2件)

テーブルと言われても具体的にイメージが沸かない状態だと思いますので、テーブルという言葉から簡単なエクセルの表形式みたいなものを連想すれば良いでしょう。


それをソースではベタな配列として実装します。
実行コードでは演算を行わず、その配列へアクセスすることで様々なメリットが享受できます。

サンプル同等のコードをテーブルを使用して記述してみると

[sw_out.c]
#include "sw_out.h"

#define NSWITCH 2
unsigned char sw_table[][NSWITCH] = {
/* { SW0, SW1 }, */
{ SW_ON, SW_OFF },/* S00 */
{ SW_OFF, SW_ON },/* S01 */
};

int sw_out( int state, int sw )
{
return (int)sw_table[state][sw];
}

スイッチの追加も、状態の追加も、各状態でのスイッチの挙動もテーブルの変更だけで可能です。
メンテの際、実行コードに触れる必要が少ないのもテーブルを使うメリットです。
(他に実行速度や可読性など)

ワンパターンな実装方法ですので、テーブルを構造体配列として好きに定義すれば、別の案件でもいろんな状態の管理が後々楽になります。


以下追記ですが、上司殿のおっしゃる言葉は、「ヘッダ(sw_out.cの先頭部分)でテーブルを持ってCソース(実行コード上)では簡単なコードにできないか?」と私なら解釈します。

「ヘッダファイル(.h)」と「(.c)ファイルのヘッダ部分」の違いですね。
ヘッダファイル(.h)に記述してしまっては、他のモジュールから複数インクルードされた場合、簡単に多重定義のエラーになりますが、(.c)ファイルのヘッダ部分に書くことで、いざメンテの場合に対象部分(テーブル)をより速く発見し、少しでも作業を短時間で終えることができますね。
    • good
    • 0
この回答へのお礼

bug_bugさん、早速の御回答ありがとうございます。
初心者の低いレベルの質問に真摯に対応いただき感謝いたします。

> それをソースではベタな配列として実装します。
> 実行コードでは演算を行わず、その配列へアクセスすることで様々な
> メリットが享受できます。

配列を使うと簡単に制御することができるのですね。

> サンプル同等のコードをテーブルを使用して記述してみると

サンプルコードの御提供ありがとうございます。
非常に分かりやすくなりますね。
まさに、自分がやらなければならない制御の実現方法が分かった気がします。

> ワンパターンな実装方法ですので、
> テーブルを構造体配列として好きに定義すれば、
> 別の案件でもいろんな状態の管理が後々楽になります。

確かに、このコードだとメンテナンスが非常に楽になりそうです。

> 以下追記ですが、上司殿のおっしゃる言葉は、
> 「ヘッダ(sw_out.cの先頭部分)でテーブルを持って
> Cソース(実行コード上)では簡単なコードにできないか?」
> と私なら解釈します。

多分、bug_bugさんの仰るとおりだと思います。
自分が「ヘッダ」という言葉からヘッダファイル(*.h)と決め付けてしまっただけでCソースファイルのヘッダ部分という解釈が正しいと思います。

細かい点にまで御忠告いただき、非常に分かりやすく御回答を拝見させていただくことができました。
本当に自分のようなレベルの技術者には助かります。
ありがとうございました。

質問させていただいた件については皆様の御協力により解決することができました。
ありがとうございました。
今後もよろしくお願いします。

お礼日時:2007/12/16 10:21

> ヘッダ(sw_out.h)でテーブルを持って、


> Cソース(sw_out.c)では簡単なコードにできないか?

上司が「テーブル」って言われているので素直に配列(テーブル)を
使えば大幅にコード削減・最適化できると思うのですが・・・
敢えてコードは記しません、ググればすぐ出ます。

また、提示されましたソースを見てもテーブル記述は無く、
テーブルというよりはただの定数宣言のような気がします。

smallinokiさんの言われている「テーブル」の定義が
私の認識と異なるものであれば、的外れですので申し訳ありません。

参考URL:http://www.geocities.jp/ky_webid/c/023.html
    • good
    • 0
この回答へのお礼

lesskeyさん、早速の御回答ありがとうございます。
初心者の低いレベルの質問に真摯に対応いただき感謝いたします。

> 上司が「テーブル」って言われているので素直に配列(テーブル)を
> 使えば大幅にコード削減・最適化できると思うのですが・・・

配列を使うと簡単に制御することができるのですね。
参考URLを拝見させていただきました。
非常に参考になりました。ありがとうございます。

> 敢えてコードは記しません、ググればすぐ出ます。

一応、「C言語」「ヘッダ」「テーブル」等のキーワードでgoogleの検索サイトから検索はしてみたのですが、なかなか自分のレベルでは何万件のなかから自分の知りたい情報にたどり着くことができずに質問させていただいた次第です。今後は知識レベルを上げてこれらの情報から目的のサイトを特定できるようにしていきたいと思います。

> また、提示されましたソースを見てもテーブル記述は無く、
> テーブルというよりはただの定数宣言のような気がします。

申し訳ありません。質問の日本語の表現力不足だったかもしれません。
[sw_out.h]の内容はテーブルを表現したものではなく、自分自身のプログラムをそのまま記したものです。
今後は、質問の日本語も分かり易い記述ができるように表現力向上に努めたいと思います。

質問させていただいた件については皆様の御協力により解決することができました。
ありがとうございました。
今後もよろしくお願いします。

お礼日時:2007/12/16 10:09

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

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

QC言語でヘッダファイルにグローバル変数を宣言する

main.hに

static int a;

と記述し、main.cで

#include "main.h"
[省略]
a=10;

のように使用して、-Wallをつけてコンパイルすると、main.cで使用しているにも関わらず、

'a' defined but not used

という警告が表示されます。
同様に、関数においても、ヘッダファイルでstaticをつけると

‘~’ declared ‘static’ but never defined

と警告されます。

静的グローバル変数などは、ソースファイル内で宣言しなければいけないのでしょうか?ヘッダファイル内で宣言しても警告が出ないような方法はありますか?

Aベストアンサー

静的なグローバル変数というのがそもそもおかしい気がしますが。(もしかしたら、static を「静的」と直訳したのかな?)

そもそも、関数の外で定義されたグローバル変数は、性格的に静的です。
プログラムの実行時に生成されてその後プログラムの終了時までそのまま生存し続けますから。

関数の外で static 宣言された変数は「ファイルスコープ」を持ちます。
※一般的には、「ファイルスコープ」という呼び方はされますが、実際には、「コンパイル単位」からは見えるのは、No.2で指摘されているとおり。

なので、複数のファイルからアクセスするための変数=グローバル変数 に、static をつけることはまずありません。

さて、質問の内容から判断すると、

・最初、static をつけずに、ヘッダファイルでグローバル変数を定義しようとしたら、リンクで失敗した。
・この時点で、質問の、[省略]の部分で、グローバル変数を定義して、それが生き残っている。
・何かの拍子に、ヘッダファイルのグローバル変数に、static をつけてみたら、リンクも成功してしまった。

という流れが見える気がするのですが。
そもそも、Cにおけるグローバル変数は、「一カ所だけ宣言」「使うところですべて定義」という決まりなので、ちょっと管理がやっかいです。

こういう流れを仮定して、ヘッダファイルに普通にグローバル変数を定義するのに、よく使われていた方法は、

---- main.h ---

#if defined(_GLOBAL_HERE)
#define GLOBAL
#else
#define GLOBAL extern
#endif

GLOBAL int a;

----- ここまで ----

というヘッダファイルを作って、

main.c では、

#define _GLOBAL_HERE
#include "main.h"

その他のファイルでは、
#include "main.h"

で、main.c では、extern なし、それ以外のファイルでは、 extern つきの変数宣言に差し替えるという手法があります。

初期化を含む場合は、

---- main.h ---

#if defined(_GLOBAL_HERE)
#define GLOBAL
#define DEF(x) = (x)
#else
#define GLOBAL extern
#define DEF(x)
#endif

GLOBAL int a;
GLOBAL int b DEF(1);

----- ここまで ----

とか。
(ただし、この DEF() は万能ではないです)

あと、関数で static をつけた場合も、ファイルスコープ(こちらも、実際には、コンパイル単位内スコープ)となりますから、コンパイル単位の中の関数しかアクセスできません。そこで、コンパイル単位の中に、関数の実体がない場合に、質問されたような警告が出たのだろうと思います。

静的なグローバル変数というのがそもそもおかしい気がしますが。(もしかしたら、static を「静的」と直訳したのかな?)

そもそも、関数の外で定義されたグローバル変数は、性格的に静的です。
プログラムの実行時に生成されてその後プログラムの終了時までそのまま生存し続けますから。

関数の外で static 宣言された変数は「ファイルスコープ」を持ちます。
※一般的には、「ファイルスコープ」という呼び方はされますが、実際には、「コンパイル単位」からは見えるのは、No.2で指摘されているとおり。

なの...続きを読む

Qリトルエンディアン→ビッグエンディアン

(1)リトルエンディアン
typedef struct recvData{
 int a;
 unsigned char b[16];
unsigned char c[8];
unsigned int d[4];
} recvData_t;

recvData_t rData;


(2)ビッグエンディアン
typedef struct sendData{
 int a;
 unsigned int b[4];
unsigned int c[2];
unsigned int d[4];
} sendData_t;

sendData_t sData;

上記のようなリトルエンディアンの構造体の各メンバのデータを、ビッグエンディアンの構造体の各メンバのデータにそれぞれ格納するには
どうしたらよいでしょうか?

Aベストアンサー

○「リトルエンディアンの構造体」とか「ビッグエンディアンの構造体」というのは、残念ながら見たことがありません。

○質問の文章からだと

rData.bにリトルエンディアンで8ビット*4(=32ビット)が4つ入っている
→これをsData.b (int型 [4] )に
rData.cにリトルエンディアンで8ビット*4(=32ビット)が2つ入っている
→これをsData.c (int型 [2])に

とも

rData.bにリトルエンディアンで8ビット*16(=128ビット)が1つ入っている
→これをsData.b (int型 [4] )をまとめて1つの128ビットと見做してビッグエンディアンに
rData.cにリトルエンディアンで8ビット*8(=64ビット)が1つ入っている
→これをsData.c (int型 [2])にまとめて1つの64ビットと見做してビッグエンディアンに

とも解釈できます。あなたのやりたいのは何でしょう?

○ .a, .dはそのままでいいのでしょうか?

○動かそうとしている計算機では、intの内部表現がビッグエンディアンになっていることは確認していますか?

○ unsigned intが32ビットだとして、ベタな方法はビットシフトとビット毎のorを使うものでしょう
unsigned int ui = (b[0] << 24) | (b[1] << 16) | ( b[2] << 8 ) | (b[3]) ;
なんども使うなら、マクロなり関数なりにすればいいです。

○「リトルエンディアンの構造体」とか「ビッグエンディアンの構造体」というのは、残念ながら見たことがありません。

○質問の文章からだと

rData.bにリトルエンディアンで8ビット*4(=32ビット)が4つ入っている
→これをsData.b (int型 [4] )に
rData.cにリトルエンディアンで8ビット*4(=32ビット)が2つ入っている
→これをsData.c (int型 [2])に

とも

rData.bにリトルエンディアンで8ビット*16(=128ビット)が1つ入っている
→これをsData.b (int型 [4] )をまとめて1つの128ビットと見做してビッグエンディアンに
...続きを読む

Q関数の実体定義にヘッダファイルの2重定義防止方法が効かない?

いつもお世話になっています。
MFCでCプログラミングをしています。

ヘッダファイルの2重定義防止のために、
ヘッダファイル全体を下記のように
囲みました。
<aaa.h>
#ifndef AAA
#define AAA
#define PI 3.141592
void Func();
int wa(int a, int b){
return a+b;
}
#endif

ビルドしたところ、
関数宣言(Func)や#define部分(PI)については、
2重定義が防止されているようなのですが、
関数の実体部分(関数wa)については、
2重定義防止機能が働かず、
***.obj : error LNK2005:
"int __cdecl wa(int a, int b)"
は既に ***.obj で定義されています。
というリンクエラーが表示されます。

関数の種類や
ヘッダファイル内の宣言の順番を
いろいろ変えてみたのですが同じ結果でした。

ここで、このヘッダファイルの先頭に
#pragma onceを使用すると
このリンクエラーは回避されるのですが、
他コンパイラとの互換性の観点から、
#pragma once以外の方法で実現する必要があるので、
困っています。

URLを検索してみたのですが、
このような特殊な場合について記述されているものは
見つけられませんでした。
どなたか解決法又はヒントをご教示頂ければ
ありがたいです。
よろしくお願いします。

いつもお世話になっています。
MFCでCプログラミングをしています。

ヘッダファイルの2重定義防止のために、
ヘッダファイル全体を下記のように
囲みました。
<aaa.h>
#ifndef AAA
#define AAA
#define PI 3.141592
void Func();
int wa(int a, int b){
return a+b;
}
#endif

ビルドしたところ、
関数宣言(Func)や#define部分(PI)については、
2重定義が防止されているようなのですが、
関数の実体部分(関数wa)については、
2重定義防止機能が働かず、
***.obj : error LNK20...続きを読む

Aベストアンサー

二重インクルードは防止できています。ただし、あくまで1つのコンパイル単位の中での話です。エラー内容から察すると、2つ以上のソースファイルでaaa.hをインクルードしていますね?コンパイル時点ではエラーにはならずに複数のobjファイルが生成されますが、リンク時にエラーが発生します。これは、リンク実行時に関数waの定義が複数のモジュールで発見されるためです。

このようなエラーを防ぐため、通常、ヘッダファイルで関数の定義は行わず、その代わりに

extern int wa(int a, int b);

のように宣言だけを記述します。関数定義はどこかのソースファイルで1回だけ行います。

QLinuxのgccのインクルードパス?

Linuxのgccで、インクルードファイルやライブラリのパスを設定する方法が知りたいのですが、gccについて詳しい書籍やサイトがありましたら、教えてください。

gccとccの違いも知りたいです。

例)
#include "example.h"

このままだと、example.hが無いと表示されます。

Aベストアンサー

標準ライブラリのパスは、gccのインストール時に指定して、Cプリプロセッサの中に組み込まれます。

#include "example.h"
は、まずカレントディレクトリを探し、次に gccコマンドラインの -I オプションで指定したディレクトリを探し、最後に標準ライブラリが探されます。

#include <example.h>
は、カレントディレクトリを探さない点が異なります。

ccも基本的には同じですが、Unixの種類によって機能が異なる可能性があります。Linuxの場合はcc=gccです。

Qミドルウエアの具体例を教えてください。

初級シスアドで、OSとアプリケーションソフトの中間に位置するものとしてミドルウエアがあり
 ・データベース管理システム(DBMS)
 ・通信管理システム(LAN制御を含む)
 ・ソフトウエア開発支援ツール
 ・EUCツール
 ・運用管理ツール
説明されています。なんとなく具体例が推測できるものもありますし、ぜんぜんイメージできないものもあります。
そこで、推測が間違っていないか確認したいのと、イメージできないものの場合具体例をあげていただければ助かります。

(1) データベース管理システム(DBMS)
多分、OracleやSQL-SeaverやMySQLのようなものだと思うのですが。
この推測はあってますか?

(2) 通信管理システム(LAN制御を含む)
プラットホームや使用アプリが違う場合のデータのやり取りを行うようなもの・・・というイメージがあります。使用アプリの場合はODBCドライバみたいなものの様な(全然自信ない)、プラットホームとなると実例が浮かんできません。

(3) ソフトウエア開発支援ツール
なんでしょう?プログラミングジェネレータのことでしょうか。
EXCELマクロの自動記録機能なんてのもこれに入るのでしょうか。ひょっとするとEXCELマクロは、次のEUCツールでしょうか?

(4) EUCツール
AccessとかEXCELとかでしょうか。イメージ沸きません。

(5) 運用管理ツール
う~ん・・・なんでしょう?

補足:IMEとかもミドルウエアと考えてよいのだろうか? WEBで調べるとワープロや表計算もミドルウエアと定義しているものもあります。それは少し拡張解釈なような気がします。

いずれにせよ、すっきりした定義と具体例を書いてあるものを見つけられないのです。

宜しくお願いします。

初級シスアドで、OSとアプリケーションソフトの中間に位置するものとしてミドルウエアがあり
 ・データベース管理システム(DBMS)
 ・通信管理システム(LAN制御を含む)
 ・ソフトウエア開発支援ツール
 ・EUCツール
 ・運用管理ツール
説明されています。なんとなく具体例が推測できるものもありますし、ぜんぜんイメージできないものもあります。
そこで、推測が間違っていないか確認したいのと、イメージできないものの場合具体例をあげていただければ助かります。

(1) データベース管理システ...続きを読む

Aベストアンサー

(1) データベース管理システム(DBMS):お書きになられた通りです。
(2) 通信管理システム(LAN制御を含む:TCP/IPドライバー等通信制御を行うアプリケーションです。ファームウェアも該当するでしょう。通常ユーザが操作する類のアプリケーションではありません。
(3) ソフトウエア開発支援ツール:VisualBASIC、C言語、Perl等、亜ぷロケーションを開発するツール、プログラミング言語と言えば分かり易いでしょうか。
(4)EUCツール:エンドユーザが使用するアプリケーションです。
(5)運用管理ツール:クライアントPCの管理ツール、DBシステムの管理ツール、WEB/メールのサーバ管理等、運用機器を管理するツールです。最近では情報漏えいを防止する目的のツールが多数出ています。

QC言語での改行コードの扱いについて教えてください。

改行コードは一般的に、
Windows・・・「CR+LF」
UNIX or Linux・・・「LF」

だと思うのですが、改行コードが「LF」のファイルをWindows上で、C言語で編集したらどういった改行コードになるのでしょうか。
(例えば、単純にファイルを1レコードずつ読み込んで別ファイルに書き込むといった処理)

出力時に「CR+LF」になってくるのでしょうか?それともまったく別のものになってしまうのでしょうか?

詳しい方、ぜひ教えてください。


※改行コード「LF」のテキストファイルは、UNIXサーバから「HULFT」というファイル転送ソフトの「バイナリ転送モード」でWindowsサーバに送られてくる予定です。

Aベストアンサー

実行環境にWindowsを想定している処理系では、ファイルをオープンする際のオープンモードには「テキスト」と「バイナリ」の区別があります。

テキストタイプでオープンすると、ファイルストリーム中の改行コードが自動的に加工されます。

具体的には、
・アプリケーションでストリームを読み込むと、ファイル中のCR+LFがLFに置き換わって読み込まれる。
・アプリケーションからストリームに書き込むと、データ中のLFがCR+LFに置き換わって書き込まれる。
という加工が行われます。

一方、バイナリタイプでオープンした場合、加工は一切行われません。

つまり「ソースコード上では、改行は\nの1文字のみ。実ファイル上で改行がどんな文字コードになっているか考慮する必要はない」と言う事です。

なので、ファイルのデフォルトのオープンモードが「テキスト」になっている場合は、
ofp=open("textfile.txt","w");
fprintf(ofp,"It is text\nHello world\n");
fclose(ofp);
と言うプログラムは、WindowsでもUNIX/Linuxでも、どちらも正しく動作します。(明示的にテキストモードでオープンする場合は、オープンモードの"w"を"wt"にすること)

質問者さんのケースでは、
・Windowsサーバにファイルが届いた時点で、改行はLFのみ⇒このファイルは「バイナリタイプ」でオープンする。
・普通に「1行読む」と言う処理をする⇒ストリームから読む際は「LFまでが1行」として読まれる。
・別ファイルは「テキスト」でオープンする。
・読み込んだ1行を、何も加工せずに別ファイルに改行付き(\n付き)で書き出す⇒改行が勝手にCR+LFに変換されてファイルに書き出される。
・ファイルの最後まで繰り返す。
という処理をすればOKです。

なお、ファイル転送ソフトでバイナリモードのままWindowsサーバに届いたファイルは、Windows上では「改行の無いファイル」として扱われますから、Windows上のメモ帖やテキストエディタでは正常に開けません。

実行環境にWindowsを想定している処理系では、ファイルをオープンする際のオープンモードには「テキスト」と「バイナリ」の区別があります。

テキストタイプでオープンすると、ファイルストリーム中の改行コードが自動的に加工されます。

具体的には、
・アプリケーションでストリームを読み込むと、ファイル中のCR+LFがLFに置き換わって読み込まれる。
・アプリケーションからストリームに書き込むと、データ中のLFがCR+LFに置き換わって書き込まれる。
という加工が行われます。

一方、バイナリタイ...続きを読む

Q構造体の各メンバにfor文からアクセスする

VC++2005で開発しています。

typedef struct {
char test1[10];
char test2[5];
・・・
char test10[5];
}Sample;

Sample sample;

char *x[] = {"あ", "い",・・・, "こ"};

strcpy(sample.test1, x[0]);
strcpy(sample.test2 = x[2]);
・・・
strcpy(sample.tet10, x[9]);

このstrcpyの部分をループ文でまわしたいのですが、
何か良い方法はありませんか?

よろしくお願いします。

Aベストアンサー

できますけど、あまり意味の無いような気がします。

int offset[] = {
offsetof(Sample,test1),
offsetof(Sample,test2),
offsetof(Sample,test3)
};

int i;
for(i=0; i<sizeof x/sizeof(char*); i++){
strcpy((char*)&sample+offset[i],x[i]);
}

QC言語の enum の使い方

インターネットのサイトなどを利用してC言語を勉強しています。 いま一通り基本的な勉強が済んだところですが、enum というユーザー定義変数をどんな風に使えばよいのか、今ひとつわかりません。サンプルコードなどを見ても、 enum でなくても配列を使えば出来そうなものが多いのですが、この型の変数はどう使えば効果的なのでしょうか。
詳しい方、どうかよろしく教えてください。

Aベストアンサー

>enum というユーザー定義変数を

変数ではないでしょう。
むしろ定数かと。

>enum でなくても配列を使えば出来そうなものが多いのですが

具体的になにがあります?
ちなみに配列ではありませんので誤解なきよう。

http://homepage2.nifty.com/well/enum.html
とか、いい感じに説明されていますかね。
defineだとただの置き換えなので何でも設定できてしまう。とか型チェックができない。とかの問題があります。

私自身、最近使ったやり方では…テーブルのインデックス用に使いましたね。


人気Q&Aランキング

おすすめ情報