ページ 11

プログラム

Posted: 2009年1月08日(木) 18:29
by ざこ
C++について勉強していますが下記のプログラムがうまくいきません。
char rev_str(char *s1)
{
   char *s="water";
   int i,j;
 
   for(i=0;i<strlen(s);i++){
   *s++;
   }
 
   for(j=strlen(s);j=0;j--){
   *s1++=*s++;
   }

  return *s1;
}

int main()
{
  char s1[80];
  cout<<"waterを逆にすると"<<rev_str(s1)<<"です\n";

  return 0;
}
ある文字列を逆にして出力するプログラムをC++で書いています。
エラーはでませんがs1の中身がフフフフフフになってます。
原因と対策を教えて下さい。

Re:プログラム

Posted: 2009年1月08日(木) 18:31
by たかぎ
> s1の中身がフフフフフフになってます。

笑われていますね。

> 原因と対策を教えて下さい。

s1を未初期化のまま使用しているからです。
適切に初期化すれば改善します。

Re:プログラム

Posted: 2009年1月08日(木) 18:39
by 御津凪
> s1の中身がフフフフフフになってます。

たかぎさんと同意見です。凡ミスです。

> 原因と対策を教えて下さい。
原因はもう一つあり、 for 文の条件が j=0 となっているところです。
正しくは j != 0 ですよね?

対策として、こういう凡ミスは、条件文は左辺に定数が置かれるように書くと発見しやすいですよ。
(癖つけないと難しいですが)

0 = j はコンパイルエラーになりますから。

Re:プログラム

Posted: 2009年1月08日(木) 18:51
by ざこ
char s1[80]; の所初期化してchar s1[80]="";にしてもさっぱり・・

Re:プログラム

Posted: 2009年1月08日(木) 19:05
by non
初期化という問題ではなく、考え方に問題があるのでは?
>char rev_str(char *s1)
s1に答えを入れたいのなら、returnの必要ないし、
returnで返したいなら、
char *rev_str(void)
です。
でも、元の文字列を関数内で定義するなら、関数を作った
意味がないですよね。

Re:プログラム

Posted: 2009年1月08日(木) 19:10
by dic
引数とポインタとローカル変数の有効範囲を理解していませんね

Re:プログラム

Posted: 2009年1月08日(木) 19:18
by toyo
for(i=0;i<strlen(s);i++){
       *s++;
   }

*s++;

s++;
でも同じですしむしろ
s += strlen(s);
ですみます。
そしてsを文字数分インクリメントした後にstrlen(s)を使ってますがこの値は不定になります。

Re:プログラム

Posted: 2009年1月08日(木) 19:24
by toyo
for(i=0;i<strlen(s);i++){
       *s++;
   }
あー
これだとループのたびにstrlen(s)が変化しますね
どうしてもループでやるなら
int len = strlen(s);
   for(i=0 ;i < len; i++){
       s++;
   }
のようにしないと

Re:プログラム

Posted: 2009年1月08日(木) 19:26
by たかぎ
すみません。
よく見ていませんでした。
問題は他にありますね。

Re:プログラム

Posted: 2009年1月08日(木) 19:28
by non
>char *rev_str(void)
ごめん。間違えたので訂正。
char *rev_str(char *s1)
でした。

Re:プログラム

Posted: 2009年1月08日(木) 19:40
by dic
独自にはできますが色々アルゴリズムが難解なやり方をやってらしているので
シンプルなのをひとつ
#include <iostream>
using namespace std;


char*	rev_str(char *s)
{
	unsigned int	len, i;

	char	temp[80];

	len = strlen(s);

	for( i=0; i<strlen(s); i++ ) {
		temp[len] = s;
		len--;
	}

	return s;
}

int main()
{
  char s[80] = "water";
  cout<<"waterを逆にすると"<<rev_str(s)<<"です\n";

  return 0;
}

Re:プログラム

Posted: 2009年1月08日(木) 19:50
by Mist
正しいソース書いてみましたのでどこが違うか確認してみてください。
ポインタと文字列の扱いに対する理解が足りていないと思います。
#include    <iostream>
using namespace std;

char    *rev_str(char *s1)
{
    char            *s = "water";
    unsigned long   length, loop;
    char            *ptmp = s1;

    length = strlen(s);
    s += (length - 1);
 
    for(loop = length; loop > 0; loop--, s1++, s--) {
        *s1 = *s;
    }

    return  ptmp;
}

int main()
{
    char s1[80];

    memset(s1, 0, sizeof(s1));

    cout << "waterを逆にすると" << rev_str(s1) << "です" << endl;

    return  0;
}

Re:プログラム

Posted: 2009年1月08日(木) 19:51
by Mist
かぶったか(^-~;

Re:プログラム

Posted: 2009年1月08日(木) 19:51
by dic
すいません
文字転換しただけで後処理してませんでした
#include <iostream>
using namespace std;


char*	rev_str(char *s)
{
	unsigned int	len, i;

	char	temp[80];

	memset( temp, 0, sizeof(temp) );	//	忘れ物

	len = strlen(s) - 1;

	for( i=0; i<strlen(s); i++ ) {
		temp[len] = s;
		len--;
	}

	strcpy( s, temp );	//	忘れ物

	return s;
}

int main()
{
  char s[80] = "water";
  cout<<"waterを逆にすると"<<rev_str(s)<<"です\n";

  return 0;
}

になります

Re:プログラム

Posted: 2009年1月08日(木) 22:08
by ざこ
うーん今「独習C++」って参考書で勉強してるんですが「独習C」の方も
買った方がいいかもしれませんね。質問したときに結構ボロボロかなとは思ってたのですが
何が悪いのかがわかりませんでした。ありがとうございました。

Re:プログラム

Posted: 2009年1月09日(金) 09:52
by バグ
せっかくなので、STL使ってみました。
#include <iostream>
#include <string>

std::string reverse(std::string str)
{
	std::string buf;
	for (std::string::reverse_iterator it = str.rbegin(); it != str.rend(); ++it)
	{
		buf += *it;
	}
	return buf;
}

int main(void)
{
	std::string str = "water";	
	std::cout << str << " を逆にすると " << reverse(str) << " です" << std::endl;
	return 0;
}

Re:プログラム

Posted: 2009年1月09日(金) 10:09
by Mist
本を読むのもいいですが、ステップ実行ができる環境(VC++)とかがあるのであれば一行ずつステップ実行して各変数の値がどのように変わっていくのか、ポインタは文字列のどの部分を指しているか等を順番に確認していくほうが本なんか読むよりも理解が早まると思いますけどね。

> 何が悪いのかがわかりませんでした。
正解見ても自分の間違いがわからなかったって意味じゃないですよね(^^;?
それだったら、具体的にどこが間違っているか書きますけど。

Re:プログラム

Posted: 2009年1月09日(金) 10:31
by ざこ
参考書に従い勉強していく→練習問題があった→自分なりに解く→一応作れた・・エラーはでない(エラーが出る場合サイトなどで調べまくる。どうしても直せない場合質問へ)→正常に稼動しない・・→デバッグで確かめる→それでも直せない→質問する→納得する→練習問題の解答をみて
納得する。

これの繰り返しです。デバックに関してはブレークポイント指定してやる方法だとわかるんですけど
ステップインの方がまだイマイチなんですよね。ステップインすると変なとこに飛ばされたりで、さっぱりです。
他にも関数の中の変数見れないとか色々理解不足が多いです。
VC++のデバッグの使い方に関してわかりやすく載ってるサイトか参考書あれば是非教えて下さい。

Re:プログラム

Posted: 2009年1月09日(金) 10:55
by Mist
「VC++ デバッグ」等のキーワードでググればいくらでもヒットしますので使用されているバージョンにあったページを探してみてください。

●ステップ実行についてのアドバイス
ステップイン、ステップオーバー、ステップアウトの違いは理解されていますか?
ステップイン:一行実行する。関数がある場合は関数の中に入る。
ステップオーバー:一行実行する。関数がある場合は関数の中には入らないですべて実行する。
通常はこれを使用し関数の中に入りたい場合だけステップインする。
標準関数を使用している行でステップインすると標準関数の中に入ってしまうのでこちらを使用する。
ステップアウト:現在の関数から抜けるまで実行する。

そのほかに、ステップ実行中の右クリックメニューに「カーソル行まで実行」というのもあります。
その名の通り、カーソルのある行まで実行していくれるものです。

Re:プログラム

Posted: 2009年1月09日(金) 11:28
by ざこ
本屋とかでデバッグの本探しまくったんですがやり方について載ってる本はなかったのですが・・
サイトで調べればよかったんですね。わかりやすく載ってるサイト見つけました。
ただステップインしてると
「STRCAT.ASMのパスを入力してください」と出た後に変な所に飛ばされます。プログラム自体は正常に稼動できます。
サイトによるとこの場所は混合モードとかいうそうですが、その辺がさっぱりです。
どういうものかはわかるんですが混合モードにならずにステップインする方法とかありますか?

Re:プログラム

Posted: 2009年1月09日(金) 14:36
by Mist
基本的にソースコードのない部分については無理です。
(他の人が作ったLIBやDLLの中など)
ソースコードがあるのであれば「xxxxxxのパスを入力してください」と要求されたときに該当するソースファイルを選択すれば実行できます。
ただし、最適化されているとソース通りには動作しない場合もありますが。

標準関数(CRT)についてはCRTのソースコードをインストールすることで出来るようになったと思いますが、VC++EEはソースコードが付いてこないので無理っぽいです。

Re:プログラム

Posted: 2009年1月09日(金) 19:34
by Naohiro19
stringクラスを使うのも手だと思います。

Re:プログラム

Posted: 2009年1月09日(金) 21:02
by バグ
>>Naohiro19さん
一応、STLを使用したサンプルをあげておいたのですが、皆さんに華麗にスルーされて、ちょっと悲しかったりしています(笑)

Re:プログラム

Posted: 2009年1月10日(土) 02:14
by Justy

>皆さんに華麗にスルーされて、ちょっと悲しかったりしています

 では寂しい思いをさせいないために、ツッコミでも(w
 
 std::iter_swapを使えばループは半分で済みませんか?
 或いは std::reverse系を使えば……。

Re:プログラム

Posted: 2009年1月10日(土) 17:38
by tk-xleader
>一応、STLを使用したサンプルをあげておいたのですが、皆さんに華麗にスルーされて、ちょっと悲しかったりしています(笑)

僕も書いてみました。
#include<iostream>
#include<string>

int main()
{
	std::string str="water";
	std::cout<<str<<"を逆にすると"<<std::string(str.rbegin(),str.rend())<<std::endl;
	return 0;
}
高速かどうかは別の問題としてください。

Re:プログラム

Posted: 2009年1月10日(土) 17:48
by たかぎ
>一応、STLを使用したサンプルをあげておいたのですが、皆さんに華麗にスルーされて、ちょっと悲しかったりしています(笑)

というか、reverseを自分で実装する必要はありません。
#include <iostream>
#include <string>
#include <algorithm>

int main()
{
  std::string str("water");
  std::cout << str << "を逆にすると";
  std::reverse(str.begin(), str.end());
  std::cout << str << std::endl;
}
結果を返却値で返した方が使い勝手はよい気もしますが、一時オブジェクトが生成される問題もあるので、どっちもどっちですね。