C言語での悩み:データの書き込みに関して

このQ&Aのポイント
  • C言語でのデータの書き込みに関する質問です。特に、与えられたソースコードの理解が難しいです。詳細な解説と解決策を教えてください。
  • 与えられたC言語のソースコードについての質問です。データの書き込みに関して理解できない部分があり、解決方法を教えてください。
  • C言語のデータ書き込みに関する質問です。特に、与えられたコードの意味や処理の流れについて理解できません。詳しい解説がほしいです。
回答を見る
  • ベストアンサー

C言語で質問です。

標題の件で質問です。 下記のソースコードが理解できなくて悩んでいます。 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ uint8_t write( uint32_t address, uint32_t data) { uint8_t n = 0; while(n++ < 2) { *(volatile uint16_t *)address = *(uint16_t *)data; data += 2; } } ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ 自分の解釈として dataはuint32_t(4バイト)ですが*(uint16_t *)dataと2バイトでキャストしているので adressにはdataの先頭から2バイト分が書き込まれ、その後dataを2インクリメント(4バイト*2? 2バイト*2?)しているのでdataの後半2バイトはadressに書き込まれていない。 dataの後半2バイトを書きたい場合は、data += 1 とする。 ということで合ってますでしょうか?addressは0x10000000のような定数です。 (data +=2 のとこでどういう処理がされてるのかよく分かっていません。) あとwhileループの中でadressはインクリメントされていないので 同じアドレス(adress)にdataが書き込まれ続けるという理解でよろしいでしょうか? カテゴリが違うなど、質問方法に問題がありましたら申し訳ありません。 以上宜しくお願いします。

質問者が選んだベストアンサー

  • ベストアンサー
  • Wr5
  • ベストアンサー率53% (2177/4070)
回答No.3

>まぁ、コピーした後(呼び出し側で)uint32_tとして扱っているのであれば、たぶん問題ないでしょう。 いやいや、adress変化していないんだから問題ありでした。 >あとwhileループの中でadressはインクリメントされていないので >同じアドレス(adress)にdataが書き込まれ続けるという理解でよろしいでしょうか? の理解通りです。 >(data +=2 のとこでどういう処理がされてるのかよく分かっていません。) dataに渡された値が+2されてdataに格納されます。 つまり通常の加算です。 # dataはポインタではありませんので+2が(sizeof(uint32_t) * 2)にはなりません。

aru333333
質問者

お礼

回答ありがとうございます。返信遅くなり申し訳ありません。 >>dataに渡された値が+2されてdataに格納されます。 >>つまり通常の加算です。 No2の回答を頂いた後、自分も気づきました。 0x10000000番地のdataを2バイト分addressに書いたあと +2、つまり0x10000002番地のデータ(dataの残り2バイト) を書いてるってことですね。 組み込み系ではこういうことをよくやるみたいですね。

その他の回答 (2)

  • Wr5
  • ベストアンサー率53% (2177/4070)
回答No.2

[技術者向] コンピューター > プログラミング > C・C++ にC/C++カテゴリがあります。 なんか…むちゃくちゃですねぇ……。 adressはアドレスではないのですが…キャストで無理矢理アドレスにしているようです。 # で、adressの値は変化していませんので書き込み先のアドレスは変化しません。 dataもアドレス扱い…なんですかねぇ。 >adressにはdataの先頭から2バイト分が書き込まれ Endianの影響を受け…ますかね。 まぁ、コピーした後(呼び出し側で)uint32_tとして扱っているのであれば、たぶん問題ないでしょう。 あと……このままだとビルド時にエラーになります。 戻り値返していないですよね? # で、なんで戻り値はuint8_tなんだろうか……。

  • hashioogi
  • ベストアンサー率25% (102/404)
回答No.1

C言語の教科書のポインタのインクリメントを勉強しなおしてください。32ビット(4バイト)のデータを指しているポインタをインクリメントすれば何バイト進むか書いてあるはずです。もし書いていないようならその教科書は捨てて別の教科書を買った方がいいでしょう。 多分32ビットのデータが3つ以上並んでいて0番目と2番目が対象になっているのではないでしょうか? 書き込み先は同じところです。 書くデータは32ビットのポインタで示されていて、16ビットのポインタでキャストしています。このような場合、32ビットの内の上位16ビットが書かれるのか下位16ビットが書かれるのかはCPUのエンディアンの設定で変わってきます。

aru333333
質問者

お礼

回答ありがとうございます。返信遅くなり申し訳ありません。 エンディアンが重要なんですね。

関連するQ&A

  • C言語、構造体の値を比較する関数

    C言語で組み込み系ソフトを作っています。 初心者です。 下記の型違いのデータを100バイト含んだ構造体があったとします。 そのデータを1バイトずつ所定の範囲内か確認して、 範囲内ならTRUE、外ならFALSEを返す関数を作りたいです。 どうな風に記述してよいかわかりません。 イメージ的には、  データ(mydt)のポインタを引数で渡して  関数内で1バイトずつ所定の範囲内か確認したいです。 データが100個もあるので1個1個変数名と範囲を書いて比較するのは大変です。 ややこしいのが、構造体に型が違うものが入っているので何バイト目のデータが サインドなのかアンサインドなのか識別しないといけないです。 何か良い関数もしくは構造体の記述方法があればお願いします。 //-記- //構造体 struct stdata_tag{ uint8_t run_time; // 0バイト目 運転時間 0~99 int8_t melt_temp; // 1バイト目 融解温度 -10~20 uint8_t cmonbaby; // 2バイト目 家紋赤子 0~2 int8_t cold_temp; // 3バイト目 高温温度 -50~10 : : uint8_t damage; //99バイト目 ダメージ 1~255 } //構造体実態を確保 struct stdata_tag mydt;

  • C言語のプログラムを書いたのですが上手く動きません

    C言語の練習問題をプログラミングしたのですが、上手く動きません。 コンパイルはできるのですが、実行すると「Segmentation fault(core dumped)」となります。 問題は、コマンドライン引数としてファイル名を指定したテキストファイルから読み込んだデータを,双方向リストに格納し,順番に表示するというものです。 テキストファイルは 10T5001 C 10T5002 A 10T5003 B 10T5004 C 10T5005 D 10T5006 B 10T5007 A 10T5008 D このように、IDとランクをランクABCDをスペースで区切ったもので、これをランクAから順番に表示させます。 上手く動けば ID: 10T5002, grade: A, ID: 10T5007, grade: A ID: 10T5003, grade: B ID: 10T5006, grade: B ID: 10T5001, grade: C ID: 10T5004, grade: C ID: 10T5005, grade: D ID: 10T5008, grade: D と表示されるはずなんですが・・・ これが僕の書いたプログラムです(長いです。ごめんなさい) #include <stdio.h> #include <stdlib.h> #include <string.h> #define ID_LENGTH 8 /* IDの長さ+ナル文字 */ #define EFOPEN -1 #define ENOMEM -2 #define EINVAL -3 /* 双方向リストのノード */ typedef struct sList /* タグ */ { char id[ID_LENGTH]; /* ID */ char grade; /* ランク*/ struct sList *prev; /* 前のノードのアドレス */ struct sList *next; /* 次のノードのアドレス */ } sNode; /* 双方向リストのノードを作成する関数 makeNewNode() 作成したノードのprevとnextはNULLにする 引数 ・ノードに格納するID ・ノードに格納するランク(A/B/C/D) 戻値 作成したノードの先頭アドレス.メモリの確保に失敗した場合はNULL */ sNode *makeNewNode(char *id, char grade) { sNode *pNewNode; pNewNode = (sNode*)malloc(sizeof(sNode)); if(pNewNode != NULL) { strncpy(pNewNode->id, id, ID_LENGTH-1); pNewNode->grade = grade; pNewNode->prev = NULL; pNewNode->next = NULL; } return pNewNode; } /* 引数で渡された任意のノードの後ろに新しいノードを追加する関数 insertNext() 引数 ・後ろにノードを追加したいノードの先頭アドレス ・追加するノードの先頭アドレス 戻値 なし */ void insertNext(sNode *node, sNode *newNode) { if(node->next == NULL) //一番後ろに追加する場合 { node->next = newNode; newNode->prev = node; } else //まん中に挿入する場合 { newNode->prev = node; newNode->next = node->next; node->next->prev = newNode; node->next = newNode; } } /* main() 引数 int argc コマンドライン引数の数 char *argv[] 与えられたコマンドライン引数の文字列の先頭アドレスの配列 戻値 int 正常終了の時 0 以下の場合は異常終了し,括弧内の値を返す データファイルが開けなかった場合(EFOPEN) makeNewNode()でメモリが確保できなかった場合(ENOMEM) 引数の数が正しくない場合(EINVAL) */ int main(int argc, char *argv[]) { sNode *top; /* リストの先頭ノードのアドレスを保持する変数*/ sNode *new; /* 新しく作成したノードのアドレスを保持する変数 */ sNode *now; /* 現在見ているノードのアドレスを保持する変数 */ FILE *fp; /* データファイルのファイルポインタ */ char id[ID_LENGTH]; /* ファイルから読み込んだIDを一時的に保持する変数 */ char grade; /* ファイルから読み込んだランクを一時的に保持する変数 */ /* コマンドライン引数の数をチェックする 数に過不足があれば,使い方を表示し,異常終了する */ if(argc != 2){ printf("Usage: %s datafilename.\n", argv[0]); return EINVAL; } /* データファイルを読み込み用に開く ファイルが開けなかった場合,エラーメッセージを表示し異常終了する */ fp = fopen(argv[1], "r"); if(NULL == fp){ printf("No such file %s.\n", argv[1]); return EFOPEN; } /* リストの先頭に番兵を立てる */ new = makeNewNode("Banpei", 'A'-1); if (new == NULL){ printf("Error: cannot allocate memory\n"); return ENOMEM; } top = new; /* データファイルから1行ずつデータを読み込み,ランク順にリストに追加していく 既にリストの先頭には番兵ノードがある点に注意 */ while(EOF != fscanf(fp, "%s %c", id, &grade)){ new = makeNewNode(id, grade); if(new == NULL){ printf("Cannot allocate memory.\n"); return EFOPEN; } if(top->next == NULL){ //リストが空の場合 insertNext(top, new); } else{ now = top; while(new->grade > now->grade){ now = now->next; } insertNext(now, new); } } /* できあがったリストの内容を先頭から順に表示する ただし,番兵ノードは表示しない */ now = top; while(now != NULL){ printf("ID: %s, grade: %c\n",now->id, now->grade,); now = now->next; } /* リストのノードを全て(番兵ノードも含む)解放し,リストを空にする */ now = top; while(now->next != NULL){ free(now->prev); now = now->next; } free(now); fclose(fp); return (0); } まだまだ練習中なもので、かなり拙いものだと思いますが、とりあえずまずはプログラム動くようにしたいです。 よろしくお願いします。

  • 構造体の初期化の時にポインタを入れるにはどうしたらいいですか?

    構造体の初期化の時にポインタを入れるにはどうしたらいいですか? 例えば、このような構造体で↓ struct PACKET { uint16_t size; // データの長さ uint16_t *data; // データバイト列 }; 初期化の時にsizeとdataを入れるにはどうしたらいいのでしょうか? dataがuint16_t*じゃなくてchar*なら struct PACKET { uint16_t size; // データの長さ char *data; // データバイト列 }; struct PACKET p = { 5, "12345" }; というようにできるのですが・・・

  • forに出来てwhileに出来ないことって

    ないですよね? 逆に whileに出来てforに出来ないことはありますよね? 例えばインクリメントしないループなどがそうです。 このような理解であっておりますでしょうか? 本には、 forは繰り返す回数が決まっている時に使い whileは繰り返す回数が決まってない時に使う と書いてありましたが、 僕はwhileですべて間に合うと思うのです。 とするなら、forは何で存在するのかという疑問にぶちあたります。 じっくり考えてもわかりませんでした。 よろしくお願い致します。

  • PIC18F15Q40のI2Cスレーブ動作について

    現在、PIC18F15Q40をMicrochip MPLAB X v5.50とMCC v5.0.3で開発しています。 MCCの自動生成コードによってI2Cスレーブ動作(割り込み駆動)をさせ、 マスタからREAD要求 -> 固定長データを送り返すというようなプログラムを書きたいのですが、 データを送信し終わった後に割り込み処理から復帰せず、WDTによるリセットが掛かってしまいます。 以下、割り込み関数内の処理です。単純にデータカウンタをインクリメントし、それに応じたデータをI2C1_Writeしているだけになります。 void I2C_WriteInterrupt(void) { volatile static uint8_t tx_buf; switch(I2C_TxCounter) { case REV: tx_buf = Revision; break; case STAT: tx_buf = Status; break; case VOL_MSB: tx_buf = (uint8_t)((ADC_Result >> 8) & 0x00FF); break; case VOL_LSB: tx_buf = (uint8_t)(ADC_Result & 0x00FF); break; case CYL_MSB: tx_buf = (uint8_t)((CycleCount >> 8) & 0x00FF); break; case CYL_LSB: tx_buf = (uint8_t)(CycleCount & 0x00FF); break; default: tx_buf = 0xFF; } I2C1_Write(tx_buf); // カウンタ操作とタイマ再開 if(I2C_TxCounter < CYL_LSB) I2C_TxCounter++; } 以前PIC16F18877で同様の処理を行わせた際には特に問題は発生しなかったのですが…… MCCの設定はスレーブアドレスとクロックストレッチ有効、Interrupt Driven有効以外に変更していません。 どなたか解決策お持ちの方はいらっしゃいますでしょうか。

  • c言語の配列の境界調整について

    c言語の配列の境界調整について 以前、タイトル(c言語の境界調整について)の回答内容中で「データ型のアライメントとは何か、なぜ必要か」の下記のURLを紹介された中の配列の部分を 教えて頂きたい。 5.2 複合データ型の各要素のオフセット記述している、  (char * )&D == (char *)&D + offsetof(D_t, Di) は、配列にも適用されるのです か たとえば、 char s[4] = {1.2.3,4}; の場合    char * が 4バイトであれば、先頭のアドレスは、4バイトアライメントで    次の配列の要素は、1バイトアライメントでよろしいでしょうか   (http://www5d.biglobe.ne.jp/~noocyte/Programming/Alignment.html)

  • C言語での構造体

    C言語で、 キュー構造を作りたいのですが、うまくできません。 途中まで作ったのですが、うまく動きませんでした。 EnqueueやDequeueでデータの出し入れをするのですが、そのままではデータを取り出したときにデータが先頭に来ないので、refreshで先頭に持ってくるようにプログラムを組みました。 ----------------------------------------------------- #include<stdio.h> #define MAXQUEUE 10 typedef struct queue{ int head, tail; char entry[MAXQUEUE]; } Queue; //キュー構造にデータを入れる。 void Enqueue(char item,Queue *q){ q->entry[q->tail]=item; q->tail++; } //キュー構造からデータを取り出す。 void Dequeue(char *item,Queue *q){ *item=q->entry[q->head]; q->head++; } //キュー構造内のデータを先頭にずらす。 void refresh(Queue *q){ while(q->head==0){ q->entry[q->head-1]=q->entry[q->head]; q->head--; q->tail--; } } void main(){ Queue qu; Enqueue('w1',&qu); Enqueue('w2',&qu); Enqueue('w3',&qu); Enqueue('w4',&qu); Dequeue(&qu,&qu); Dequeue(&qu,&qu); refresh(&qu); } ---------------------------------------------------------------- mainからEnqueueやDequeueを呼び出すときに、引数として何を渡せばいいのでしょうか?構造体をそのまま渡してみたのですが、「error C2664: 'Dequeue' : 1 番目の引数を 'Queue *' から 'char *' に変換できません。(新しい機能 ; ヘルプを参照) 指示された型は関連がありません。変換には reinterpret_cast、C スタイル キャストまたは関数スタイルのキャストが必要です。」というエラーを吐いてしまいます。 分かりづらい説明で申し訳御座いませんが、ご回答宜しくお願いいたします。

  • C言語 ダブルポインタを引数にもつAPI

    GetBuf ( char ** address, size_t *dataSize ); 第1引数: バッファの先頭アドレスをかえす 第2引数: バッファサイズをByte単位で返す 戻り値 1:成功      -1 取得失敗 typedef struct Test_t{ char* tempAddr; /* 先頭アドレスを格納 */ int bufSize; /* サイズを格納 */ } test_t 上記のAPIから情報を取得し、以下の構造体にデータを保持しようとしていますが GetBuffのダブルポインタの情報を構造体のメンバtempAddrに格納するにはどうしたらよいでしょうか このAPIのように引数でダブルポインタを使用するケースは一般的なのでしょうか? 教えていただけるとうれしいです。 よろしくお願い致します。

  • C言語 クイックソートについて

    クイックソートでわからない点があるため、 質問させていただきます。 ----------------------------------------------------- ~省略~ data = [5, 3, 4, 2, 6, 1]; no = 6; //データの個数 ~省略~ dqsort(data, 0, no-1); ~省略~ void dqsort(double data[], int lower, int upper) {   int i, boundary; //boundaryは配列の前半と後半の境界を示す   if(lower >= upper) { return; }   //基準値となる値(データの中央に位置する値)とデータの先頭の値を入れ替える   swapdata(&data[lower], &data[(lower + upper)/2]);   boundary = lower; //boundaryを先頭に移動させる     for(i=lower+1; i<=upper; i++) {       if(data[i] < data[lower]) {         //配列の前半部分に移動し、境界を移動する         swapdata(&data[++boundary], &data[i]);       }     }   //最後に基準値を境界位置にコピー   swapdata(&data[lower], &data[boundary]);   //配列の前半部分をクイックソート   dqsort(data, lower, boundary-1);   //配列の後半部分をクイックソート   dqsort(data, boundary+1, upper); } void swapdata(double *i, double *j) {   int temp;   temp = *i;   *i = *j;   *j = temp; } --------------------------------------------------------- 基準値と配列の先頭のデータを入れ替えた後、 データは、[4, 3, 5, 2, 6, 1]となり、lower = 0であるため、 data[boundary]はdata[0]となります。 for文の最初のループでdata[0]<data[1]なら、 swapdata(&data[++boundary], &data[i]); となっているのですが、 [++boundary]の部分はboundary=0に1を足してから処理するため、 swapdata(&data[1], &data[1]); となると思うのですが、そうすると同じデータを入れ替えることになってしまいます。 何が間違っているのか、教えていただけますでしょうか。

  • C言語でBMPファイルの内容を表示 その2(Unix使用)

    こんにちは。私は30代の男性です。 以前、コマンドラインで指定したBMPファイルの中身をバイナリ形式で読み込むということにチャレンジして、とりあえずBITMAPFILEHEADER構造体の中の情報を引き出すことには成功しました。 ※以前の質問とご回答 → http://okwave.jp/qa2837931.html fread関数を使ってoffsetという情報(ファイル先頭から画像データまでのバイト数)を取り出すことができたので、あとはBMPファイルの先頭アドレスからoffsetのバイト数分だけ進んだ箇所からデータを取り出して出力すれば、数値として格納されている画像データが引き出せると思ったのですが、うまくいきません。 どのようにアドレスを指定すれば、バイナリ形式の画像データを表示できるのでしょうか?宜しくお願い致します。