• ベストアンサー

C++で2つ質問です

C++で2つ質問です。 char str[1]; これは char str; と同じですか? memcpy(str,NULL,0); これはやってもよいと定義されていますか?

  • inugh
  • お礼率36% (11/30)

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

  • ベストアンサー
回答No.4

【char str と char str[1]】 まず、 char str; char str[1]; は、前者は char 型の単純変数。後者は、char 型の入れるなので、異なるものです。 実体としては、いずれも char 1 個分の変数領域を占めてそれ自体は(それぞれ、str と str[0]として)同様に扱えるので、その意味では同じとも言えます。 また、 char str[1]; としたときの、str は、あくまでも「配列名」です。 C言語では、「多くの文脈では、配列名は配列の先頭アドレスに暗黙のうちに変換される」ので、配列とポインタの混乱というのはおきやすいのですが、本来別のものです。 これがよくわかるのは、 char array[1]; char *pointer; という2つがあったとして、 1. sizeof array は、'1' です。 sizeof pointer は、ポインタのサイズで、おそらく、1ではありません。 2. &array は、array の先頭アドレスです   (多くの文脈では、arrya を &array に書き換えても、「最終的には」同じ動作をします) &pointer は、pointer という変数自体をポイントするアドレスです。   (多くの文脈では、pointer を &pointer に書き換えると、コンパイルすら通らないでしょう) 【memcpy(str, NULL, 0)】  これは、「やってはいけない」という記述を見つけることは出来ませんでした。  しかし、こういうことを考えてみるのは、無意味ではありません。直接上記の記述をすることはないと思いますが、 memcpy(dest, src, size);  と記述した中で、たまたま、dest が NULL だったとか、src が NULL だったとかという際の挙動を把握しておくのは、不具合の解析に役立ちます。  また、厳密に言えば、NULL が、「0番地」である保証はありません。(逆に、定数 0 をアドレスに変換すると、NULL になることは保証されています)  あくまでも、「そのポインタは、『何もポイントしていない』ということを示す定数」です。 【エラーにならなかったので……】 コンパイラが「エラー」と表示するのは表面的なことに過ぎません。今回の例でいえば、memcpy の引数が、順に、「何かのアドレス、何かのアドレス、サイズを表す数値(size_t)」であることをチェックしているだけです。 コンパイラは、その意味までは関知しません。

inugh
質問者

お礼

ありがとうございました。

その他の回答 (4)

  • matsu-td
  • ベストアンサー率31% (5/16)
回答No.5

#2です。 > 前者では、strは配列の先頭アドレスを表すポインタです。 この表現は間違いでした。AsanoNagiさんの説明が正しいです。訂正させて頂きます。

  • matsu-td
  • ベストアンサー率31% (5/16)
回答No.3

> エラーになればやっていけないことと分かりますが > エラーにならなかったので定義を知りたいです。 エラーというのはコンパイルエラーということですか? コンパイルエラーは出ないと思います。文法的に間違っていないからです。 例えば、 memcpy (NULL, str, 1); これもコンパイルエラーは出ませんが、絶対にやってはいけません。memcpyの仕様としてやってはいけないと定義されているのではなく、0番地を上書きしてしまうからやってはいけないのです。これはC/C++プログラミングでは常識ですね。繰り返しますが、「常識」であって、「仕様」または「定義」ではありません。memcpyは、与えられたパラメータで言われた通りに動くだけで、それをどう使うかはプログラマに責任があるのです。 また、inughさんの例では、実行時でも問題なく動作すると思います。なぜなら、書き込みバイト数が0バイトなので、実質的には何も行われないからです。 memcpy(str,NULL,0); は、全く意味の無いプログラムです。 memcpy()を自作してみると、分かるかもしれませんね。

inugh
質問者

お礼

ありがとうございます。 2つ目の質問の回答は、やってもよいと定義されていないが、やってはいけないという定義は見たことがない。 ということでしょうか? No.7の回答まで目を通しました。 memcpy(str,NULL,0);はやってはいけないということはなさそうですね。 エラーというのがコンパイルエラーなのか実行エラーなのかを答えても意味がありません。 それがどっちのエラーであろうとエラーが起こらなかったわけですから定義を知る必要があります。

  • matsu-td
  • ベストアンサー率31% (5/16)
回答No.2

1. char str[1]; と char str; では、strというシンボルの扱いが変わります。 前者では、strは配列の先頭アドレスを表すポインタです。後者では、char型変数の実体になります。 前者をchar型の変数として扱う場合は、str[0]としなければいけません。 以下のようなプログラムは、コンパイルできません。 char str1[1]; char str2; str1 = str2; // 型が違うので、代入できない。 2. やって良いかどうかは知りませんが、 「strの指すアドレスに、0番地(NULL)から始まるデータを0バイト分コピーする」という意味になると思いますし、プログラムはその通り動くと思います。 結果どうなるかは、実際に試してみるのが一番良いと思います。

inugh
質問者

お礼

ありがとうございます。 1は分かりました。 2はエラーになればやっていけないことと分かりますがエラーにならなかったので定義を知りたいです。

  • xcrOSgS2wY
  • ベストアンサー率50% (1006/1985)
回答No.1

(1) char str[1]; と char str; は違います。 (2) 「やってはならぬ」とは定義されていません。

関連するQ&A

  • C言語のmemcpy関数について質問します。

    C言語のmemcpy関数について質問します。 以下のプログラムを作成して実行したのですが期待した動作が行えません。 どなたか原因が分かる方がいましたらアドバイスいただければと思います。 (期待する動作) STR_Bに"aaaa"を格納したい (現在の動作) STR_Bに"aaaabbbb"が格納される。 (printfで"aaaabbbb"が表示される) (以下プログラムです) char *STR_A = "aaaabbbb"; char *STR_B; /* 初期化 */ memset(&STR_B,0x00,sizeof(STR_B)); /* 文字列コピー */ memcpy(&STR_B,&STR_A,4); /* コピーした文字の表示 */ printf("STR_B = %s\n",STR_B);

  • C++での戻り値について

    C++で以下のソースを書きました。 どうしてaaaは問題ないのにbbbはだめなのかがわかりません。 どちらも、func1()、func2()で設定した文字列・vectorのポインタを返したいです。 int main() { const char* aaa = NULL; std::vector<const char*>* bbb = NULL; aaa = func1(); bbb = func2(); } const char* func1() { const char* str = NULL; str = "test"; return str; } std::vector<const char*>* func2() { std::vector<const char*>* str2 = NULL; str2->push_back("test2"); str2->push_back("test3"); return str2; } 現在必要に迫られてC++勉強中です。よろしくお願いいたします。

  • 文字列strの中から文字cを探すプログラム(C言語)がわからない

    文字列strの中から文字cを探すプログラム(C言語)がわからない 柴田望洋さんの「[新版]明解C言語」という本の演習11-2なんですがどうしてもわかりません。間違いは無いと思うのにコンパイルすると警告を吐かれます。 僕が書いたプログラムを載せます。 /* 文字列strの中に、文字cが含まれていれば(複数ある場合は、最も先頭側とする)、 その文字へのポインタを返し、含まれていなければNULLを返す関数 char *str_chr(const char *str, int c) {} を作成せよ。 */ #include<stdio.h> char *str_chr(const char *str, int c){ while(*str){ if(*str==c) return str; str++; }     return NULL; } int main(){ char *str; char c; scanf("%s",str); scanf(" %c",c);     printf("%d",str_chr(str,c)); return 0; } コンパイラは「関数str_chrのif分の中のreturn strの型変換に問題がある」と言っているんです。 型変換はしるつもりは無いのにコンパイラはなぜそのように認識するのでしょうか。 またネット答えを探しましたがどうやらこのreturn strの部分はreturn (char*)strが正解のようです。意味がわかりません。strはポインタなのになぜまたわざわざchar型に変換しているのですか?といか(char*)の意味が根本的にわかりません。 質問ばかりですみません。初心者でポインタがどうにも理解できないんです。 誰か詳しい人教えてください。 お願いします。

  • C言語 ポインタと配列

    #include <stdio.h> /* scanf("%c", &search); ではなく scanf(" %c", &search); であることに注意する */ char *str_chr(const char *str, char c) { char *find; find = NULL; do { if(*str == c) { find = (char*)str; break; } } while(*str++); return(find); } int main(void) { char str[100] = {0}; char search; char *find; printf("文字列を入力してください:"); scanf("%s", str); printf("検索する文字を入力してください:"); scanf(" %c", &search); find = str_chr(str, search); if(find == NULL) { puts("検索した文字は見つかりませんでした。"); } else { printf("検索した文字 %c は\"%p\"にあります。\n", *find , find); } return(0); } このコードのfind = (char*)str;の (char*)str;の部分がどうなっているのかわかりません。 あとこのfindというのは&find[0]という解釈でいいでしょうか? 教えてくださいm(_ _ )m

  • C言語のint型の配列が分かりません

    #include<stdio.h> int main(void) { int str[ ]={0,1,2} printf("%s\n", str); return 0; } というプログラムをC言語でつくってみましたが動きません.(012と表示されて欲しかったのですが) int str[ ]={1,2,3}の部分をchar str[ ]={'0','1','2'}とすれば動きます. そこで質問なのですが, printf("~%s~", (配列名));  はchar型の配列にしか適応できないのですか? ※追記 puts関数の定義は int puts (const char *str); であるそうなので char型の仮引数にはchar型のアドレスを渡さなければいけません. ではprintf関数の定義は一体どんなものなのですか?

  • C言語 文字列操作

    トリム関数とリムーブ関数を作成してみました。改良点はありますでしょうか? ~~~~以下ソース~~~~ #include <stdio.h> #include <stdlib.h> #include <string.h> char *Trim(char *str); char *Remove(char *str, char *rmv); void main(void) {  char str[10], rmv[10], *p;  int c;  /* " abcd "をトリムする */  strcpy(str, " abcd ");  printf("トリム前 |%s|\n", str);  p = Trim(str);  printf("トリム後 |%s|\n", str);  /* 指定文字列を削除する */  printf("削除する文字列を入力してください :");  scanf("%s", rmv);  Remove(str, rmv);  printf("削除後 |%s|\n", str);  exit(0); } char *Trim(char *str) {  char space[] = " ";  char null[] = "";  int index = 0;  while(1){   if(strcmp(&(str[index]), null) == 0){    index--;    if(strncmp(&(str[index]), space, 1) == 0){     strcpy(&(str[index]), &(str[index]) + 1);    }else{     break;    }   }else{    if(strncmp(&(str[index]), space, 1) == 0 && index == 0){     strcpy(&(str[index]), &(str[index]) + 1);    }else{     index++;    }   }  }  return str; } char *Remove(char *str, char *rmv) {  int c, size, i;  char *p;  c = '\0';  p = strchr(rmv, c);  size = p - rmv;  for(i = 0; i < size; i++){   c = (int)rmv[i];   p = strchr(str, c);   if (p != NULL) {    strcpy(&(str[p-str]), p + 1);   }   else{    printf("""%c""は見つかりませんでした\n", c);   }  }  return str; }

  • 前回質問させて頂き、改善できるところはしました。

    前回質問させて頂き、改善できるところはしました。 再度駄目な所をご指摘お願いします。 仕様は出来るだけ標準関数に近いものを自作したいと思っています。 //---------------------------- MyStrstr ----------------------------------// char *MyStrstr(const char *str1, const char *str2) { const char *p1 = str1; const char *p3 = str1; const char *p2 = str2; while(*p1 != *p2 && *p1 && *p2) { *p1++; *p3++; } while(*p1 == *p2){ *p1++; *p2++; } return (*p2 ? NULL : (char*)p3); } //------------------------------ 終了 ------------------------------------// //---------------------------- MyStrcat ----------------------------------// char *MyStrcat(char *str1, const char *str2) { char *p1 = str1; const char *p2 = str2; while(*p1 != NULL){ p1++; } if(*p2){ while(*p2 != NULL){ *p1 = *p2; p1++; p2++; } } if(*p2 == NULL){ *p1 = *p2; } return p1; } //------------------------------ 終了 ------------------------------------// //---------------------------- MyStrcmp ----------------------------------// int MyStrcmp(const char *str1, const char *str2) { const char *p1 = str1; const char *p2 = str2; while(*p1 != NULL || *p2 != NULL){ if(*p1 == *p2){ p1++; p2++; }else{ if(*p1 < *p2) return -1; if(*p1 > *p2) return 1; } } return 0; } //------------------------------ 終了 ------------------------------------// //----------------------------- MyMemcpy -----------------------------------// void *MyMemcpy(void *str1, void *str2, size_t n) { char *p1 = (char*)str1; char *p2 = (char*)str2; while(n--){ *p1 = *p2; p1++; p2++; } return str1; } //------------------------------ 終了 ------------------------------------// //---------------------------- MyStrcpy ----------------------------------// char *MyStrcpy(char *str1, const char *str2) { char *p1 = str1; const char *p2 = str2; while( *p2 != NULL){ *p1 = *p2; p1++; p2++; } if(*p2 == NULL){ *p1 = *p2; } return p1; } //------------------------------ 終了 ------------------------------------// //---------------------------- MyMe

  • C言語、fgetcを利用しファイルの内容を変数に

    C言語について質問です C言語のfgetcを利用しファイルの内容を変数にいれてそれを返す関数を作っているのですがうまくいきません <!--以下ソース--> char *file_get_contents(char *filename){ FILE *fp; int c; char *return_str; if((fp=fopen(filename,"r"))==NULL) return NULL; while((c=fgetc(fp))!=EOF ){ sprintf(return_str,"%c",c); } fclose(fp); return return_str; } 誰か理由と改善方法を教えてください!

  • メモリ上に配置されていないバイト配列を代入

    memcpyでメモリ上に配置されていないバイト配列を代入する方法を教えて下さい。 #include<iostream.h> int main() { char str[3]; memcpy(str,"aa",3); printf("%s",str); return 0; } これは分かるんですが、バイト配列では"aa"のところに何を書けばいいんですか? memcpy(str, (char *){0x61,0x61,0x0}, 3);ではコンパイルできませんでした。 str[0]=0x61;のように1つずつなら代入できるんですが、配列ごとの方法が分かりません。

  • Cでは文字列をどのように認識するのでしょうか?

    C言語には文字列型というものは存在しないと教わりました。 文字列の終わりはヌル文字で認識できますが、 文字列型というものが存在しないのに何故次のバイトを読もうとするのですか? たとえばchar str[] = "abc";、あるいはchar *p = "abc";とあったとして printf(str);あるいはprintf(p); でなぜabcが出力されるのでしょうか?なぜaの次にbがbの次にcがあるとわかるのでしょうか? char型で先頭アドレスが渡された場合、ヌル文字を見つけるまでアドレスをインクリメントし続けるという決まりでもあるのでしょうか? それに文字列型というものが存在しないなら''と""を分ける意味もないのでは??

専門家に質問してみよう