ฉันจะต่อสตริง const / literal ใน C ได้อย่างไร


348

ฉันทำงานใน C และฉันต้องต่อสิ่งเล็ก ๆ น้อย ๆ

ตอนนี้ฉันมีสิ่งนี้:

message = strcat("TEXT ", var);

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

ตอนนี้ถ้าคุณมีประสบการณ์ใน C ฉันมั่นใจว่าคุณรู้ว่านี่เป็นข้อผิดพลาดในการแบ่งส่วนเมื่อคุณพยายามเรียกใช้ ดังนั้นฉันจะแก้ไขได้อย่างไร


6
ฉันอยากจะแนะนำให้คุณใช้ strlcat แทน strcat! gratisoft.us/todd/papers/strlcpy.html
activout.se

3
ฉันอยากจะแนะนำสิ่งนั้นอีกครั้ง Strcat ทำให้เกิดช่องโหว่ในการบัฟเฟอร์การเอารัดเอาเปรียบล้น บางคนสามารถให้ข้อมูลโปรแกรมของคุณที่ทำให้โค้ดรันเอง
ไบรอัน

คำตอบ:


389

ใน C "strings" เป็นเพียง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 buffer[1024];

ตรวจสอบให้แน่ใจว่าพารามิเตอร์แรกมีพื้นที่เพียงพอที่จะเก็บสิ่งที่คุณพยายามคัดลอกไว้ หากมีให้คุณจะปลอดภัยกว่าถ้าใช้ฟังก์ชันเช่น: strcpy_sและstrcat_sที่ที่คุณต้องระบุขนาดของบัฟเฟอร์ปลายทางอย่างชัดเจน

หมายเหตุ : สตริงตัวอักษรไม่สามารถใช้เป็นบัฟเฟอร์ได้เนื่องจากมันเป็นค่าคงที่ ดังนั้นคุณต้องจัดสรรอาร์เรย์ถ่านสำหรับบัฟเฟอร์เสมอ

ค่าที่ส่งคืนของstrcatสามารถถูกละเว้นได้เพียงคืนค่าตัวชี้เดียวกับที่ส่งผ่านเป็นอาร์กิวเมนต์แรก มันอยู่ที่นั่นเพื่อความสะดวกและช่วยให้คุณสามารถเชื่อมโยงการโทรเป็นรหัสหนึ่งบรรทัด:

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

ดังนั้นปัญหาของคุณสามารถแก้ไขได้ดังนี้:

char *foo = "foo";
char *bar = "bar";
char str[80];
strcpy(str, "TEXT ");
strcat(str, foo);
strcat(str, bar);

66
คุณจะช่วยให้ "ระวังตัวด้วย ... " ด้วยตัวหนาได้มั้ย สิ่งนี้ไม่สามารถถูกตรึงเครียดได้เพียงพอ การใช้ strcat, strcpy และ sprintf ในทางที่ผิดเป็นหัวใจของซอฟต์แวร์ที่ไม่เสถียร / ไม่ปลอดภัย
แท่นที่

12
คำเตือน: ตามที่เขียนไว้รหัสนี้จะทำให้เกิดช่องโหว่ขนาดใหญ่ในรหัสของคุณเพื่อหาช่องโหว่การบัฟเฟอร์ล้น
Brian

11
ไม่มีการใช้ประโยชน์จากบัฟเฟอร์มากเกินไปในตัวอย่างข้างต้น และใช่ฉันเห็นด้วยโดยทั่วไปฉันจะไม่ใช้ตัวอย่างข้างต้นสำหรับความยาวของสตริงที่ไม่ จำกัด ของ foo และ bar
Brian R. Bondy

13
@psihodelia: อย่าลืมว่าช้อนนั้นดีกว่าส้อมเลย! ดังนั้นต้องใช้ช้อนเสมอ!
Brian R. Bondy

20
ถึง @dolmen ที่สอง Joel Spolsky ได้เขียนบทความที่ค่อนข้างละเอียดเกี่ยวกับเรื่องนี้ ควรจะอ่านบังคับ ;-)
peter.slizik

247

หลีกเลี่ยงการใช้strcatในรหัส C วิธีที่ปลอดภัยที่สุดและที่สำคัญที่สุดคือวิธีใช้ที่ปลอดภัยที่สุดsnprintf:

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

ผู้แสดงความคิดเห็นบางคนหยิบยกปัญหาที่จำนวนข้อโต้แย้งอาจไม่ตรงกับสตริงรูปแบบและรหัสจะยังคงรวบรวม แต่คอมไพเลอร์ส่วนใหญ่ออกคำเตือนแล้วถ้าเป็นกรณีนี้


3
หมากฮอสเขากำลังพูดถึงวงเล็บรอบ "buf" ของอาร์กิวเมนต์ sizeof ไม่จำเป็นถ้าอาร์กิวเมนต์เป็นนิพจน์ แต่ฉันไม่เข้าใจว่าทำไมคุณถึงถูกลดระดับลง ฉันคิดว่าคำตอบของคุณดีที่สุดของทั้งหมดแม้ว่าจะเป็น c99 (อาจเป็นเพราะพวกเขาไม่เห็นด้วย! lamers!) +1
Johannes Schaub - litb

4
sizeof () ใช้งานได้ที่นี่เฉพาะสำหรับ char buf [... ] ไม่ใช่สำหรับ char * buf = malloc (... ) มีความแตกต่างไม่มากระหว่างอาร์เรย์และตัวชี้ แต่นี่เป็นหนึ่งในนั้น!
Mr.Ree

2
นอกจากนี้เขากำลังพยายามทำการเรียงต่อกัน การต่อการใช้งานsnprintf()เป็นเรื่องใหญ่
Leonardo Herrera

5
@MrRee: ความแตกต่างระหว่างพอยน์เตอร์และอาร์เรย์นั้นกว้างและสมบูรณ์! มันเป็นวิธีที่คุณใช้พวกมันซึ่งไม่ได้แตกต่างกันเสมอไป นอกจากนี้ตัวชี้และการจัดสรรแบบไดนามิกเป็นแนวคิดแนวตั้งฉากจริงๆ
การแข่งขัน Lightness ใน Orbit

34
หนึ่ง peeves สัตว์เลี้ยงของฉันเป็นคนชอบ @unwind ที่ยืนยันในความแตกต่างระหว่างจุดหมายและsizeof(x) sizeof xสัญกรณ์ที่วงเล็บจะใช้งานได้เสมอและสัญกรณ์ที่ไม่ได้จัดทำเครื่องหมายวงเล็บจะใช้งานได้ในบางครั้งเท่านั้นดังนั้นให้ใช้สัญกรณ์ที่วงเล็บ มันเป็นกฎง่าย ๆ ในการจดจำและปลอดภัย นี่เป็นข้อโต้แย้งทางศาสนา - ฉันมีส่วนร่วมในการพูดคุยกับผู้ที่คัดค้านมาก่อน - แต่ความเรียบง่ายของ 'ใช้วงเล็บเสมอ' มีค่ามากกว่าข้อดีใด ๆ ที่ไม่ได้ใช้ (IMNSHO แน่นอน) นี่คือการนำเสนอเพื่อความสมดุล
Jonathan Leffler

24

คนใช้ STR n cpy (), STR nแมว () หรือ s n printf ()
การเกินพื้นที่บัฟเฟอร์ของคุณจะทำลายสิ่งอื่นใดในหน่วยความจำ!
(และอย่าลืมเว้นช่องว่างสำหรับตัวอักษร "\ 0" ที่ต่อท้าย!)


3
ไม่เพียง แต่คุณควรจำไว้ว่าให้มีที่ว่างสำหรับอักขระ NULL แต่คุณต้องจำไว้ว่าต้องเพิ่มอักขระ NULL strncpy และ strncat ไม่ได้ทำเพื่อคุณ
แกรมเพอร์โรว์

เอ่อ? strncpy () และ strncat () ตรวจสอบว่าเพิ่มอักขระที่ยกเลิก ในความเป็นจริงพวกเขาเพิ่มมากเกินไป อย่างน้อยตราบใดที่มีพื้นที่เหลือในบัฟเฟอร์ซึ่งเป็นกับดักขนาดใหญ่ที่มีการเรียกเหล่านี้ ไม่แนะนำ.
ผ่อนคลาย

3
@unwind ฉันคิดว่าจุดของ Graeme คือถ้าบัฟเฟอร์มีขนาดเล็กเกินไป strncpy หรือ strncat จะไม่เพิ่มการยกเลิก '\ 0'
quinmars

2
snprintf ดี, strncpy / strncat เป็นคำแนะนำที่แย่ที่สุด, strlcpy / strlcat ดีกว่ามาก
Robert Gamble

9
strncpy()อย่าใช้ มันไม่ได้เป็น "ที่ปลอดภัย" strcpy()รุ่น อาเรย์ของตัวละครเป้าหมายอาจไม่จำเป็นต้องมีการเสริมด้วย'\0'อักขระพิเศษหรือแย่กว่านั้นมันอาจจะถูกทิ้งไว้โดยไม่ถูกทำลาย (เช่นไม่ใช่สตริง) (มันถูกออกแบบมาเพื่อใช้กับโครงสร้างข้อมูลที่ไม่ค่อยได้ใช้อีกต่อไปอาเรย์ตัวละครจะถูกเติมเต็มด้วย'\0'อักขระศูนย์หรือมากกว่า)
Keith Thompson

22

สตริงสามารถต่อกันได้ในเวลาคอมไพล์

#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 */ 
                ;

15

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
12431234123412341234123

5

อย่าลืมเตรียมใช้งานบัฟเฟอร์ผลลัพธ์ อาร์กิวเมนต์แรกของ strcat ต้องเป็นสตริงที่สิ้นสุดด้วยค่า null ซึ่งมีพื้นที่พิเศษเพียงพอที่จัดสรรให้กับสตริงผลลัพธ์:

char out[1024] = ""; // must be initialized
strcat( out, null_terminated_string ); 
// null_terminated_string has less than 1023 chars

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;
}

ฉันไม่แน่ใจว่าถูกต้อง / ปลอดภัย แต่ตอนนี้ฉันไม่สามารถหาวิธีที่ดีกว่านี้ใน ANSI C


<string.h>เป็นสไตล์ C ++ "string.h"คุณต้องการ คุณยังคำนวณstrlen(s1)สองครั้งซึ่งไม่จำเป็น s3ควรจะtotalLenght+1ยาว
Mooing Duck

4
@MooingDuck: "string.h"ไร้สาระ
sbi

ฉันไม่ได้ใช้สตริงแบบ C สักระยะหนึ่ง รู้สึกอิสระที่จะโพสต์รุ่นคงที่
นิลส์

4
@MooingDuck: ไม่ถูกต้อง #include <string.h>ถูกต้อง C. ใช้เครื่องหมายวงเล็บมุมสำหรับส่วนหัวมาตรฐานและระบบ (รวมถึง<string.h>) เครื่องหมายคำพูดสำหรับส่วนหัวที่เป็นส่วนหนึ่งของโปรแกรมของคุณ ( #include "string.h"จะเกิดขึ้นหากคุณไม่มีไฟล์ส่วนหัวของคุณเองโดยใช้ชื่อนั้น แต่ใช้<string.h>ต่อไป)
Keith Thompson

โปรดทราบว่าสิ่งนี้ขึ้นอยู่กับคุณลักษณะเฉพาะของ C99: การประกาศและคำสั่งที่ผสมกันและอาร์เรย์ความยาวผันแปร (VLAs) โปรดทราบว่า VLA ไม่มีกลไกในการตรวจจับหรือจัดการกับความล้มเหลวในการจัดสรร หากมีพื้นที่ไม่เพียงพอในการจัดสรร VLA พฤติกรรมของโปรแกรมของคุณจะไม่ได้กำหนด
Keith Thompson

4

มันเป็นพฤติกรรมที่ไม่ได้กำหนดเพื่อพยายามแก้ไขตัวอักษรสตริงซึ่งเป็นสิ่งที่ชอบ:

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.

4
จะเกิดอะไรขึ้นหาก var / foo / bar มีอักขระมากกว่า 1,000 ตัว > :)
Geo

1
จากนั้นคุณจะได้รับบัฟเฟอร์ล้นซึ่งคุณสามารถเพิ่มรหัสเพื่อตรวจสอบล่วงหน้า (พูดกับ strlen) แต่จุดประสงค์ของข้อมูลโค้ดคือเพื่อแสดงให้เห็นว่าบางสิ่งบางอย่างทำงานได้โดยไม่สร้างมลพิษด้วยรหัสเพิ่มเติมมากเกินไป มิฉะนั้นผมจะตรวจสอบความยาวไม่ว่าจะเป็น var / foo / บาร์เป็นโมฆะ ฯลฯ
paxdiablo

7
@paxdiablo: แต่คุณไม่ได้พูดถึงมันในคำตอบสำหรับคำถามที่มันดูเหมือนจะต้องพูดถึง ที่ทำให้คำตอบของคุณเป็นอันตราย คุณยังไม่ได้อธิบายว่าเพราะเหตุใดรหัสนี้จึงดีกว่ารหัสดั้งเดิมของ OP ยกเว้นตำนานที่ว่า "บรรลุผลเช่นเดียวกับต้นฉบับของคุณ" (ถ้าอย่างนั้นจะเป็นประเด็นอะไร? ต้นฉบับแตก !) ดังนั้นคำตอบ นอกจากนี้ยังไม่สมบูรณ์
การแข่งขัน Lightness ใน Orbit

หวังว่าจะแก้ปัญหาของคุณได้ที่ @PreferenceBean แต่ในเวลาที่เหมาะสมน้อยกว่าอุดมคติ :-) แจ้งให้เราทราบหากคุณยังมีปัญหากับคำตอบและฉันจะปรับปรุงให้ดีขึ้นต่อไป
paxdiablo

3

อาร์กิวเมนต์แรกของ strcat () จำเป็นต้องมีพื้นที่เพียงพอสำหรับสตริงที่ต่อกัน ดังนั้นจัดสรรบัฟเฟอร์ที่มีพื้นที่เพียงพอที่จะรับผล

char bigEnough[64] = "";

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

/* and so on */

strcat () จะต่อเชื่อมอาร์กิวเมนต์ที่สองเข้ากับอาร์กิวเมนต์แรกและเก็บผลลัพธ์ไว้ในอาร์กิวเมนต์แรก char ที่ส่งคืนเป็นเพียงอาร์กิวเมนต์แรกนี้และเพื่อความสะดวกของคุณเท่านั้น

คุณไม่ได้รับสตริงที่จัดสรรใหม่โดยมีอาร์กิวเมนต์ตัวแรกและตัวที่สองมาต่อกันซึ่งฉันเดาว่าคุณคาดหวังตามรหัสของคุณ


3

วิธีที่ดีที่สุดในการทำโดยไม่มีขนาดบัฟเฟอร์ที่ จำกัด คือใช้ asprintf ()

char* concat(const char* str1, const char* str2)
{
    char* result;
    asprintf(&result, "%s%s", str1, str2);
    return result;
}

2
คุณควรจะกลับมาไม่ได้char * ค่าตอบแทนจะต้องมีการส่งผ่านไปยังconst char * free
ต่อ Johansson

น่าเสียดายที่asprintfเป็นเพียงส่วนขยาย GNU
Calmarius

3

หากคุณมีประสบการณ์ใน C คุณจะสังเกตเห็นว่าสายอักขระเป็นเพียงอาร์เรย์ถ่านที่ตัวละครตัวสุดท้ายเป็นตัวละคร null

ตอนนี้มันค่อนข้างไม่สะดวกเท่าที่คุณต้องหาตัวละครตัวสุดท้ายเพื่อที่จะต่อท้ายบางอย่าง strcatจะทำเพื่อคุณ

ดังนั้น strcat ค้นหาอาร์กิวเมนต์แรกสำหรับอักขระ null จากนั้นจะแทนที่สิ่งนี้ด้วยเนื้อหาของอาร์กิวเมนต์ที่สอง (จนกว่าจะสิ้นสุดลงในค่า 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เพื่อตรวจสอบตัวเลือก

และตอนนี้จุดสำคัญ:

ตรวจสอบให้แน่ใจว่าบัฟเฟอร์มีพื้นที่เพียงพอที่จะเก็บข้อความและอักขระ null มีฟังก์ชั่นสองสามอย่างที่สามารถช่วยคุณได้เช่น strncat และ printf รุ่นพิเศษที่จัดสรรบัฟเฟอร์ให้คุณ การไม่มั่นใจขนาดบัฟเฟอร์จะนำไปสู่ความเสียหายของหน่วยความจำและบั๊กที่หาประโยชน์ได้จากระยะไกล


ประเภทของการ"TEXT"เป็นchar[5], ไม่ const char*มันสลายไปchar*ในบริบทส่วนใหญ่ ด้วยเหตุผลด้านความเข้ากันได้แบบย้อนหลังสตริงตัวอักษรไม่ได้constแต่พยายามปรับเปลี่ยนผลลัพธ์ในลักษณะการทำงานที่ไม่ได้กำหนด (ใน C ++ มีตัวอักษรสตริงอยู่const)
Keith Thompson

2

คุณสามารถเขียนฟังก์ชั่นของคุณเองที่ทำสิ่งเดียวกัน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;
}

หากทั้งสองสายรวมกันมีความยาวมากกว่า 1,000 ตัวอักษรก็จะตัดสายที่ 1,000 ตัว คุณสามารถเปลี่ยนค่าของMAX_STRING_LENGTHให้เหมาะกับความต้องการของคุณ


ฉันคาดว่าบัฟเฟอร์ล้นฉันเห็นคุณจัดสรรstrlen(str1) + strlen(str2)แต่คุณเขียนstrlen(str1) + strlen(str2) + 1ตัวอักษร ดังนั้นคุณสามารถเขียนฟังก์ชั่นของคุณเองได้หรือไม่?
Liviu

ว้าว! คุณไม่เคยเพิ่มหน่วยความจำน่ารังเกียจน่ารังเกียจ! return buffer; free(buffer);
Liviu

BTW sizeof(char) == 1(นอกจากนี้ยังมีข้อผิดพลาดอื่น ๆ อีกมากมาย ... ) คุณเห็นไหมว่าทำไมคุณไม่ต้องเขียนฟังก์ชั่นของคุณเอง?
Liviu

@Liviu free(buffer);ฉันทำฟรีหน่วยความจำที่บรรทัด
Donald Duck

1
free(buffer);หลังจากที่return buffer;ไม่เคยถูกประหารให้ดูใน debugger;) ฉันเห็นตอนนี้: ใช่คุณจะต้องเพิ่มหน่วยความจำในmainฟังก์ชั่น
Liviu

1

สมมติว่าคุณมีอักขระ [fixed_size] แทนที่จะเป็น char * คุณสามารถใช้มาโครเดียวที่สร้างสรรค์เพื่อทำสิ่งทั้งหมดพร้อมกันด้วยการ<<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);
  • หมายเหตุ 1 คุณมักจะไม่ใช้ argv [0] เช่นนี้ - เป็นเพียงตัวอย่าง
  • หมายเหตุ 2 คุณสามารถใช้ฟังก์ชันใด ๆ ที่ส่งออกอักขระ char * รวมถึงฟังก์ชันที่ไม่เป็นมาตรฐานเช่น itoa () สำหรับการแปลงจำนวนเต็มเป็นชนิดสตริง
  • หมายเหตุ 3 หากคุณใช้ printf อยู่แล้วในโปรแกรมของคุณไม่มีเหตุผลที่จะไม่ใช้ snprintf () เนื่องจากโค้ดที่คอมไพล์จะมีขนาดใหญ่กว่า (แต่ถูกแทรกและเร็วกว่ามาก)


1

คุณพยายามคัดลอกสตริงไปยังที่อยู่ที่ได้รับการจัดสรรแบบคงที่ คุณต้อง cat เป็นบัฟเฟอร์

โดยเฉพาะ:

... บุคคลที่ไม่สำคัญ ...

ปลายทาง

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

มีตัวอย่างที่นี่เช่นกัน


0

นี่คือทางออกของฉัน

#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");

0

ลองทำสิ่งที่คล้ายกับสิ่งนี้:

#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;
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.