16バイトアライメントで要素へのポインタ

このQ&Aのポイント
  • C言語で16バイトアライメント配置された要素へのポインタの処理方法について教えてください。
  • 1MBの範囲にある要素へのポインタを16ビットで記憶する方法をC言語で実現できるでしょうか。
  • C言語で、要素へのポインタを16バイトアライメントで配置し、指定の条件でアセンブラコードが生成できる方法を教えてください。
回答を見る
  • ベストアンサー

16バイトアライメントで配置された要素へのポインタ

非常にトリッキーな質問です。標準のC言語で記述可能かもわかりません。もし可能なら教えてください。 16バイトアライメント配置された要素の先頭アドレスは下位4ビットが0となっています。 そこで、1MBの範囲にある要素へのポインタは本来20ビット必要ですが、下位4ビットが常に0なので、上位16ビットを記憶するだけで十分のはずです。アセンブラで記載すれば可能ですが、Cで、「使うときは、4ビット右シフト」「記憶するときは、上位16ビット」となるようなコードを出力するような記載がわかりません。具体的には、以下のようなアセンブラ結果が希望です。 struct ABC {  struct ABC *next;  int x,y,z;   : } *p,*q; のとき、 q = p->next; は mov ecx,dword ptr p mov eax,word ptr [ecx] slr eax,4 // 使うときは4ビット右シフト mov q, eax q->next = p; は mov ecx,dword ptr q mov eax,p slr eax,4 // 構造体ABCに保存する場合には4ビット左シフト mov word ptr [ecx],eax となってほしいです。

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

  • ベストアンサー
  • mtaka2
  • ベストアンサー率73% (867/1179)
回答No.2

struct ABC {  unsigned short next;  int x,y,z;   : } *p,*q; としておいて、 next を「(struct ABC*)NULL を基点とした長大な配列」へのインデックス として取り扱う、というのはどうでしょうか。 同型のポインタ同士の引き算は要素サイズ単位での差に、 ポインタと整数の足し算は、要素サイズ単位での実アドレス計算になりますから、 q = p->next; の代わりに q = ((struct ABC*)NULL) + p->next; q->next = p; の代わりに q->next = p-((struct ABC*)NULL); になります。 #define PointerToIndex(PTR) (PTR-((struct ABC*)NULL)) #define IndexToPointer(IDX) (((struct ABC*)NULL)+IDX) とでもしておけば、 q=IndexToPointer(p->next); q->next=PointerToIndex(p); と書けます。

usatan2
質問者

お礼

回答ありがとうございます。 インデックスとして扱うというのはすばらしいアイデアですね。 これならば、16バイトアライメントでなくても、使えますよね。 参考になりました。

その他の回答 (1)

  • trapezium
  • ベストアンサー率62% (276/442)
回答No.1

メモリ管理自前でやってるシステムだと余ったビットを流用とかは、割とやってた。例えば sizeof(struct ABC *)==4 なシステムなら丁度 16bit 浮くので、 struct ABC {  unsigned short next; short padding; /* 穴埋め、他に流用可能みたいな */  int x,y,z;   : } *p,*q; q = (struct ABC *)((unsigned long)p->next << 4); q->next = (unsigned short)((unsigned long)p >> 4); もっとも eax とか書いてるのを見ると、そもそも 20bit なんたらの前提条件が怪しいけど。

usatan2
質問者

お礼

早速の回答、ありがとうございます。 >もっとも eax とか書いてるのを見ると、そもそも 20bit なんたらの前提条件が怪しいけど。 eaxはあくまで説明用です。実際は組み込み機器のCPUなので、アセンブリコードはまったく別ものです。

関連するQ&A

  • x64移行でのインラインアセンブラ修正

    Win32のプログラムをx64に移行する仕事を頼まれました。 プラットフォームをx64に移してビルドを行ったところ、 インラインアセンブラを使用していた以下のコードでエラーが発生しました。 ~前略~ __asm { mov eax, 0; /* EAXに0を入れる */ cpuid; /* CPUID実行 */ mov dword ptr [vender_sig + 0], ebx; /* 最初の4文字 */ mov dword ptr [vender_sig + 4], edx; /* 次の4文字 */ mov dword ptr [vender_sig + 8], ecx; /* 最後の4文字 */ mov byte ptr [vender_sig + 12], 0; /* ラストに \0 */ } ~中略~ __asm { mov eax, 1; /* EAXに1を入れる */ cpuid; /* CPUID実行 */ mov dword ptr [prosessor_sig], eax; /* プロセッサシグネチャ */ mov dword ptr [function_flag], ebx; /* ファンクションフラグ */ mov dword ptr [feature_flag2], ecx; /* Featureフラグ */ mov dword ptr [feature_flag1], edx; /* Featureフラグ */ } ~中略~ __asm { mov eax, 1; cpuid; /* CPUID実行 */ mov dword ptr [t], eax; /* top */ mov eax, 3; cpuid; /* CPUID実行 */ mov dword ptr [m], edx; /* middle */ mov dword ptr [b], ecx; /* bottom */ } ~後略~ x64ではインラインアセンブラが使用できないということはわかったのですが、 これをどう直していいかがわかりません。 アドバイスよろしくお願い致します。

  • VC2008のアセンブラ出力

    Visual C++ 2008 Express Editionで勉強をしています。 Cで簡単なプログラムを作り、そのアセンブラ出力を見ているのですが、下のアセンブラリストの###部のようなCの記述と関係ないコードが追加されます。この行を削除してアセンブルしても動作に問題ないように見えます。 このコードが追加される意味を教えて下さい。名前からセキュリティに関係しそうですが…… またプログラムによって追加される場合と、されない場合があります。何故でしょう? ご教示いただければ幸いです。 ----scanf.c #include <stdio.h> #include <stdlib.h> int main(void) { int i; char buf[256]; scanf("%s",buf); i = atoi(buf); printf("%d\n",i); return 0; } ----- -----scanf.asm ; Listing generated by Microsoft (R) Optimizing Compiler Version 15.00.30729.01 TITLE D:\vc_asm\scanf.c .686P .XMM include listing.inc .model flat INCLUDELIB LIBCMT INCLUDELIB OLDNAMES _DATA SEGMENT $SG3702 DB '%s', 00H ORG $+1 $SG3703 DB '%d', 0aH, 00H _DATA ENDS PUBLIC __$ArrayPad$ PUBLIC _main EXTRN _printf:PROC EXTRN _atoi:PROC EXTRN _scanf:PROC EXTRN ___security_cookie:DWORD EXTRN @__security_check_cookie@4:PROC ; Function compile flags: /Odtp _TEXT SEGMENT _buf$ = -264 ; size = 256 __$ArrayPad$ = -8 ; size = 4 _i$ = -4 ; size = 4 _main PROC ; Line 5 push ebp mov ebp, esp sub esp, 264; 00000108H mov eax, DWORD PTR ___security_cookie ###この行 xor eax, ebp ###この行 mov DWORD PTR __$ArrayPad$[ebp], eax ###この行 ; Line 8 lea eax, DWORD PTR _buf$[ebp]  (中略  Cで記述した内容の動作が記載) ; Line 11 xor eax, eax ; Line 12 mov ecx, DWORD PTR __$ArrayPad$[ebp] ###この行 xor ecx, ebp ###この行 call @__security_check_cookie@4 ###この行 mov esp, ebp pop ebp ret 0 _main ENDP _TEXT ENDS END -----

  • メモリデータ比較中にハング

    メモリデータ比較中にハング A領域のデータとB領域のデータを先頭から比較するプログラムをアセンブラで組んでおります。 しかしかなりの頻度でプログラムがハングし、困っております。 ソースを眺めていても、どこが悪いのかわかりません。ハングの原因をうまく調べる方法はあるでしょうか? 動作環境:MS-DOS アセンブラ:MASM32 MOV ECX, srcAddr;Aのメモリアドレス PUSH ECX POP ESI MOV ECX, dstAddr;Bのメモリアドレス PUSH ECX POP EDI MOV ECX, CmpByte;比較するバイト数(比較するのは100000Hexバイト) XOR EAX, EAX CALL ENTER_PROTECT;プロテクトモードに移行 CALL GET_SORC_SEL;SORC_SELのBASE AddressとLimitを変更 CALL GET_DEST_SEL;DEST_SELのBASE AddressとLimitを変更 ; DWORD単位で比較するので2で割る SHR ECX, 2 @@compare: CLD ;CMPS*でのESI,EDIの変化を増加方向に @@cmp4: CMPSD DS:[ESI], ES:[EDI] ;4Byteずつ比較 jne @@err4 dec ECX jnz @@cmp4 ; 4で割った余り分のチェック MOV ECX, CmpByte AND ECX, 3 or ecx, ecx jz @@exit jmp @@cmp1 @@err4: ; 1Byteずつチェックしてコンペアエラーしたオフセットを割り出す LEA ESI,[ESI-4] ; コンペアエラー前のオフセットに戻す LEA EDI,[EDI-4] shl ecx, 2 @@cmp1: ;1Byteずつ比較 CMPSB DS:[ESI], ES:[EDI] jne @@exit dec ECX jnz @@cmp1 @@exit: ; コンペアしたByte数を計算(EAX <- CmpByte - ECX) MOV EAX, CmpByte SUB EAX, ECX PUSH EAX CALL FREE_SORC_SEL CALL FREE_DEST_SEL CALL LEAVE_PROTECT POP AX POP BX MOV DX, BX RET _MemCmp2 ENDP

  • ポインタの扱い

    すみません、構造体へのポインタの配列の扱いに困っています。 下記ソースの struct list *hashtable[HASHSIZE]; の箇所をmain部に入れた場合の処理がうまくいきません。。 どのように修正すれば良いのでしょうか。 申し訳ありませんが教えてください。 --------------------- #include <stdio.h> #include <stdlib.h> #include <string.h> #define HASHSIZE 40 #define MAX_KW_LEN 256 #define NUM_KW 23 #define TRUE 1 #define FALSE 0 struct list { char keyword[MAX_KW_LEN]; struct list *next; }; struct list *hashtable[HASHSIZE]; static char kw[NUM_KW][MAX_KW_LEN] = { "auto", "break", "double", "enum", "char", "continue", "extern", "float", "for", "int", "long", "register", "short", "signed", "static", "struct", "typedef", "union", "unsigned", "return", "void", "volatile", "while" }; int Hash(char *key); void InitHTable(void); int FindKeyWord(char *key); void ListKeyWord(void); void FreeKeyWord(void); int main(void); int Hash(char *key) { int hashval = 0; while (*key != '\0') hashval += *key++; return (hashval % HASHSIZE); } void InitHTable(void) { int i; struct list *p, *q; int hashval; for (i = 0; i < NUM_KW; i++) { printf("%d\n",i); if ((FindKeyWord(kw[i])) == FALSE) { if ((p = (struct list *)malloc(sizeof(struct list))) == NULL) { fprintf(stderr, "メモリ不足です。\n"); exit(2); } strcpy((*p).keyword, kw[i]); hashval = Hash(kw[i]); if (hashtable[hashval] == NULL) { hashtable[hashval] = p; p->next = NULL; } else { q = hashtable[hashval]; while (q->next != NULL) { q = q->next; } q->next = p; p->next = NULL; } } } } int FindKeyWord(char *key) { struct list *p; for (p = hashtable[Hash(key)]; p != NULL; p = p->next) if (!strcmp(key, (*p).keyword)) return (TRUE); return (FALSE); } void ListKeyWord(void) { int i; struct list *p; for (i = 0; i < HASHSIZE; i++) for (p = hashtable[i]; p != NULL; p = p->next) printf("予約語:%s ハッシュ値:%d:\n", (*p).keyword, Hash((*p).keyword)); } void FreeKeyWord(void) { int i; struct list *p, *q; for (i = 0; i < HASHSIZE; i++) for (p = hashtable[i]; p != NULL; ) { q = p->next; free(p); p = q; } } int main(void) { char word[MAX_KW_LEN]; int i; InitHTable( ); ListKeyWord( ); for (i = 0; i < 4; i++) { printf("Cの予約語を入力して下さい "); fgets(word, 128, stdin); if ((FindKeyWord(word)) == TRUE) printf("%s は登録済みです。\n", word); else printf("%s は未登録です。\n", word); } FreeKeyWord( ); }

  • エンディアン反転まわりの速度関係

    エンディアンの反転を高速でやる方法を模索していたのですが コンパイラはVC++2008EE OSがWindowsXP SP2 32 bitで 最適化オプションなしでやれば DWORD d=242397; __asm { mov eax, d bswap eax mov d, eax } のほうが DWORD d=242397; d=_byteswap_ulong(d); よりも4倍程度速かったのですが 実行速度 (/O2) または 最大限の最適化 (/Ox) の最適化オプションだと 後者の方が前者より3倍ほど速く実行されました。 そこでbswapではなくビットシフトを使ったら d = ((d & 0xff000000) >> 24) | ((d & 0x00ff0000) >> 8 ) | ((d & 0x0000ff00) << 8 ) | ((d & 0x000000ff) << 24); これでほぼ同じ速度になりました。 これらの速度関係は、Windowsでは比較的安定しているのでしょうか? それともWindowsでもまちまちなのでしょうか?

  • 構造体へのポインタ

    すみません、構造体へのポインタの配列の扱いに困っています。 下記ソースの struct list *hashtable[HASHSIZE]; の箇所をmain部に入れた場合の 他の関数内での使用の仕方が全くわかりません。 どのように修正すれば良いのでしょうか。 申し訳ありませんが教えてください。 --------------------- #include <stdio.h> #include <stdlib.h> #include <string.h> #define HASHSIZE 40 #define MAX_KW_LEN 256 #define NUM_KW 23 #define TRUE 1 #define FALSE 0 struct list {  char keyword[MAX_KW_LEN];  struct list *next;   /* 次の list へのポインタ */ }; struct list *hashtable[HASHSIZE]; /* ハッシュテーブル */ /* キーワード ( Cの予約語 ) */ static char kw[NUM_KW][MAX_KW_LEN] = {   "auto", "break", "double",   "enum", "char", "continue", "extern", "float", "for", "int",   "long", "register", "short", "signed", "static",   "struct", "typedef", "union", "unsigned", "return",   "void", "volatile", "while" }; int Hash(char *key); void InitHTable(void); int FindKeyWord(char *key); void ListKeyWord(void); void FreeKeyWord(void); int main(void); int Hash(char *key) {  int hashval = 0;  while (*key != '\0')   hashval += *key++;  return (hashval % HASHSIZE); } void InitHTable(void) {  int i;  struct list *p, *q;  int hashval;  for (i = 0; i < NUM_KW; i++) {   printf("%d\n",i);   if ((FindKeyWord(kw[i])) == FALSE) { /* 登録されていなかったら */             /* メモリを割り付ける */    if ((p = (struct list *)malloc(sizeof(struct list))) == NULL) {     fprintf(stderr, "メモリ不足です。\n");     exit(2);    }    strcpy((*p).keyword, kw[i]);    hashval = Hash(kw[i]);    /* ハッシュ値を求めて */        if (hashtable[hashval] == NULL) { /* 未登録なら */     hashtable[hashval] = p;  /* p の指すアドレスを登録 */     p->next = NULL;    /* リストの末尾に NULL を追加 */    }    else {        /* 既に登録していたら */     q = hashtable[hashval];     while (q->next != NULL) {  /* データがなくなるまで */      q = q->next;    /* リストをたどる */     }     q->next = p;     /* リストの末尾に p の指すアドレスを登録 */     p->next = NULL;    /* その末尾に NULL を追加 */    }   }  } } int FindKeyWord(char *key) {  struct list *p;  for (p = hashtable[Hash(key)]; p != NULL; p = p->next)   if (!strcmp(key, (*p).keyword))  /* 登録済みなら */    return (TRUE);     /* TRUE を返す */   return (FALSE);      /* 未登録ならFALSE を返す */ } void ListKeyWord(void) {  int i;  struct list *p;  for (i = 0; i < HASHSIZE; i++)   for (p = hashtable[i]; p != NULL; p = p->next) /* p が NULL でなければ */               /* ハッシュ値とキーワードを表示 */    printf("予約語:%s ハッシュ値:%d:\n", (*p).keyword, Hash((*p).keyword)); } /* malloc( ) で割り付けたメモリを解放 */ void FreeKeyWord(void) {  int i;  struct list *p, *q;  for (i = 0; i < HASHSIZE; i++)   for (p = hashtable[i]; p != NULL; ) { /* p が NULL でなければ */    q = p->next;      /* p->next を保存 */    free(p);       /* メモリを解放 */    p = q;        /* p->next を p に代入 */   } } int main(void) {  char word[MAX_KW_LEN];  int i;  InitHTable( );  ListKeyWord( );  for (i = 0; i < 4; i++) {   printf("Cの予約語を入力して下さい ");   fgets(word, 128, stdin);   if ((FindKeyWord(word)) == TRUE)    printf("%s は登録済みです。\n", word);   else    printf("%s は未登録です。\n", word);  }  FreeKeyWord( ); }

  • 配列変数のポインターが勝手に変わる

    下記の2個のファイルを持つプログラムでインラインアセンブラのcall命令で配列変数のポインター(アドレス)が勝手に変わる現象をおしえてください。 但し、配列の最初のポインターのみが変わる。 開発環境はWin7(64bit)、VC++2010無償版です。 main.cpp int *disp; int data[3]; WinMain() { _asm mov disp,offset disp_top ・ ・ ・ メッセージループへ _asm{ disp_top://下記move()からcallされた時 lea eax,data//下記のmove()関数のeaxの値より16番地少ない lea ebx,data+4//下記のmove()関数のebxの値と同じ lea ecx,data+8//下記のmove()関数のecxの値と同じ ・ ・ ・ ret   } } move.cpp extern int *disp; extern int data[3]; void move() { _asm{ lea eax,data lea ebx,data+4 lea ecx,data+8 call disp   } }

  • VC2005 プログラム領域への書込み制限について

    <前提> VC2005 で、以下のプログラムを実行すると、 エラーメッセージが出力されます。 <エラーメッセージ> test.exe の 0x004113c9 でハンドルされていない例外 が発生しました: 0xC0000005: 場所 0x004113cb に 書き込み中にアクセス違反が発生しました。 ----------- #include <stdio.h> void main(void) { unsigned long pc_pos; // PC位置をpc_posへ格納 __asm { CALL NXT NXT: POP pc_pos; } (*(unsigned char*)pc_pos) = 0x3F; return; } ----------- そこで、アセンブラで命令列をみると、 アドレス004113C9の場所で、発生しています。 ---------- // 今のPC位置を知るプログラムは? __asm { CALL NXT 004113BE E8 00 00 00 00 call NXT (4113C3h) NXT: POP pc_pos; 004113C3 8F 45 F8 pop dword ptr [pc_pos] } (*(unsigned char*)pc_pos) = 0x3F; 004113C6 8B 45 F8 mov eax,dword ptr [pc_pos] 004113C9 C6 00 3F mov byte ptr [eax],3Fh ※eaxレジスタには、0x004113c3 が格納されています ---------- さて、ここで質問です。 <質問> プログラム領域への書込みは、ライトプロテクトされています。 このライトプロテクトを一時的に外す方法はありますでしょうか。 つまり、0x004113c3アドレスに、3Fhを書き込んでも、 エラーが発生しない方法が知りたいです。 > プログラム領域に対する書込み命令コード 004113C9 C6 00 3F mov byte ptr [eax],3Fh

  • このプログラムのポインタを配列に変換したいんですけど

    C言語初心者です。助けてください。 途中までがんばったのですがどうしてもできません。問題はこの構造体の*name→name[25],*email→email[30]に変換させたいのですが、そのまま変換すると左辺値が必要や移植性のないポインタ変換などのエラーが出てしまいます。どうすればいいですか?教えてください。あと関数getlineと構造体は変えないでください。(指定した部分は除く) このプログラムは名前、メールアドレス、年齢を保存していき-1を入力した時出力されるというものです。 よろしくお願いします。 入力例 10 hakata abc@・・・ 12 yokohama dfg@・・・ -1 -1 -1 出力例 -1 -1 -1 12 yokohama dfg@・・・ 10 hakata abc@・・・ #include <stdio.h> #include<stdlib.h> struct node { char *name; char *email; int age; struct node *next; } heap[100], *hp = heap; void getline(char *s,int n) { int c; while(--n>0&&((c=getchar())!=EOF && c!='\n')) *s++=c; *s='\0'; } char* dupstr(char* strg) { char* newstr; newstr=(char*)malloc(sizeof(char)*strlen(strg)); strcpy(newstr,strg); return newstr; } struct node *new(int n ,char* a, char* b) { hp->age=n; hp->email=a; hp->name=b; hp->next=NULL; return hp++; } struct node *add(struct node *p, struct node *q) { q->next=p; p=q; return p ; } void print_list(struct node *p) { while (p != NULL) { printf("%d ", p->age); printf("%s ", p->name); printf("%s ", p->email); p=p->next; } printf("\n"); } main() { int age,p; char buf[80],a[80],b[80],*email,*name; struct node *root=NULL; while(getline!=NULL) { getline(buf,sizeof(buf)); age=atoi(buf); getline(b,sizeof(b)); getline(a,sizeof(a)); email=dupstr(b); name=dupstr(a); root=add(root,new(age,email,name)); if(age==-1) break; } print_list(root); }

  • C言語 リスト

    (1) /* list.c */ #include <stdio.h> #include <stdlib.h> struct node { int num; struct node *next; }; main() { struct node *head, *p; int x; head = NULL; while(scanf("%d",&x) != EOF) { p = (struct node *)malloc(sizeof(struct node)); p->num = x; p->next = head; head = p; } // リストの要素のnumの値を先頭から順に表示する p=head; while(p!=NULL) { printf("%d\n" ,p->num); p = p->next; } } (2) struct node *q; q = head; while(q->next != NULL) q = q->next; (1)を(2)を使い新しいノードをリストの最後に追加するようにしたいのですが どう書いたら良いのか教えていただきたいです

専門家に質問してみよう