質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

3回答

5459閲覧

文字列とポインタについてです。

strike1217

総合スコア651

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

1グッド

1クリップ

投稿2017/01/14 05:45

編集2017/01/14 05:53

以前、int型のポインタについて、質問しました。
今回はchar型のほうです。
1,

char *buf; scanf("%s", buf);

これがダメな理由を教えてください。NULLポインタで初期化してもsegmentation faultでした。
Linuxだと、上記のやり方でも、エラーはでませんが、windowsだと強制的に終了します。

char buffer[1024]; char *buf = buffer2; scanf("%s", buf);

こちらは問題ありませんでした。
なので、

char *buf = "you will make me happy"; scanf("%s", buf);

とやってみました。 これもダメでした・・・・

2,

char *buf = "123"; buf = "456"; *buf = 'Z'; これは問題がありません。ですが・・・ *buf = "456"; とやるとエラーです。

なぜです?? *buf = "456"の方が正しくみえるのですが・・・
buf と *bufの違いがまだよくわかっていません。

3,

char str[8]; char *ptr = str; scanf("%s", &ptr);

これはどういうことですか??
実際にやってみて、printf("%s", ptr);とやるとエラーになるんですが・・・

4,

char s[] = "ABC"; s = "DEF";

これはエラーですね。
なんで、ダメなんですか??
配列の値を上書きするには、他に方法がありますか??

ちょっといろいろ混乱していて、わかりません。
教えてください。

[追記]
int *w, a;
w = &a
printf("%d", *w);
int 型はこうできます。

char *buf = "jfkdoaur";
printf("%s", *buf);これはエラーでした。
printf("%s", buf);
char型の場合はprintf("%s", *buf);ではダメなようです・・・
何が違うんですか??

witchy👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

ベストアンサー

1(前半) 使用可能な領域のアドレスをポインタにセットしていないから。
未初期化のポインタや、ヌルポインタという利用不可能な領域を指すアドレスをセットしたポインタは、使用可能な領域のアドレスをセットしておらず、正しく実行されない。

1(後半) リテラル文字列(二重引用符で囲んだ文字列)は、その内容が格納された読み取り専用領域のアドレスを指す。そのため環境によっては正しく実行されない。

2 どちらにも問題がある。
*bufへの代入は、bufが指す先のchar型への代入を意味する。"456"はchar型のポインタであり、char型ではなく、代入できない。
'Z'はchar型であり、代入可能なのでコンパイルエラーは発生しない。しかし、1(後半)同様、bufは読み取り専用領域を指しているので、環境によっては正しく実行されない。

3 配列strの「中身が」初期化されておらず、printf関数がどこまで読み込んでいいのか分からず(printf関数はヌル文字にあたるまで読み込み続ける)、利用不可能な領域にアクセスしてしまい、異常終了する。

4(前半) C言語には、配列をまるごと別の配列にコピーする命令は用意されていないからです。配列の名前を書くと、そのアドレスを意味するので、質問者さんのコードは

C

11 = 2;

と無理矢理代入しているようなものになってしまうのです。
面倒でも、

C

1char s[] = "ABC"; 2s[0] = 'D'; 3s[1] = 'E'; 4s[2] = 'F'; 5s[3] = '¥0';

のように、1要素(文字)ずつコピーする必要があります。
(strcpyという関数を使用すればこの面倒は解消されます。)

4(後半) char型の場合はprintf("%s", *buf);ではダメなようです・・・
ということですが、まさにそれが答えです。
printfの書式設定で、sは「引数のポインタの指す先のアドレス」にある文字列の表示を意味します。
*bufはポインタではなく、char型の変数なので、printf関数が利用不可能な領域にアクセスしてしまい、異常終了します。
1文字表示させたいときには、printf("%c", *buf);
文字列を表示させたいときには、printf("%s", buf);
というように使い分ける必要があります。

3(追記)

C

1char str[8]; 2char *ptr = str; 3scanf("%s", &ptr);

最終行が問題です。これはscanf("%s", ptr);とする必要があります。
例えば、scanf関数で数値を1つint型の変数iに読み込みたいとしたとき、なぜ

C

1scanf("%d", &i);

というように、&を使う必要があるかを説明します。
C言語では、関数の引数は元の変数や値の「コピー」というルールになってます。
コピーの方をいくら操作しても、元の変数には何の影響もありません。
そのため、

C

1scanf("%d", i); /* 本当はダメよ */

というように書いたとしても、元の変数は操作できません。
なので、元の変数の「アドレス」を関数に渡して、そのアドレスの先を操作することで、変数を操作しているのです。
では、文字列を読み込むときはどうでしょうか。
ptrは元々ポインタ、すなわちアドレスですよね。
ptrの指す先の領域を操作すれば、配列の中身を変更できます。
そして、ptrという「ポインタ変数」を操作する必要はありません。操作するのはその「中身」です。
なので、%sという文字列読み込みの書式設定の場合だけ例外で、&ptrではなくptrを渡すというルールになっているのです。

投稿2017/01/14 06:55

編集2017/01/14 08:28
majiponi

総合スコア1720

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

strike1217

2017/01/14 07:57

ありがとうございます。
majiponi

2017/01/14 07:59

行き違いで加筆しました。追記分はこれからさらに加筆します。
strike1217

2017/01/14 08:08

とても、わかりやすいです。
majiponi

2017/01/14 08:10

できるだけ正確に書こうとしたため、かえってわかりにくいところがなかったか心配していますが、内容でまだわかりにくいところはありますか?
strike1217

2017/01/14 08:17

3番ですね。 char *p; scanf("%s", &p); ポインタのアドレスをscanfに指定するとどうなるかが、まだよくわかりません
strike1217

2017/01/14 08:39

2番については、再度質問します。
strike1217

2017/01/14 08:40

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

0

こんにちは。

最初の方だけ、ちろっと回答してみます。

char *buf;

これは、char* buf;と書くと分かりやすいです。char*変数を1つ定義してます。
見ての通り初期化してませんので、不定値のままです。
bufはchar型へのポインタですから、どこか分からない謎の場所を指してます。

scanf("%s", buf);

は、その「謎の場所」へ文字列を読み込みますので、普通は落ちます。
linuxで落ちなかったのはたまたまかも知れません。

char *buf = "you will make me happy";

scanf("%s", buf);

この場合、bufは"you will make me happy"という文字列が記録された先頭メモリを指してます。
そして、"you will make me happy"は定数ですから、最近のPCでは書き換え不可と指定されたメモリ上に記録されます。(この辺はMMUの機能です。x86系CPUはそのような機能をもつMMUを内蔵してます。)
書き換え不可ですから、そこにscanf()で読み込もうとすると、書き換え不可例外が発生し落ちます。

C++でビルドした場合、"you will make me happy"はchar const型です。それをcharへ代入しようとした時点でコンパイラによっては警告、もしくは、エラーになると思います。

投稿2017/01/14 06:31

Chironian

総合スコア23272

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

strike1217

2017/01/14 07:58

ありがとうございます。 いままで、初期化されていないポインタを使っていたので、やめますね。
guest

0

ポインタは:宣言されただけでは有効なメモリ番地を指し示しているとは限りません。

配列:宣言するだけで有効なメモリ領域を確保し、配列名と先頭番地を指し示すポインタを兼ねます。
→ただし、これは固定のポインタ変数であり、他のアドレスを代入し直すことができません。

投稿2017/01/14 06:20

HogeAnimalLover

総合スコア4830

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

strike1217

2017/01/14 07:58

ありがとうございます
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問