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

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

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

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

Q&A

解決済

5回答

742閲覧

C言語で、二つの文字列を連結する関数を作成する際の疑問

退会済みユーザー

退会済みユーザー

総合スコア0

C

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

0グッド

0クリップ

投稿2017/07/27 12:48

編集2017/07/27 13:51

C言語で、二つの文字列を連結するプログラムを以下のように書きました。

C

1#include <stdio.h> 2#include <stdlib.h> 3#include <string.h> 4 5int main (int count, char* param[]) { 6 7 char* str_a = NULL; 8 char* str_b = NULL; 9 char* my_strconcat(char* a, char* b); 10 if (count > 2) { 11 str_a = param[1]; 12 str_b = param[2]; 13 } else { 14 printf("It is not enough that parameter which you proposed."); 15 exit(2); 16 } 17 char* res = NULL; 18 res = my_strconcat(str_a, str_b); 19 printf("%s", res); 20 return(0); 21} 22 23char* my_strconcat(char* a, char* b) { 24 char* temp = NULL; 25 char* source_p = NULL; 26 int len_a = 0; 27 int len_b = 0; 28 len_a = strlen(a); 29 len_b = strlen(b); 30 temp = realloc(a, len_a + len_b); 31 if (temp != NULL) { 32 a = temp; 33 source_p = a; 34 } else { 35 printf("The application failed the command."); 36 exit(2); 37 } 38 // \0を示すインデックスまでポインタを進める 39 a = a + len_a; 40 int i = 0; 41 while(1) { 42 if (*(b) != '\0') { 43 *a = *(b); // (1)ここで第一引数のポインタ変数に第二引数の文字を代入(コピー)して文字列を拡張 44 a = a + 1; 45 b = b + 1; 46 } else { 47 break; 48 } 49 i++; 50 } 51 *a = '\0'; 52 free(a); 53 return (source_p); 54}

ふと疑問に思ったのですが、(1)の第一引数のポインタが指し示すヌル文字に第二引数の最初の文字を代入していきますが、Javaやpythonなどの文字列はイミュータブルだと聞いたのですが
C言語では上記のように任意の文字列リテラルの任意の位置の文字を差し替えるなどしても問題ないのでしょうか?

AtsushiSaitoさんから
mallocしていないポインタをreallocすることは不適切だとの指摘を受けたので
修正してみました。

C

1#include <stdio.h> 2#include <stdlib.h> 3#include <string.h> 4 5int main (int count, char* param[]) { 6 7 char* str_a = NULL; 8 char* str_b = NULL; 9 char* my_strconcat(char* a, char* b); 10 if (count > 2) { 11 str_a = param[1]; 12 str_b = param[2]; 13 } else { 14 printf("It is not enough that parameter which you proposed."); 15 exit(2); 16 } 17 char* res = NULL; 18 res = my_strconcat(str_a, str_b); 19 printf("%s", res); 20 return(0); 21} 22 23char* my_strconcat(char* a, char* b) { 24 char* temp = NULL; 25 char* source_p = NULL; 26 int len_a = 0; 27 int len_b = 0; 28 len_a = strlen(a); 29 len_b = strlen(b); 30 temp = malloc(len_a + len_b); 31// temp = realloc(a, len_a + len_b); 32 if (temp != NULL) { 33 source_p = temp; 34 } else { 35 printf("The application failed the command."); 36 exit(2); 37 } 38 39 int i = 0; 40 while(1) { 41 if (*a != '\0') { 42 *temp = *a; 43 temp++; 44 a++; 45 } else if (*a == '\0') { 46 if (*(b) != '\0') { 47 *temp = *(b); 48 temp++; 49 b++; 50 } else { 51 break; 52 } 53 } 54 } 55 *temp = '\0'; 56 return (source_p); 57}

reallocをmallocに変えて 引数に渡った2つの文字列を一バイトずつ
コピーしました。
ちょっとというかかなり冗長になってますが,Cのポインタ操作としては問題ないでしょうか?

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

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

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

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

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

guest

回答5

0

こんにちは。

C言語では上記のように任意の文字列リテラルの任意の位置の文字を差し替えるなどしても問題ないのでしょうか?

C言語といえども文字列リテラルを書き換えてはいけません。問題が超でます。
しかし、提示されたプログラムは「文字列リテラル」を書き換えていないと思います。「リテラル」はソースの中にハードコーディングされた定数のことです。
「文字列リテラル」はその文字列版です。

例えば、"abcdefg"の一部が書き換えられてしまい、ソース上では"abcdefg"なのに実際には"abcDEFG"のようなことになるとデバッグは地獄を見ます。
最近のPCではありえませんが、10年くらい前のPCではありえました。
今も例えば、μI-TRONが乗るようなCPUではあるだろうと思います。
定数がRAMに記録され、かつ、書き込み禁止にできない処理系の場合に該当します。
C++では言語仕様的に簡単にはできないようになっているのでまず大丈夫です。


ところで、argv[1]ってreallocして大丈夫でしたっけ?
そういう発想がなかったから、やったことないです。

投稿2017/07/27 13:37

Chironian

総合スコア23272

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

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

raccy

2017/07/27 15:32

> ところで、argv[1]ってreallocして大丈夫でしたっけ? n1570の7.22.3.5-3(p349)に Otherwise, if ptr does not match a pointer earlier returned by a memory management function, or if the space has been deallocated by a call to the free or realloc function, the behavior is undefined. とありますので、memory management functionによって取得された訳ではないargvについてreallocした場合は、動作が未定義(undefined)になるかと思います。
Chironian

2017/07/27 16:13

raccyさん、フォローありがとうございます。 なるほど、やはり処理系依存ということですね。
guest

0

ベストアンサー

問題ありますが、提示されているプログラムと疑問の内容に齟齬がありますね。 考え方の前提が誤っているのだとは思いますが、問題を分けて説明します。

まず「文字列リテラル」という用語はプログラム中に文字列そのものとして書かれたもののことを指します。 つまりダブルクォーテーションで囲まれたトークンで与えられたものが文字列リテラルと呼ばれます。 main の引数として渡ってくる文字列のことは文字列リテラルとは言いません。

この C 言語の用語で言うところの文字列リテラルを書き換えることが許されるかというと言語仕様では「やった結果は未定義」となっています。 許容されることもあるかもしれませんが、されないこともあるかもしれません。 まあ、実質的にはやっていけないことだと考えてよいです。

main の引数として与えられた文字列を書き換えてよいかということについては__書き換えは許されます__。 ただし、 realloc できるのは malloccalloc で確保されたメモリに限られますので、質問で提示されているプログラムは誤りであると言えます。

投稿2017/07/27 13:18

SaitoAtsushi

総合スコア5444

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

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

退会済みユーザー

退会済みユーザー

2017/07/27 13:32

ご回答ありがとうございます。 > ただし、 realloc できるのは malloc や calloc で確保されたメモリに限られますので、 ということですが,WindowsのMinGWのコンパイラだと特に問題なくうごきましたがCの標準の規格(ANSI?とかですかね?)には反しているということですね...。 となると str_aとstr_bを連結するならば,mallocで空っぽのメモリを確保して str_aの最初からヌル文字手前までを新しいメモリへコピーしその後str_bの最初からヌル文字手前までを 続きのアドレスへコピーするして呼び出し元へ返すという感じでしょうか?
SaitoAtsushi

2017/07/27 13:53

はい。 malloc や calloc で割り当てたのではないメモリを realloc に渡したとき、その動作は未定義とされています。 スタートアップルーチン内ではひょっとすると main に渡す前の引数を処理するためのメモリ空間を確保するために malloc を使っていたりするような実装になっていることもあるかもしれませんし、そのような環境では問題が起こらないということはありうることです。 が、言語仕様ではあくまで未定義なので、そういった期待はしてはいけないのです。 新しくメモリを確保してそこへふたつの文字列を連続でコピーするというのは妥当な設計のひとつではあると思います。 ただ、それは設計思想によるので一概には言えません。 my_strconcat の仕様として、 malloc で確保したメモリを渡さなければならないという仕様にするのであればそれはそれで使えますよ。
guest

0

重箱の隅をつつくようですが、strlen()が返す値にはNULL('\0')文字は含まれません。従って、temp = malloc(len_a + len_b);ではNULL文字の分が足りません。malloc()[realloc()も?]はメモリを確保する場合に必要量+α(環境によりますがメモリ管理が楽なサイズ)を確保するので問題はなさそうですが、厳密には間違いです。(恐ろしく発見が難しいバグになります)

投稿2017/07/28 02:48

cateye

総合スコア6851

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

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

0

パラメータの領域は直接上位のアドレスを指していますのでreallocするとエラーになると思います。ですのでrealloc用に領域を確保します。

c

1#include <stdio.h> 2#include <stdlib.h> 3#include <string.h> 4 5char* my_strconcat(char* a, char* b); 6 7int main (int count, char* param[]) { 8 9 char* str_a = NULL; 10 char* str_b = NULL; 11 12 if (count > 2) { 13 str_a=malloc(strlen(param[1])+1); 14 str_b=param[2]; 15 memcpy(str_a,param[1],strlen(param[1])+1); 16 } else { 17 printf("It is not enough that parameter which you proposed."); 18 exit(2); 19 } 20 char* res = NULL; 21 printf("%s", my_strconcat(str_a, str_b)); 22 23 free(str_a); 24 return(0); 25} 26 27char* my_strconcat(char* a, char* b) { 28 char* temp = NULL; 29 char* source_p = NULL; 30 int len_a = 0; 31 int len_b = 0; 32 33 len_a = strlen(a); 34 len_b = strlen(b); 35 temp = realloc(a, len_a + len_b); 36 37 if (temp == a) { 38 source_p = a; 39 } else { 40 free(a); 41 a=temp; 42 source_p = a; 43 } 44 // \0を示すインデックスまでポインタを進める 45 a += len_a; 46 47 while(*b != '\0') { 48 *a = *b; // (1)ここで第一引数のポインタ変数に第二引数の文字を代入(コピー)して文字列を拡張 49 a++; 50 b++; 51 } 52 *a = *b; 53 return (source_p); 54}

投稿2017/07/27 14:15

編集2017/07/27 14:52
A.Ichi

総合スコア4070

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

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

0

C言語はメモリを直接操作できるため、文字列の書き換えも自由にできます。ただ、元の文字列を書き換えるような処理はデバッグや拡張が難しくなるため、出来るだけ避けた方が良いかと思います。

投稿2017/07/27 13:23

SVC34

総合スコア1149

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問