私は C で作業しており、いくつかのことを連結する必要があります。
今私はこれを持っています:
message = strcat("TEXT ", var);
message2 = strcat(strcat("TEXT ", foo), strcat(" TEXT ", bar));
C の経験がある場合は、これを実行しようとするとセグメンテーション違反が発生することに気付くと思います。それで、どうすればそれを回避できますか?
私は C で作業しており、いくつかのことを連結する必要があります。
今私はこれを持っています:
message = strcat("TEXT ", var);
message2 = strcat(strcat("TEXT ", foo), strcat(" TEXT ", bar));
C の経験がある場合は、これを実行しようとするとセグメンテーション違反が発生することに気付くと思います。それで、どうすればそれを回避できますか?
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_s
利用可能な場合は、次のような関数を使用する方が安全strcat_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);
文字列はコンパイル時に連結することもできます。
#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 */
;
皆さん、str n cpy()、str n cat()、または s n printf() を使用してください。
バッファ スペースを超えると、メモリ内で後続するものはすべて破棄されます。
(そして、末尾のヌル '\0' 文字のためのスペースを確保することを忘れないでください!)
また、連結される文字列の数が事前にわからない場合は、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);
}
人々が指摘するように、文字列の処理は大幅に改善されました。したがって、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でこれを行うためのより良い方法を見つけることができませんでした。
出力バッファを初期化することを忘れないでください。strcat の最初の引数は、結果の文字列に十分な余分なスペースが割り当てられた、null で終了する文字列でなければなりません。
char out[1024] = ""; // must be initialized
strcat( out, null_terminated_string );
// null_terminated_string has less than 1023 chars
文字列リテラルを変更しようとするのは、次のような未定義の動作です。
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.
C の経験があれば、文字列は、最後の文字が null 文字である 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 など、役立つ関数がいくつかあります。バッファー サイズを確保しないと、メモリが破損したり、リモートで悪用可能なバグが発生したりします。
strcat() の最初の引数は、連結された文字列に十分なスペースを保持できる必要があります。そのため、結果を受け取るのに十分なスペースのあるバッファーを割り当ててください。
char bigEnough[64] = "";
strcat(bigEnough, "TEXT");
strcat(bigEnough, foo);
/* and so on */
strcat() は、2 番目の引数を最初の引数と連結し、結果を最初の引数に格納します。返される char* は単にこの最初の引数であり、便宜上のみです。
最初と 2 番目の引数が連結された、新しく割り当てられた文字列は取得されません。これは、コードに基づいて予想したことです。
静的に割り当てられたアドレスに文字列をコピーしようとしています。バッファに猫を入れる必要があります。
具体的には:
...をちょきちょきと切る...
行き先
Pointer to the destination array, which should contain a C string, and be large enough to contain the concatenated resulting string.
...をちょきちょきと切る...
http://www.cplusplus.com/reference/clibrary/cstring/strcat.html
ここにも例があります。
と同じことを行う独自の関数を作成できますが、strcat()
何も変更されません。
#define MAX_STRING_LENGTH 1000
char *strcat_const(const char *str1,const char *str2){
static char buffer[MAX_STRING_LENGTH];
strncpy(buffer,str1,MAX_STRING_LENGTH);
if(strlen(str1) < MAX_STRING_LENGTH){
strncat(buffer,str2,MAX_STRING_LENGTH - strlen(buffer));
}
buffer[MAX_STRING_LENGTH - 1] = '\0';
return buffer;
}
int main(int argc,char *argv[]){
printf("%s",strcat_const("Hello ","world")); //Prints "Hello world"
return 0;
}
両方の文字列を合わせて 1000 文字を超える場合、文字列は 1000 文字でカットされます。MAX_STRING_LENGTH
ニーズに合わせての値を変更できます。
これに似たものを試してください:
#include <stdio.h>
#include <string.h>
int main(int argc, const char * argv[])
{
// Insert code here...
char firstname[100], secondname[100];
printf("Enter First Name: ");
fgets(firstname, 100, stdin);
printf("Enter Second Name: ");
fgets(secondname,100,stdin);
firstname[strlen(firstname)-1]= '\0';
printf("fullname is %s %s", firstname, secondname);
return 0;
}
char* ではなく char[fixed_size] があると仮定すると、単一の創造的なマクロを使用して、<<cout<<like
順序付けを使用して一度にすべてを実行できます ("バラバラの %s ではなく %s\n"、"than"、"printfスタイル形式」)。*printf
組み込みシステムで作業している場合、この方法を使用すると、malloc や次のような関数の大規模なファミリを除外することもできますsnprintf()
(これにより、dietlibc が *printf について文句を言うこともありません)。
#include <unistd.h> //for the write example
//note: you should check if offset==sizeof(buf) after use
#define strcpyALL(buf, offset, ...) do{ \
char *bp=(char*)(buf+offset); /*so we can add to the end of a string*/ \
const char *s, \
*a[] = { __VA_ARGS__,NULL}, \
**ss=a; \
while((s=*ss++)) \
while((*s)&&(++offset<(int)sizeof(buf))) \
*bp++=*s++; \
if (offset!=sizeof(buf))*bp=0; \
}while(0)
char buf[256];
int len=0;
strcpyALL(buf,len,
"The config file is in:\n\t",getenv("HOME"),"/.config/",argv[0],"/config.rc\n"
);
if (len<sizeof(buf))
write(1,buf,len); //outputs our message to stdout
else
write(2,"error\n",6);
//but we can keep adding on because we kept track of the length
//this allows printf-like buffering to minimize number of syscalls to write
//set len back to 0 if you don't want this behavior
strcpyALL(buf,len,"Thanks for using ",argv[0],"!\n");
if (len<sizeof(buf))
write(1,buf,len); //outputs both messages
else
write(2,"error\n",6);