文字列のstrcat


【オリジナル】最近C言語を使ってプログラミングをしていて、自分の元の多くの細部のものがよく把握されていないことを発見しました.例えば、文字列の接続関数strcat()には注意しなければならないものがたくさんあります.今晩プログラムを書くとき、ここで大きな間違いが発生し、プログラムがクラッシュしました.GOOGLEを通じて発見しました.
【回転】
すぐにエラーが発生するプログラムコード
Cコード
#include <stdio.h> 
#include <string.h> 

int main(void) 
 {
    char string[10]="123456";
    char *str1     ="abcdefghij";

    printf("string.length=%d
",strlen(string)); strcat(string,str1); printf("string.length=%d
",strlen(string)); printf("string=%s
", string); printf("str1=%s
",str1); return 0; }

しかし、string[10]をstring[20]に変更すると、通常は次のように動作します.
Cコード
string.length=6
string.length=16
string=123456abcdefghij
str1=abcdefghij

これは、strcat(str 1,str 2)では、str 1が定義されるときの長さがstr 1+str 2の長さより小さくなければならないかもしれないことを示しています.逆に、文字列配列の値を文字ポインタに接続する実験を行います.
Cコード
#include <stdio.h> 
#include <string.h> 

int main(void) 
 {
   char string[20]="123456";
    char *str1     ="abcdefghij";

    printf("str1.length=%d
",strlen(str1)); strcat(str1,string); printf("str1.length=%d
",strlen(str1)); printf("string=%s
", string); printf("str1=%s
",str1); }

 
Cコード
str1.length=10
str1.length=16
string=123456
str1=abcdefghij123456

おかしい、どうしてすべて正常なの?この場合,strcat(str 1,str 2)は2つの状況に分かれる可能性があると推測できる.str 1が文字配列である場合、道理は最初の実験と同じである.str 1が文字列ポインタである場合、str 1はstr 1+str 2の長さより大きい必要はありません.str 1はポインタであり、ポインタにはアドレスが格納されているからです.しかし、よく考えてみると、c言語の古典的な経験は、ポインタで文字列char*p=「12345678」を定義した場合、これはp[2]=3では使用できません.このような文は*pの値を変更します.そうしないとメモリ汚染が発生します.もちろん数行のコードでは見えません.
参照
配列とポインタは、それらの定義で文字列定数で初期化できます.同じように見えるが、下層のメカニズムは同じではない.
コンパイラは、ポインタが指すオブジェクトに空間を割り当てるのではなく、定義時にポインタに文字列定数を同時に与えて初期化しない限り、ポインタ自体の空間を割り当てるだけです.たとえば、次の定義では、メモリが割り当てられた文字列定数が作成されます.
char *p="breadfruit";
文字列定数のみがそうであることに注意してください.浮動小数点数などの定数に空間を割り当てることは期待できません.
float *pip=3.141;/*エラー!コンパイルできません*/
ANSI Cでは、ポインタの初期化時に作成される文字列定数を読み取り専用として定義します.ポインタでこの文字列の値を変更しようとすると、プログラムは未定義の動作をします.一部のコンパイラでは、文字列定数は、読み取りのみが許可されているテキストセグメントに格納され、変更されないようにします.
配列は文字列定数で初期化することもできます.
char a[]="gooseberry";
ポインタとは逆に、文字列定数によって初期化された配列は変更できます.次の文など、個々の文字は後で変更できます.
strncpy(a,"black",5);
配列の値を「blackberry」に変更します.
『C専門家プログラミング』より抜粋
私たちの前の経験では、コードを以下に修正して、すぐに問題を発見することができます.
Cコード
#include <stdio.h> 
#include <string.h> 

int main(void) 
 {
    char string[50]="12345678901234567890123456789012345678901234567890";
    char *str1     ="abcdefghijabcdefghijabcdefghijabcdefghijabcdefghij";

	printf("str1.length=%d
",strlen(str1)); strcat(str1,string); printf("
str1.length=%d
",strlen(str1)); return 0; }

私の機械では、すぐにプログラムがクラッシュします!もう1つの実験をして、2つの文字列の配列、str 1とstr 2を定義して、彼らを接続した後、str 2の値を変えて、str 1に影響するかどうかを見ます.
Cコード
#include <stdio.h> 
#include <string.h> 

int main(void) 
 {
    char str1[50]="12345678901234567890";
    char str2[50]="12345678901234567890";

	printf("str1.length=%d
",strlen(str1)); strcat(str1,str2); printf("
str1.length=%d
",strlen(str1)); str2[0]='A'; printf("
str1=%s",str1); printf("
str2=%s",str2); return 0; }

 
Cコード
str1.length=20

str1.length=40

str1=1234567890123456789012345678901234567890
str2=A2345678901234567890

結論は:影響しない!以上の様々な兆候を組み合わせると、strcat(str 1,str 2)のstr 1は文字列配列であるか、文字列配列を指すポインタである必要があり、文字配列の長さは十分であることが明らかになった.strcatはstr 2の値をstr 1にコピーし、str 1の既存の文字の後ろに接続します!strcatで何があったのか分かった!次の例を示します.
Cコード
#include <stdio.h> 
#include <string.h> 
int main(void) 
 { 
    char string[101]="12345678901234567890123456789012345678901234567890";
    char  *str1    ="abcdefghijabcdefghijabcdefghijabcdefghijabcdefghij";
    char *str2;
    str2=string;
    strcat(str2,str1);

	printf("str2.length=%d",strlen(str2));
	printf("
str2=%s",str2); return 0; }

 
Cコード
str2.length=100
 str2=12345678901234567890123456789012345678901234567890abcdefghijabcdefghijabcd
efghijabcdefghijabcdefghij

注:string[100]ではなくstring[101]です.文字列処理では、最後に文字列終了子が加算されます.string[100]が上記のプログラムを実行すると、エラーが報告され、プログラムはstr 2を出力できない.lengthとstr 2の値.