私は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);
strcat
Cコードでの使用は避けてください。最もクリーンで、最も重要なのは、最も安全な方法は以下を使用することsnprintf
です。
char buf[256];
snprintf(buf, sizeof buf, "%s%s%s%s", str1, str2, str3, str4);
一部のコメンターは、引数の数がフォーマット文字列と一致しない可能性があり、コードは引き続きコンパイルされるという問題を提起しましたが、これが当てはまる場合、ほとんどのコンパイラーはすでに警告を発行しています。
snprintf()
は非常に重要です。
sizeof(x)
とsizeof x
。括弧付き表記は常に機能し、括弧なし表記は時々しか機能しないため、常に括弧付き表記を使用してください。覚えておくのは簡単なルールであり、安全です。これは宗教的な議論に入ります—私は以前に反対する人との議論に関与したことがあります—しかし、「常に括弧を使用する」という単純さは、それらを使用しないことのメリットよりも重要です(もちろん、IMNSHO)。これはバランスのために提示されています。
皆さん、str n cpy()、str n cat()、またはs n printf()を使用してください。
バッファスペースを超えると、メモリ内の他のすべてが破棄されます。
(そして、末尾のヌル '\ 0'文字にスペースを許可することを忘れないでください!)
strncpy()
。これはの「安全な」バージョンではありませんstrcpy()
。ターゲット文字配列は、余分な'\0'
文字で不必要に埋め込まれる場合があります。さらに悪いことに、文字列ではなく、終端されないままになる場合があります。(これは、めったに使用されないデータ構造で使用するために設計されました。文字配列の最後に0個以上の'\0'
文字が埋め込まれます。)
文字列は、コンパイル時に連結することもできます。
#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 */
;
また、事前に連結されている文字列の数がわからない場合は、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);
}
num_words>INT_MAX
かもしれないあなたが使用する必要があり、size_t
ためにi
出力バッファを初期化することを忘れないでください。strcatの最初の引数は、結果の文字列に十分な追加スペースが割り当てられたnullで終了する文字列である必要があります。
char out[1024] = ""; // must be initialized
strcat( out, null_terminated_string );
// null_terminated_string has less than 1023 chars
人々が指摘したように、文字列の処理は大幅に改善されました。したがって、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;
}
それが正しい/安全かどうかはわかりませんが、現時点では、ANSI Cでこれを行うためのより良い方法を見つけることができませんでした。
<string.h>
C ++スタイルです。あなたが欲しい"string.h"
。またstrlen(s1)
、2回計算する必要はありません。 長いs3
はずtotalLenght+1
です。
"string.h"
ナンセンスです。
#include <string.h>
正しいC.標準およびシステムヘッダー(を含む<string.h>
)には山かっこを使用し、プログラムの一部であるヘッダーには引用符を使用します。(#include "string.h"
その名前の独自のヘッダーファイルがない場合でも機能しますが、<string.h>
とにかく使用します。)
文字列リテラルを変更しようとするのは未定義の動作です。これは次のようなものです。
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.
strcat()の最初の引数は、連結された文字列のために十分なスペースを保持できる必要があります。したがって、結果を受け取るのに十分なスペースのあるバッファを割り当てます。
char bigEnough[64] = "";
strcat(bigEnough, "TEXT");
strcat(bigEnough, foo);
/* and so on */
strcat()は、2番目の引数を最初の引数と連結し、結果を最初の引数に格納します。返されるchar *は、単にこの最初の引数であり、ユーザーの便宜のためにのみ使用されます。
最初に割り当てられた引数と2番目の引数が連結された、新しく割り当てられた文字列は取得されません。これは、コードに基づいて予想したとおりです。
バッファサイズを制限せずにそれを行う最良の方法は、asprintf()を使用することです
char* concat(const char* str1, const char* str2)
{
char* result;
asprintf(&result, "%s%s", str1, str2);
return result;
}
char *
ないでくださいconst char *
。戻り値をに渡す必要がありfree
ます。
asprintf
はGNU拡張機能にすぎません。
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の特別なバージョンなど、いくつかの機能が役立ちます。バッファサイズを確認しないと、メモリが破損し、リモートで悪用可能なバグが発生します。
"TEXT"
あるchar[5]
、ありません const char*
。char*
ほとんどの状況で崩壊します。下位互換性の理由から、文字列リテラルはそうconst
ではありませんが、それらを変更しようとすると、未定義の動作が発生します。(C ++では、文字列リテラルはconst
です。)
同じことをする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
、ニーズに合わせて変更できます。
strlen(str1) + strlen(str2)
いますstrlen(str1) + strlen(str2) + 1
。割り当てられているようですが、文字を書き込んでいます。それで、あなたは本当にあなた自身の関数を書くことができますか?
return buffer; free(buffer);
sizeof(char) == 1
(他に、他にも微妙なエラーがあります...)独自の関数を記述する必要がない理由がわかりますか?
free(buffer);
。
free(buffer);
return buffer;
実行されなかった後は、デバッガーで確認してください;)わかりました:はい、main
関数のメモリを解放する必要があります
char *ではなくchar [fixed_size]があると仮定すると、単一のクリエイティブマクロを使用して、<<cout<<like
順序付けですべてを一度に実行できます(「むしろ%sはバラバラの%s \ n」、「より」、「printfスタイル形式」)。組み込みシステムで作業している場合、この方法を使用すると、mallocや次の*printf
ような大規模な関数ファミリーを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);
静的に割り当てられているアドレスに文字列をコピーしようとしています。バッファに猫を入れる必要があります。
具体的には:
...をちょきちょきと切る...
先
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
ここにも例があります。
これが私の解決策でした
#include <stdlib.h>
#include <stdarg.h>
char *strconcat(int num_args, ...) {
int strsize = 0;
va_list ap;
va_start(ap, num_args);
for (int i = 0; i < num_args; i++)
strsize += strlen(va_arg(ap, char*));
char *res = malloc(strsize+1);
strsize = 0;
va_start(ap, num_args);
for (int i = 0; i < num_args; i++) {
char *s = va_arg(ap, char*);
strcpy(res+strsize, s);
strsize += strlen(s);
}
va_end(ap);
res[strsize] = '\0';
return res;
}
ただし、連結する文字列の数を指定する必要があります
char *str = strconcat(3, "testing ", "this ", "thing");
これに似たものを試してください:
#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;
}