EAccessViolationクラス例外の原因と解決方法

このQ&Aのポイント
  • WindowsXPでBCB5を使用している場合、文字列の反転関数でEAccessViolationクラスの例外が発生することがあります。
  • この問題は、文字列の長さに関する不具合が原因で起こる可能性があります。
  • 解決方法としては、文字列の長さを適切に管理し、メモリのアクセスエラーを防ぐようにすることが重要です。
回答を見る
  • ベストアンサー

EAccessViolationクラス例外

WindowsXPでBCB5を使用しています。 柴田望洋氏著、新版明解C++入門編の演習8-14ですが 「文字列sの文字の並びを反転する関数str_rvsを作成せよ。 char* str_rvs(char* s); たとえば、文字列sが"abc"であれば、その文字列を"cba"に更新する。受け取ったsの値をそのまま返却すること。」 と言う問題で、下記の様に作成したんですが、 char* s="a"、または"ab"、または"abc"とすると正常に終了しますが "abcd"とすると、 文字列[abcd]を反転します。 dcba 文字列[dcba]を反転します。 abcd が繰り返された後、「EAccessViolationクラスの例外を生成しました。~」が表示されて停止します。 s="abcde"とすると 文字列[abcde]を反転します。 edcba と1回表示されて、同様のエラーが発生しますが、何が悪いのかわかりません。ご教授願います。 //--------------------- #include <iostream> #include <cstring> using namespace std; char* str_rvs(char* s){ int len=strlen(s); char* tmp; for(int i=0;i<len;i++) *(tmp+i)=*(s+len-1-i); *(tmp+len)=NULL; for(int i=0;i<len;i++) *(s+i)=*(tmp+i); return s; } int main(){ char* s="abcd"; cout << "文字列[" << s << "]を反転します。\n"; cout << str_rvs(s) << '\n'; //getchar(); }

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

  • ベストアンサー
noname#137556
noname#137556
回答No.5

> 文字列のコピーを作りたかったので、char* tmp="";等と初期化してやってみてどうしてもうまく行きませんでした ANo.4 でも書きましたが,この場合 tmp (が指している先)を書き換えてはいけません。 (const char* tmp=""; とすべき) コピー元の文字列(終端のナル文字含む)を格納するのに十分なサイズのコピー先を自分で用意してやる必要があります。 const char *src = "abcd"; // コピー元 char dst[256];         // コピー先 strcpy(dst, src);  // src (が指している先にある文字列)を dst にコピー

yoshiyossy
質問者

お礼

度々の回答ありがとうございます。 >ANo.4 でも書きましたが,この場合 tmp (が指している先)を書き換えてはいけません。 >(const char* tmp=""; とすべき) char dst[256]; strcpy(dst, src); とすると正常に目的の結果が得られました。   ポインタはこういうものだと理解しておきます。

その他の回答 (4)

noname#137556
noname#137556
回答No.4

他にもまずいところがあって,  char* s="abcd"; と宣言した場合,s の内容を書き換えてはいけません。  char s[] = "abcd"; なら OK。 # 文字列の先頭と末尾から,文字単位で入れ替えていけば, # 文字列のコピーを作らなくても大丈夫ですよ。

yoshiyossy
質問者

お礼

ありがとうございます。 文字列のコピーを作りたかったので、char* tmp="";等と初期化してやってみてどうしてもうまく行きませんでしたが、おっしゃられる通り char tmp; for(int i=0;i<=(len/2-1);i++){ tmp=*(s+i); *(s+i)=*(s+len-1-i); *(s+len-1-i)=tmp; } として、文字単位で入れ替えてみると正常に終了しました。

  • D-Matsu
  • ベストアンサー率45% (1080/2394)
回答No.3

tmpが参照するべき実体がありません。 実体がないので > *(tmp+i)=*(s+len-1-i); は「どことも知れないメモリ」にアクセスしようとします。結果としてOS側で不正なメモリアクセスを検知して例外を送出します。 あと、どうでもいい部類には入りますが、 > *(tmp+len)=NULL; 「ヌルポインタ(NULL)」と「ヌル文字('\0')」は(C++の範疇では)同じ値を示しますが、概念としては別モノです。 文字列終端のヌル文字の代用にNULLを使うのは避けた方がいいでしょう。

yoshiyossy
質問者

お礼

ありがとうございます。 >>「ヌルポインタ(NULL)」と「ヌル文字('\0')」は(C++の範疇では)同じ値を示しますが、 >>概念としては別モノです。 >>文字列終端のヌル文字の代用にNULLを使うのは避けた方がいいでしょう。 まだポインタの概念を良く理解してない為、参考にさせていただきます。

回答No.2

# ご参考: C++なんだからそんなものわざわざ自作せんでも... #include <iostream> #include <cstring> #include <algorithm> // 追加 using namespace std; char* str_rvs(char* s){  std::reverse(s, s+strlen(s)); // これでオシマイ  return s; } int main(){  char s[] = "abcd";  cout << "文字列[" << s << "]を反転します。\n";  cout << str_rvs(s) << '\n'; }

yoshiyossy
質問者

お礼

ありがとうございます。 勉強中なのでrevrese()は知りませんでしたが、うまく行きました。 revrese()無しでもやってみます。

  • osamuy
  • ベストアンサー率42% (1231/2878)
回答No.1

tmpが領域確保されてないのに、未初期化のままデレファレンスしてるからでは。 詳しいことは、他の人が回答してくれるかと。

yoshiyossy
質問者

お礼

ありがとうございます。他の人の回答も参考にさせていただきます。

関連するQ&A

  • sscanfでフォーマットに合っているかを調べるには。

    今,Cの関数で0-9,A-Zの4文字以内の文字列かどうか を調べたいのですが, int main() { int i; char str[] = "ABCD" /* 調べたい文字列1 */ char str1[]= "ABC@" /* 調べたい文字列1 */ char str2[]= "ABCDE" /* 調べたい文字列1 */ char *p; p = str; /* ここをstr1,str2に変える */ if (strlen(p) != 4) printf("エラーです\n"); for (i = 0; i < 4; i++) { if (!isupper(p[i]) && !isdigit(p[i])) { printf("えらーです\n"); return EXIT_FAILURE; } } printf("すべてOKです\n"); return EXIT_SUCCESS; } というようにすれば,strとstr1とstr2がフォーマットに合うかわかるのですが,これをsscanfの正規表現を使用して実装したいのですが,可能でしょうか。 int main() { int ret; char str[] = "ABCD" /* 調べたい文字列1 */ char str1[]= "ABC@" /* 調べたい文字列1 */ char str2[]= "ABCDE" /* 調べたい文字列1 */ char *p; chat tmp[30]; p = str; ret = sscanf(p, "%4[0-9A-Z]", tmp); if (ret != 1) { printf("エラーです\n"); return EXIT_FAILURE; } printf("すべてOKです\n"); return EXIT_SUCCESS; } といしょうとするならば,str1のときは,@までのABCの文字列がとれて,retには1が帰ってきてしまいます。 なにか4文字以内ということを実装できる正規表現の使い方はあるのでしょうか? ちなみに sscanf(str, "%1[0-9A-Z]%1[0-9A-Z]%1[0-9A-Z]%1[0-9A-Z]", tmp1, tmp2, tmp3, tmp4) という以外でわかる方,よろしくおねがいします。

  • 文字列の途中から途中までを抽出

    文字列の一部を抽出したいときのプログラムです。 絶対条件は ・文字列はchar*型 ・ある文字で区切られた区間を抽出 です。 以下のコードだとstrncpyの部分でsegmantation faultになってしまいます。 -------------------------- char array[10] = "abcd/efgh"; char* str0 char* str1; char* str2; int = len; str0 = array; len = strstr(str0, "/") - str0; strncpy(str1, str0, len); str2 = array + len + 1; -------------------------- 各変数が str1 = abcd str2 = fghi となるようにしたいです。 char str1[10];などにすればいいのですが、それ以外の方法でお願いします。

  • C++

    文字を入力して、それを1つ右にずらして出力する問題。 例  aret → tare 関数内でポインタを使用するのが条件です。 ズラしているつもりが、入力した文字がそのまま出力されてしまいます。 自分の方針としては、 関数内で入力された文字の長さを確認。 文字列の最後の文字をtmpに保存して ドンドン右にずらして保存していく。 最後にtmpに保存したものを、文字列の最初に入れる。 というものです。 わかるかた、ご指導お願いします。 #include<stdio.h> #include<string.h> void kaiten(char *t) { int len =0; int i; char tmp; while (*t++){ len++; } tmp = *(t+(len-1)); for(i=len-1;i>0;i--) *(t+i)=*(t+i-1); *t = tmp; } int main() { char s[100]; printf("文字列を入力:"); scanf("%s",s); kaiten(s); printf("一文字ずらすと:%s\n",s); }

  • 配列の渡し方と表示の仕方

    初心者です。 以下のメイン関数で実行をしたところ、 定義した文字列の最後の文字しか表示されません。 関数に上手く引数として配列が渡されていないか、 表示のさせ方が悪いと思うのですが、よく分かりません。 int main(void) { char str[CASENUM][10] = {'abcdef','abcde','abcd','abc'}; int startIndex[CASENUM] = {2,1,3,1}; char expected[CASENUM][10] = {'abcdef','abcde','abcd','abc'}; int i,m; char res; for(i = 0; i < CASENUM; i++) { printf("%c",str[m][i]); res = *subString(str[i],startIndex[i]); printf("実行結果:%c , 期待結果:%c\n", res, expected[i][10]); } return 0; } よろしくお願いします。

  • 文字列クラスを作りたいと思っています

    文字列クラスを作りたいと思っています。 以下のようなところまでは作れましたが、 エラーがでてしまいます。 どこかおかしいところがあるのでしょうか? *********************************************** #include<stdio.h> #include<string.h> class stt { public: char *str; int len; bool maked; stt::stt() { len=0; str=NULL; maked=false; } stt::~stt() { delete[] str; } stt &operator =(char *c) { if(maked) { delete[] str; maked=false; } len=strlen(c); str=new char[len]; strcpy(str,c); maked=true; return (*this); } virtual operator char*() { return str; } }; int main() { stt s; s="Hello World"; printf(s); getchar(); return 0; } ***********************************************

  • 配列の練習問題

    #include<iostream> using namespace std; //count関数の宣言 int count(char str[], char ch); int main() { char str[100]; char ch; cout << "文字列を入力して下さい。\n"; cin >> str; cout << "文字列から探す文字を入力して下さい。\n"; cin >> ch; int c = count(str, ch); cout << str << "の中に" << ch << "は" << c << "個あります。\n"; return 0; } //count関数の定義 int count(char str[], char ch) { int i = 0; int c = 0; while (str[i]) { if (str[i] == ch) c++; i++; } return c; } こんにちは。 この問題の解答のプログラムの意味がイマイチ解らないので良かったら教えて下さい。 確認がてらに質問します。 よろしくお願いします。

  • C言語で分からないところがあるのですが

    すみません。C言語のポインタで分からないことがあって来ました。 ポインタの理屈は理解してはいるのですが、いざソースコードを書いてみようということになると全く手がつけられずにいます。 以下のソースコードですが、strlen()と同じ働きをする関数mystrlen()と、strcmp()と同じ働きをする関数mystrcmpを、ポインタを使って作成するものです。どこをどうすればいいのか教えてくださいませんか。 #include <stdio.h> int main(void) { char str1[80], str2[80]; int i, j; int len1, len2; printf("第1の文字列を入力してください: "); gets(str1); printf("第2の文字列を入力してください: "); gets(str2); /* * 文字列の長さを確認する */ /* NULL文字(文字列の最後)まで読み飛ばす */ for (len1 = 0; len1 < 80 && str1[len1] != '\0'; len1++) ; /* ループ終了後、len1 に文字列の長さが入っている */ if (len1 < 80) { printf("%s は %d 文字の長さです\n", str1, len1); } else { printf ("第1の文字列が80字以上あります\n"); } /* str2 についても同様 */ for (len2 = 0; len2 < 80 && str2[len2] != '\0'; len2++) ; if (len2 < 80) { printf("%s は %d 文字の長さです\n", str2, len2); } else { printf ("第2の文字列が80字以上あります\n"); } if (len1 < 80 && len2 < 80) { for (i = 0; i < 80 && str1[i] != '\0' && str2[i] != '\0' && str1[i] == str2[i]; i++) ; if (str1[i] == str2[i]) { /* 両者同時に == '\0' のはず*/ printf("文字列は等しい\n"); } else if (str1[i] < str2[i]) { /* str1[i] == '\0' のはず*/ printf("%s は %s より小さい\n", str1, str2); } else { /* str2[i] == '\0' のはず*/ printf("%s は %s より大きい\n", str1, str2); } } /* * 十分なスペースがあれば、str2をstr1の最後に連結する */ if (len1 + len2 < 80) { /* str1 の末尾を探す */ for (i = 0; str1[i] != '\0'; i++) ; /* ループを抜けた段階では i は len1 と同じはずなので、 上記のループを作らず、i の代わりに len1 を用いるのも可 */ /* それ以降に str2 の中身をコピーする */ for (j = 0; str2[j] != '\0'; j++) { str1[i+j] = str2[j]; } str1[i+j] = '\0'; printf("%s\n", str1); } else { printf ("文字列をつなげた長さが80字以上あります\n"); } /* * str2をstr1にコピーする */ if (len1 + len2 < 80) { for (i = 0; str1[i] != '\0'; i++) { str1[i] = str2[i]; } str1[i] = '\0'; printf("%s %s\n", str1, str2); } return 0; }

  • メモリの解放について

    文字列の左右のスペースを削除する関数を作っています。 そこで引数の文字列と同じ領域のメモリを確保し、そこで一時的にスペースを削除する作業をしています。 作業が終わったら、引数のポインタに文字列をコピーし、作業領域を解放したいと思っています。 しかし、確保した領域を解放しようとすると、「セグメンテーション違反」なるエラーが出て、解放できません。どなたか分かる方ご教授お願いします。 void trim(char *str) { int i; int len; char *tmp_p, *save_p; tmp_p = (char *)malloc(strlen(str) + 1); strcpy(tmp_p, str); save_p = tmp_p; len = strlen(tmp_p); // 後方スペース削除 // ポインタを最後まで進める for(i = 0; i < len - 1; i++) { tmp_p++; } // 文字だった場合'\0'でターミネート while(tmp_p != save_p) { if(!isspace(*tmp_p)) { tmp_p++; *tmp_p = '\0'; break; } tmp_p--; } // 前方スペース削除 // ポインタを最初に戻す tmp_p = save_p; // 文字が出てくるまでポインタを進める while(isspace(*tmp_p)) tmp_p++; strcpy(str, tmp_p); // free(tmp_p); // <- セグメンテーション違反 }

  • 検索するプログラミング

    C言語の問題で、ユーザが文字列を入力し、半角英数字の入力(最大でも1000文字)を改行があるまで受け付ける。次に、ユーザに検索する文字列(最大でも半角20文字)を入力させ、はじめに入力された文字列内にあるかどうかを表示するプログラムを作成する。配列の外部を参照しないように注意する。そのプログラム内で以下の関数を完成させる。 int str_srch (char str [], char srch_str[]) : 文字列strの中に文字列srch_strが含まれていれば、1を返し、含まれていなければ0を返す関数とする。 このような感じになると思うのですがどなたか分かりませんか? int str_compare(char *s1, char *s2) { //文字列s1の先頭にs2が含まれていれば等しいければ1、 //そうでなければ0を返す //while文を利用して、s2の1文字目からs2がヌル文字になるまで //繰り返し、s1の対応する文字と等しいかどうかチェック //s2の最後の文字まで等しければ、1 //そうでなければ0を返す } int str_length(char *str) { //文字列strの長さを返す } int str_srch(char *str, char *srch_str) { int strlen, srch_strlen; int i,j=0; strlen =str_length(str); //文字列strの長さ srch_strlen =str_length(srch_str); //文字列srch_strの長さ for(i=0;i<strlen;i++){ //strのi文字目からにsrch_strがあるかどうか  j=j+str_compare(&str[i], srch_str) } //jは文字列srch_strが文字列strに何回含まれているか、を表す //jが1より多くなれば1を返し //jが0なら0を返す }

  • 文字列

    下のプログラムは何をするためのプログラムなのか教えてください。 個人的にはJISコードに関係していると思うのですがさっぱりわかりません。 どなたか詳しい説明お願いします。 #include <stdio.h> #define LEN 255 int main(void) { char str[LEN]; char cipher[LEN]; int ikey; int i,n; printf("文字列を入力せよ : "); scanf("%s", str); printf("鍵を入力 : "); scanf("%d", &ikey); i = 0; while (str[i]!='\0') { n = (str[i]-'A'+ikey)%26; cipher[i] = 'A'+n; ++i; } cipher[i] = '\0'; printf("%s\n", cipher); return 0; }