Cでconst /リテラル​​文字列を連結するにはどうすればよいですか?

2008年11月21日に質問されました。  ·  閲覧回数 1.1M回  ·  ソース

The.Anti.9 picture
2008年11月21日

私はCで働いており、いくつかのことを連結する必要があります。

今私はこれを持っています:

message = strcat("TEXT ", var);

message2 = strcat(strcat("TEXT ", foo), strcat(" TEXT ", bar));

これで、Cの経験がある場合は、実行しようとするとセグメンテーション違反が発生することを理解していると思います。 では、どうすればそれを回避できますか?

回答

Brian R. Bondy picture
2008年11月21日
409

Cでは、「文字列」は単なるchar配列です。 したがって、それらを他の「文字列」と直接連結することはできません。

strcat関数を使用できます。この関数は、 srcが指す文字列を、 dest指す文字列の末尾に追加します。

char *strcat(char *dest, const char *src);

これがcplusplus.comから

char str[80];
strcpy(str, "these ");
strcat(str, "strings ");
strcat(str, "are ");
strcat(str, "concatenated.");

最初のパラメーターには、宛先バッファー自体を指定する必要があります。 デスティネーションバッファはchar配列バッファである必要があります。 例: char buffer[1024];

最初のパラメーターに、コピーしようとしているものを格納するのに十分なスペースがあることを確認してstrcpy_sstrcat_sなどの関数を使用する方が安全です。ここでは、宛先バッファーのサイズを明示的に指定する必要があります。

:文字列リテラルは定数であるため、バッファーとして使用できません。 したがって、常にバッファにchar配列を割り当てる必要があります。

strcatの戻り値は単純に無視でき、最初の引数として渡されたものと同じポインターを返すだけです。 便宜上そこにあり、呼び出しを1行のコードにチェーンすることができます。

strcat(strcat(str, foo), bar);

したがって、問題は次のように解決できます。

char *foo = "foo";
char *bar = "bar";
char str[80];
strcpy(str, "TEXT ");
strcat(str, foo);
strcat(str, bar);
Alex B picture
2008年11月21日
253

Cコードでstrcatを使用することsnprintfを使用することです。

char buf[256];
snprintf(buf, sizeof buf, "%s%s%s%s", str1, str2, str3, str4);

一部のコメント提供者は、引数の数がフォーマット文字列と一致しない可能性があり、コードは引き続きコンパイルされるという問題を提起しましたが、これが当てはまる場合、ほとんどのコンパイラはすでに警告を発行しています。

dbagnara picture
2015年09月19日
31

文字列は、コンパイル時に連結することもできます。

#define SCHEMA "test"
#define TABLE  "data"

const char *table = SCHEMA "." TABLE ; // note no + or . or anything
const char *qry =               // include comments in a string
    " SELECT * "                // get all fields
    " FROM " SCHEMA "." TABLE   /* the table */
    " WHERE x = 1 "             /* the filter */ 
                ;
Mr.Ree picture
2008年11月21日
25

皆さん、str n cpy()、str n cat()、またはs n printf()を使用してください。
バッファスペースを超えると、メモリ内に続くものはすべて破棄されます。
(そして、末尾のnull '\ 0'文字用のスペースを確保することを忘れないでください!)

Reed Hedges picture
2008年11月21日
16

また、mallocとreallocは、連結されている文字列の数が事前にわからない場合に役立ちます。

#include <stdio.h>
#include <string.h>

void example(const char *header, const char **words, size_t num_words)
{
    size_t message_len = strlen(header) + 1; /* + 1 for terminating NULL */
    char *message = (char*) malloc(message_len);
    strncat(message, header, message_len);

    for(int i = 0; i < num_words; ++i)
    {
       message_len += 1 + strlen(words[i]); /* 1 + for separator ';' */
       message = (char*) realloc(message, message_len);
       strncat(strncat(message, ";", message_len), words[i], message_len);
    }

    puts(message);

    free(message);
}
paxdiablo picture
2008年11月21日
5

文字列リテラルを変更しようとするのは未定義の動作です。これは次のようなものです。

strcat ("Hello, ", name);

しようとします。 name文字列を文字列リテラル"Hello, "の最後に追加しようとしますが、これは明確に定義されていません。

これを試してみてください。 それはあなたがやろうとしているように見えることを達成します:

char message[1000];
strcpy (message, "TEXT ");
strcat (message, var);

これにより、変更許可されるバッファ領域が作成され、文字列リテラルとその他のテキストの両方がそこにコピーされます。 バッファオーバーフローに注意してください。 入力データを制御する(または事前に確認する)場合は、私のように固定長のバッファーを使用しても問題ありません。

それ以外の場合は、ヒープから十分なメモリを割り当てて処理できるようにするなどの緩和戦略を使用する必要があります。 言い換えれば、次のようなものです。

const static char TEXT[] = "TEXT ";

// Make *sure* you have enough space.

char *message = malloc (sizeof(TEXT) + strlen(var) + 1);
if (message == NULL)
     handleOutOfMemoryIntelligently();
strcpy (message, TEXT);
strcat (message, var);

// Need to free message at some point after you're done with it.
David Rodr&#237;guez - dribeas picture
2008年11月21日
5

出力バッファを初期化することを忘れないでください。 strcatの最初の引数は、結果の文字列に十分な追加スペースが割り当てられたnullで終了する文字列である必要があります。

char out[1024] = ""; // must be initialized
strcat( out, null_terminated_string ); 
// null_terminated_string has less than 1023 chars
Ralf picture
2008年11月21日
4

Cの経験がある場合、文字列は最後の文字がヌル文字であるchar配列のみであることに気付くでしょう。

何かを追加するために最後の文字を見つけなければならないので、これは非常に不便です。 strcatがあなたに代わってそれを行います。

したがって、strcatは最初の引数からヌル文字を検索します。 次に、これを2番目の引数の内容に置き換えます(nullで終わるまで)。

それでは、コードを見ていきましょう。

message = strcat("TEXT " + var);

ここでは、テキスト「TEXT」へのポインタに何かを追加しています(「TEXT」のタイプはconst char *です。ポインタです)。

それは通常は機能しません。 また、「TEXT」配列は通常一定のセグメントに配置されるため、変更しても機能しません。

message2 = strcat(strcat("TEXT ", foo), strcat(" TEXT ", bar));

静的テキストを再度変更しようとしていることを除いて、これはうまくいくかもしれません。 strcatは、結果に新しいメモリを割り当てていません。

代わりに、次のようなことを提案します。

sprintf(message2, "TEXT %s TEXT %s", foo, bar);

sprintfのドキュメントを読んで、そのオプションを確認してください。

そして今重要なポイント:

バッファにテキストとヌル文字を保持するのに十分なスペースがあることを確認してください。 バッファを割り当てるstrncatやprintfの特別なバージョンなど、役立つ関数がいくつかあります。 バッファサイズを保証しないと、メモリが破損し、リモートで悪用可能なバグが発生します。

Nils picture
2011年12月08日
4

人々が指摘するように、文字列の処理は大幅に改善されました。 したがって、Cスタイルの文字列の代わりにC ++文字列ライブラリを使用する方法を学びたいと思うかもしれません。 ただし、ここに純粋なCのソリューションがあります

#include <string.h>
#include <stdio.h>
#include <stdlib.h>

void appendToHello(const char *s) {
    const char *const hello = "hello ";

    const size_t sLength     = strlen(s);
    const size_t helloLength = strlen(hello);
    const size_t totalLength = sLength + helloLength;

    char *const strBuf = malloc(totalLength + 1);
    if (strBuf == NULL) {
        fprintf(stderr, "malloc failed\n");
        exit(EXIT_FAILURE);
    }

    strcpy(strBuf, hello);
    strcpy(strBuf + helloLength, s);

    puts(strBuf);

    free(strBuf);

}

int main (void) {
    appendToHello("blah blah");
    return 0;
}

それが正しい/安全かどうかはわかりませんが、現時点では、ANSICでこれを行うためのより良い方法を見つけることができませんでした。

Pieter picture
2008年11月21日
3

strcat()の最初の引数は、連結された文字列のために十分なスペースを保持できる必要があります。 したがって、結果を受け取るのに十分なスペースのあるバッファーを割り当てます。

char bigEnough[64] = "";

strcat(bigEnough, "TEXT");
strcat(bigEnough, foo);

/* and so on */

strcat()は、2番目の引数を最初の引数と連結し、結果を最初の引数に格納します。返されるchar *は、単にこの最初の引数であり、便宜上のものです。

最初と2番目の引数が連結された、新しく割り当てられた文字列は取得されません。これは、コードに基づいて予想されたと思います。