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

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

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

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

GCC

GCCはGNU Compiler Collectionの略です。LinuxのC言語コンパイラのデファクトスタンダードであり、数多くの他言語やプラットフォームサポートもします。

Linux

Linuxは、Unixをベースにして開発されたオペレーティングシステムです。日本では「リナックス」と呼ばれています。 主にWebサーバやDNSサーバ、イントラネットなどのサーバ用OSとして利用されています。 上位500のスーパーコンピュータの90%以上はLinuxを使用しています。 携帯端末用のプラットフォームAndroidは、Linuxカーネル上に構築されています。

Ubuntu

Ubuntuは、Debian GNU/Linuxを基盤としたフリーのオペレーティングシステムです。

Q&A

解決済

2回答

804閲覧

[C言語]char*への再代入時のセグフォ[Segmentation fault]

haru86

総合スコア2

C

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

GCC

GCCはGNU Compiler Collectionの略です。LinuxのC言語コンパイラのデファクトスタンダードであり、数多くの他言語やプラットフォームサポートもします。

Linux

Linuxは、Unixをベースにして開発されたオペレーティングシステムです。日本では「リナックス」と呼ばれています。 主にWebサーバやDNSサーバ、イントラネットなどのサーバ用OSとして利用されています。 上位500のスーパーコンピュータの90%以上はLinuxを使用しています。 携帯端末用のプラットフォームAndroidは、Linuxカーネル上に構築されています。

Ubuntu

Ubuntuは、Debian GNU/Linuxを基盤としたフリーのオペレーティングシステムです。

0グッド

0クリップ

投稿2022/09/01 16:45

概要

ふつうのLinuxプログラミング第2版の練習問題で理解できないところがあり、
ご教示いただきたいです。

第10章
問題1: コマンドライン引数で指定されたディレクトリ以下を再帰的にトラバースして、
見つかったファイルのパスをすべて表示するプログラムを書きなさい。
シンボリックリンクをたどってはいけません。
公式回答ソース:https://github.com/aamine/stdlinux2-source/blob/master/traverse.c

不明点、試したこと

上記コードの106行目で、構造体の(char*)型メンバ変数ptrに対して、下記の処理を実施しています。
*strrchr(pathbuf->ptr, '/') = '\0';

手元で動かしたところ、上記のソースは正常終了いたしますが、
通常だと(char*)型変数への再代入は Segmentation fault になると理解しています。
試しに、該当する箇所のみ抜き出した以下のプログラムを実行したところ、
再代入の箇所で Segmentation fault となりました。
この事象について、どのように解釈したらよろしいでしょうか。

#include <stdio.h> #include <stdlib.h> #include <string.h> #define INITLEN 1024 struct strbuf { char *ptr; size_t len; }; int main(int argc, char *argv[]) { struct strbuf *pathbuf; pathbuf = (struct strbuf *)malloc(sizeof(struct strbuf)); pathbuf->ptr = malloc(INITLEN); pathbuf->len = INITLEN; pathbuf->ptr = "abc"; printf("%s\n", pathbuf->ptr); // "abc" printf("%s\n", strrchr(pathbuf->ptr, 'b')); // "bc" // pathbuf->ptr[0] = 'A'; // Segmentation fault // *strrchr(pathbuf->ptr, 'b') = '\0'; // Segmentation fault return 0; }

実行環境

OS: Ubuntu 22.04.1 LTS
gcc: Ver11

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

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

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

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

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

guest

回答2

0

ベストアンサー

(char*)型変数への再代入は Segmentation fault になると理解

その理解は間違っています。ポインタが指している先が書き込み禁止であれば(OSレベルの)エラーになるということであって、char*の一般的性質ではありません。

C

1 pathbuf->ptr = malloc(INITLEN); 2 <> 3 pathbuf->ptr = "abc";

これは、プログラムとしては、即破滅的な状況にはなりませんが「間違っている」と断じていいレベルの操作です(そもそもfree()がないし)。「メモリリーク」という言葉について調べてみることをお勧めします。あるいは、「ポインタって何?」というところから復習が必要かもしれません。
pathbuf->ptr = malloc(INITLEN);は、malloc()によって大きさINITLENのユーザーが好きに使って良い領域を(何らかの正当な方法で、誰かから)「借り」てその領域のアドレスをpathbuf->ptrに記憶します。
続いておこなうpathbuf->ptr = "abc"は、メモリのどこかに"abc"というデータを持った領域を作成し(あるいは作成しておき)、その領域のアドレスをpathbuf->ptrに上書きします(それまでに入っていたデータは失われます)。
さて借りた領域のアドレスはわからなくなってしまいました。借りたものは返さなければいけないのですが...小さなサイズだからしらばっくれるとか、OSがある場合は破産(プログラムが終了)したら勝手に回収される「はずだから」放置、というのはよいお作法ではありません。

お作法で言えば、malloc()ではメモリ領域を借りられたことを確認する、つまりmalloc()の返り値がNULLでない)ことを「必ず」確認するのもお作法です。

余談に近い話になりますが、Segmentation faultになるのはC/C++の仕事ではなく、OSの仕事です。Cのレベルでは、定数領域に書き込む行為は「未定義」(結果の保証はなにもしない)というだけ。現実としてはシステムによって「何事もなかったかのように書き換えられる」「書き込み操作は行われるがデータは変化しない」「(今回のように)OS等がちょっかいをかけてくる」などの結果が考えられますが、それはC言語の範囲で決まるものではありません。

投稿2022/09/01 22:19

編集2022/09/01 22:21
thkana

総合スコア7639

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

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

thkana

2022/09/02 11:36

参照元のプログラムもfree()してないのか...だめぢゃん。
haru86

2022/09/02 13:20

詳しく教えてくださりありがとうございます。 わかった気になって「(char*)型変数への再代入は Segmentation fault になる」などど 意味のわからないことを書いてました。。 > 続いておこなうpathbuf->ptr = "abc"は、メモリのどこかに"abc"というデータを持った領域を作成し > (あるいは作成しておき)、その領域のアドレスをpathbuf->ptrに上書きします > (それまでに入っていたデータは失われます)。 これでわかった気がします。 試しに以下のプログラムを実行してみましたが、文字列リテラルのアドレスで上書きした場合は、 free()もできず、文字列リテラルの記憶領域はリードオンリーだということが理解できました。 質問する前は、mallocで確保するメモリ領域も文字列リテラルが格納されている領域も 同じもんだと思っていましたが、そうではないのですね。 ```C int main(int argc, char *argv[]) { char *s; char *temp = (char *)malloc(20); if (!temp) { perror(NULL); return 1; } s = temp; strcpy(s, "abc"); // s = "def"; // free(): invalid pointer free(s); return 0; } ```
haru86

2022/09/02 13:21

あれ、コメントだとmarkdownが効かないのか。
guest

0

文字列リテラルは書き込み禁止です。
mallocした領域にstrcpyしましょう。

c

1 strcpy(pathbuf->ptr, "abc");

投稿2022/09/01 16:55

shiracamus

総合スコア5406

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

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

haru86

2022/09/01 17:31

代入演算子だと文字列リテラルと同じアドレスを指してしまうが、 strcpyなら中身が同じでも別のアドレスだから書き込み可能ということですね。 理解できました、ありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問