ฉันจะสร้างอาร์เรย์ของสตริงใน C ได้อย่างไร


263

ฉันกำลังพยายามสร้างอาร์เรย์ของสตริงใน C หากฉันใช้รหัสนี้:

char (*a[2])[14];
a[0]="blah";
a[1]="hmm";

gcc ให้ฉัน "คำเตือน: การกำหนดจากประเภทตัวชี้ที่เข้ากันไม่ได้" วิธีที่ถูกต้องในการทำเช่นนี้คืออะไร?

แก้ไข: ฉันอยากรู้ว่าทำไมสิ่งนี้ควรให้คำเตือนคอมไพเลอร์เพราะถ้าฉันทำprintf(a[1]);มันพิมพ์ "hmm" อย่างถูกต้อง

c  arrays  string 

12
เพียงเพื่อบันทึกchar (*a[2])[14]เป็นอาร์เรย์ของสองตัวชี้ไปยังอาร์เรย์ 14 ตัวอักษร
avakar

4
ฉันคิดว่ามันเป็นสิบสี่ตัวชี้ไปยังอาร์เรย์สองตัวอักษร xD
Fortran

74
คำแนะนำที่เป็นประโยชน์ที่สุดที่ฉันเคยอ่านสำหรับการถอดรหัสประเภท C: "เริ่มต้นที่ชื่ออ่านทันทีเมื่อคุณสามารถออกจากเมื่อคุณต้อง": char (*a[2])[14]- เริ่มที่aย้ายไปทางขวา: "อาร์เรย์สอง" เลื่อนไปทางซ้าย: "ตัวชี้ไปที่" วงเล็บสมบูรณ์เพื่อให้อ่านได้ถูกต้อง: "อาร์เรย์สิบสี่", อ่านซ้าย: "ถ่าน" ... รวบรวมเข้าด้วยกันและเรามี "a คืออาร์เรย์ของสองพอยน์เตอร์ไปยังอาร์เรย์ของสิบสี่ตัวอักษร"
Mark K Cowan

4
@dotancohen: เคล็ดลับนั่นคือสิ่งที่บอกให้ฉันเขียนตัวชี้เป็นมากกว่าchar *str char* strมาจากพื้นหลังของ Delphi / Pascal ฉันคุ้นเคยกับวิธีการต่อไปจนกระทั่งฉันเจอรูปแบบที่ซับซ้อนมากขึ้น วิธีแรกยังคงดูน่าเกลียดสำหรับฉัน แต่ทำให้สัญกรณ์ประเภทสอดคล้องกันมากขึ้น (IMO)
Mark K Cowan

@ MarkKCowan หนึ่งที่น่าทึ่ง! ขอบคุณ! :)
ดร. เอสเซน

คำตอบ:


232

หากคุณไม่ต้องการเปลี่ยนสตริงคุณก็สามารถทำได้

const char *a[2];
a[0] = "blah";
a[1] = "hmm";

const charเมื่อคุณทำเช่นนี้คุณจะจัดสรรอาร์เรย์ของสองตัวชี้ไปที่ คำแนะนำเหล่านี้จะถูกตั้งค่าไปยังที่อยู่ของสตริงคงที่และ"blah""hmm"

หากคุณต้องการที่จะสามารถเปลี่ยนเนื้อหาสตริงจริงคุณต้องทำอะไรเช่น

char a[2][14];
strcpy(a[0], "blah");
strcpy(a[1], "hmm");

นี่จะจัดสรรสองอาร์เรย์ต่อเนื่องกัน 14 charวินาทีหลังจากนั้นเนื้อหาของสตริงแบบคงที่จะถูกคัดลอกลง


185

มีหลายวิธีในการสร้างอาร์เรย์ของสตริงใน C หากสตริงทั้งหมดจะมีความยาวเท่ากัน (หรืออย่างน้อยก็มีความยาวสูงสุดเท่ากัน) คุณเพียงประกาศอาร์เรย์ 2 มิติและกำหนดตามความจำเป็น:

char strs[NUMBER_OF_STRINGS][STRING_LENGTH+1];
...
strcpy(strs[0], aString); // where aString is either an array or pointer to char
strcpy(strs[1], "foo");

คุณสามารถเพิ่มรายการ initializers ได้เช่นกัน:

char strs[NUMBER_OF_STRINGS][STRING_LENGTH+1] = {"foo", "bar", "bletch", ...};

นี่จะถือว่าขนาดและจำนวนของสตริงใน initializer จับคู่กับขนาดอาร์เรย์ของคุณ ในกรณีนี้เนื้อหาของแต่ละสตริงตัวอักษร (ซึ่งเป็นตัวเองอาร์เรย์ถ่านสิ้นสุดลงเป็นศูนย์) จะถูกคัดลอกไปยังหน่วยความจำที่จัดสรรให้กับ STRs ปัญหาด้วยวิธีนี้คือความเป็นไปได้ของการแยกส่วนภายใน หากคุณมี 99 สายอักขระที่มี 5 ตัวอักษรหรือน้อยกว่า แต่ 1 สายที่มีความยาว 20 ตัวอักษร 99 สายจะมีตัวอักษรที่ไม่ได้ใช้อย่างน้อย 15 ตัว นั่นเป็นการสิ้นเปลืองพื้นที่

แทนที่จะใช้ถ่าน 2 มิติคุณสามารถจัดเก็บพอยน์เตอร์อาร์เรย์ 1 มิติเพื่อถ่าน:

char *strs[NUMBER_OF_STRINGS];

โปรดทราบว่าในกรณีนี้คุณได้จัดสรรหน่วยความจำให้เก็บตัวชี้ไว้กับสตริงเท่านั้น หน่วยความจำสำหรับสตริงจะต้องถูกจัดสรรที่อื่น (ไม่ว่าจะเป็นอาร์เรย์คงที่หรือโดยการใช้malloc()หรือcalloc()) คุณสามารถใช้รายการ initializer เช่นตัวอย่างก่อนหน้านี้:

char *strs[NUMBER_OF_STRINGS] = {"foo", "bar", "bletch", ...};

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

strs[i] = "bar";
strs[i] = "foo"; 

แต่คุณอาจไม่สามารถเปลี่ยนเนื้อหาของสตริงได้ กล่าวคือ

strs[i] = "bar";
strcpy(strs[i], "foo");

อาจไม่ได้รับอนุญาต

คุณสามารถใช้malloc()การจัดสรรบัฟเฟอร์แบบไดนามิกสำหรับแต่ละสตริงและคัดลอกไปยังบัฟเฟอร์นั้น:

strs[i] = malloc(strlen("foo") + 1);
strcpy(strs[i], "foo");

BTW,

char (*a[2])[14];

ประกาศเป็นอาร์เรย์ 2 พอยน์เตอร์ของพอยน์เตอร์ถึงอาร์เรย์ 14 องค์ประกอบของถ่าน


3
@ Slater: ใช่ถ้ามันเป็นผลของการmallocโทร
John Bode

ขอบคุณสำหรับคำตอบที่มีรายละเอียดมาก สิ่งนี้ช่วยฉันได้จริงๆ
cokeude

1
เหตุใดเราจึงสามารถใช้ strcpy บน String Array ที่ประกาศเป็นอาร์เรย์ 2 มิติเท่านั้น เหตุใดการกำหนดมาตรฐานจึงล้มเหลว
Andrew S

4
@AndrewS: คำตอบที่สมบูรณ์จะไม่เหมาะสมกับความคิดเห็น แต่โดยทั่วไปมันเป็นสิ่งประดิษฐ์ของวิธีการที่ C ปฏิบัติต่อการแสดงออกของอาร์เรย์; ภายใต้สถานการณ์ส่วนใหญ่นิพจน์ชนิดT [N]ถูกแปลงเป็นนิพจน์ชนิดT *และค่าของนิพจน์คือที่อยู่ขององค์ประกอบแรก ดังนั้นหากคุณเขียนstr = "foo"คุณจะพยายามกำหนดที่อยู่ของอักขระตัวแรกของ"foo"ให้กับอาร์เรย์strซึ่งใช้งานไม่ได้ ดูคำตอบนี้สำหรับรายละเอียดเพิ่มเติม
John Bode

@ JohnBode คุณช่วยเพิ่ม tweak ขนาดเล็กได้ไหม char *strs[NUMBER_OF_STRINGS] = {0}; ซึ่งจะช่วยป้องกันปัญหาในอนาคตโดยการเริ่มต้นที่จะstrs NULLผู้คนจำนวนมากอ่านโพสต์นี้เมื่อไม่ google ค้นหาบนอาร์เรย์ของสตริงใน C.
cokedude

94

Ack! สตริงคงที่:

const char *strings[] = {"one","two","three"};

ถ้าจำไม่ผิด

โอ้และคุณต้องการใช้strcpyสำหรับการมอบหมายไม่ใช่ตัวดำเนินการ = strcpy_sปลอดภัยกว่า แต่ก็ไม่ได้อยู่ในมาตรฐาน C89 หรือ C99

char arr[MAX_NUMBER_STRINGS][MAX_STRING_SIZE]; 
strcpy(arr[0], "blah");

ปรับปรุง: โทมัสพูดว่าstrlcpyเป็นวิธีที่จะไป


นั่นคือ C99 หรือเปล่า ผมไม่เชื่อว่ามันเป็นไปได้ใน ANSI ซี
Noldorin

6
เป็นไปได้ทั้งใน C89 และ C99 มันก็ไม่สำคัญว่าจะเป็นแบบมีหรือไม่มีแม้ว่าจะเป็นที่นิยมมากกว่าก็ตาม
avakar

1
const เป็นเรื่องใหม่และคุณเคยต้องระบุขนาดของอาเรย์ด้านนอก (3 ในกรณีนี้) แต่อย่างอื่นนี่เป็นที่ยอมรับได้อย่างสมบูรณ์แบบ K&R C. ฉันมีหนังสือ C ลิขสิทธิ์เก่า 1984 ที่มีส่วนแสดงวิธีการ ทำเช่นนี้. พวกเขาเรียกมันว่า "อาร์เรย์ขาด" แน่นอนว่ามันไม่มี "โอเปอเรเตอร์" และ strcpy_s เป็นสิ่งใหม่สำหรับฉัน
TED

6
strcpy_s เป็นฟังก์ชันของ Microsoft ควรหลีกเลี่ยงเพราะมันไม่ได้อยู่ในมาตรฐาน C.
347 Cromulent Cromulent

5
strcpy_s และ "ฟังก์ชั่นที่ปลอดภัย" อื่น ๆ ได้รับการรับรองมาตรฐาน ISO / IEC TR 24731 (เป็นมาตรฐาน ISO ที่เผยแพร่และดังนั้นจึงไม่มีให้บริการออนไลน์ฟรีแบบร่างล่าสุดคือopen-std.org/jtc1/sc22/wg14/www /docs/n1225.pdf )
Pavel Minaev

14

ตัวเลือกของคุณมีดังนี้:

char a1[][14] = { "blah", "hmm" };
char* a2[] = { "blah", "hmm" };
char (*a3[])[] = { &"blah", &"hmm" };  // only since you brought up the syntax -

printf(a1[0]); // prints blah
printf(a2[0]); // prints blah
printf(*a3[0]); // prints blah

ข้อดีของa2คือคุณสามารถทำสิ่งต่อไปนี้ด้วยตัวอักษรสตริง

a2[0] = "hmm";
a2[1] = "blah";

และสำหรับa3คุณอาจทำสิ่งต่อไปนี้:

a3[0] = &"hmm";
a3[1] = &"blah";

สำหรับa1คุณจะต้องใช้strcpy()(ยังดีกว่าstrncpy()) แม้เมื่อกำหนดตัวอักษรสตริง เหตุผลก็คือa2และa3เป็นอาเรย์ของพอยน์เตอร์และคุณสามารถสร้างองค์ประกอบของมัน (เช่นพอยน์เตอร์) ชี้ไปที่ที่เก็บข้อมูลใด ๆ ในขณะa1ที่อาเรย์ของ 'อาเรย์ของตัวอักษร' และแต่ละองค์ประกอบเป็นอาเรย์ ซึ่งหมายความว่ามันจะถูกทำลายเมื่อไม่ได้อยู่ในขอบเขต) - คุณสามารถคัดลอกข้อมูลลงในที่จัดเก็บได้เท่านั้น

สิ่งนี้ยังนำเราไปสู่ข้อเสียของการใช้a2และa3- เนื่องจากพวกเขาชี้ไปที่การจัดเก็บแบบคงที่ (ที่เก็บสตริงตัวอักษร) เนื้อหาที่ไม่สามารถเปลี่ยนแปลงได้อย่างน่าเชื่อถือ (viz. พฤติกรรมที่ไม่ได้กำหนด) ถ้าคุณต้องการกำหนดตัวอักษรที่ไม่ใช่สตริงให้ องค์ประกอบของa2หรือa3- คุณจะต้องจัดสรรหน่วยความจำให้เพียงพอก่อนจากนั้นให้องค์ประกอบของหน่วยความจำชี้ไปที่หน่วยความจำนี้แล้วคัดลอกอักขระลงในนั้น - จากนั้นคุณต้องแน่ใจว่าจะจัดสรรคืนหน่วยความจำ

Bah - ฉันคิดถึง C ++ แล้ว;)

ps แจ้งให้เราทราบหากคุณต้องการตัวอย่าง


ฉันต้องการอาร์เรย์สตริงสำหรับโครงการ Arduino ในตอนท้ายฉันใช้สไตล์ a2 ตอนแรกฉันลองใช้สไตล์ a1 ซึ่งกำหนดอาร์เรย์สตริงของฉันในลักษณะถ่าน a1 [] [2] = {"F3", "G3" ... ฯลฯ } ตามที่ตั้งใจไว้เพื่อเก็บสายอักขระยาว 2 ตัว สิ่งนี้ให้ผลลัพธ์ที่ไม่คาดคิดเพราะฉันลืม null-terminator หมายความว่าแต่ละสตริงควรมีขนาดอย่างน้อย 3 ตัวเพื่อเก็บอักขระ 2 ตัว ใช้สไตล์ a2 ฉันไม่จำเป็นต้องระบุความยาวของสตริงและมันสามารถรองรับความยาวของสตริงที่แตกต่างได้เช่นกันดังนั้นฉันจึงตัดสินใจที่จะยึดติดกับ :-)
Jeromy Adofo

ถ่าน (* a3 []) [] = {& "blah", & "hmm"}; => ไม่ทำงานใน g ++ Apple LLVM เวอร์ชัน 9.1.0 แต่ใช้งานได้ใน gcc
1234

12

หรือคุณสามารถประกาศประเภท struct ที่มีตัวอักษร arry (1 สตริง) พวกเขาสร้างอาร์เรย์ของ structs และทำให้อาร์เรย์หลายองค์ประกอบ

typedef struct name
{
   char name[100]; // 100 character array
}name;

main()
{
   name yourString[10]; // 10 strings
   printf("Enter something\n:);
   scanf("%s",yourString[0].name);
   scanf("%s",yourString[1].name);
   // maybe put a for loop and a few print ststements to simplify code
   // this is just for example 
 }

หนึ่งในข้อดีของนี้มากกว่าวิธีการอื่นใดก็คือว่านี้จะช่วยให้คุณสามารถสแกนโดยตรงในสายได้โดยไม่ต้องใช้strcpy;


10

ใน ANSI C:

char* strings[3];
strings[0] = "foo";
strings[1] = "bar";
strings[2] = "baz";

8
@Zifre: ฉันไม่เห็นด้วยอย่างยิ่ง มันมากเป็นส่วนหนึ่งของประเภท - "ตัวชี้ถ่าน" ในกรณีนี้ คุณจะพูดยังไงว่า ... มันเป็นส่วนหนึ่งของชื่อตัวแปร? ฉันเคยเห็นโปรแกรมเมอร์ที่มีความสามารถหลายคนใช้รูปแบบนี้
Noldorin

14
สำหรับคนอื่นที่อ่านข้อความนี้ฉันอยากจะชี้ให้เห็นว่า Bjarne Stroustrup ใส่ * ตามประเภท ...
MirroredFate

1
@MirroredFate: ถูกต้อง แน่นอนมันแนะนำให้ฝึกฝนใน C ++ จากสิ่งที่ฉันรู้ ฉันไม่ได้ใช้ความหมายในเชิงความหมายเพราะตัวระบุเพราะวิธีการใช้งาน : /
Noldorin

16
@ โนลอรินchar* foo, bar;ประเภทของbarอะไร?
mASOUD

10
C ได้รับการพัฒนาโดย Dennis Ritchie ในปี 1972 และในปี 1988 เขาและ Brian Kernighan ได้ตีพิมพ์ฉบับที่สองของ K&R - The C Programming Language หนังสือหลายเล่มถือเป็นมาตรฐานตามความเป็นจริงสำหรับ C. พวกเขาวาง * โดยตัวระบุ
Marius Lian

10

หากสตริงคงที่คุณจะดีที่สุดด้วย:

const char *my_array[] = {"eenie","meenie","miney"};

ในขณะที่ไม่ได้เป็นส่วนหนึ่งของ ANSI C พื้นฐานโอกาสของคุณคือสภาพแวดล้อมที่สนับสนุนไวยากรณ์ สตริงเหล่านี้ไม่เปลี่ยนรูปแบบ (อ่านอย่างเดียว) และในสภาพแวดล้อมจำนวนมากใช้ค่าใช้จ่ายน้อยกว่าการสร้างอาร์เรย์สตริงแบบไดนามิก

ตัวอย่างเช่นในโครงการไมโครคอนโทรลเลอร์ขนาดเล็กไวยากรณ์นี้ใช้หน่วยความจำของโปรแกรมมากกว่า (โดยปกติ) หน่วยความจำ RAM ที่มีค่ามากกว่า AVR-C เป็นสภาพแวดล้อมตัวอย่างที่สนับสนุนไวยากรณ์นี้ แต่ส่วนใหญ่จะทำในลักษณะอื่น


10

หากคุณไม่ต้องการติดตามจำนวนสตริงในอาร์เรย์และต้องการวนซ้ำสตริงเหล่านั้นเพียงเพิ่มสตริง NULL ในท้ายที่สุด:

char *strings[]={ "one", "two", "three", NULL };

int i=0;
while(strings[i]) {
  printf("%s\n", strings[i]);
  //do something
  i++;
};

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

2
ไม่มีความคิด :) คุณอาจเปรียบเทียบกับ NULL ในขณะที่งบถ้าคุณชอบ
Sergey

9

ตัวอักษรของสตริงคือconst char *s

และการใช้วงเล็บคือเลขคี่ คุณอาจหมายถึง

const char *a[2] = {"blah", "hmm"};

ซึ่งประกาศให้อาเรย์ของสองพอยน์เตอร์เป็นอักขระคงที่และเริ่มต้นให้ชี้ไปที่ค่าคงที่ของสตริง hardcoded สองค่า


3

รหัสของคุณกำลังสร้างอาร์เรย์ของพอยน์เตอร์ของฟังก์ชัน ลอง

char* a[size];

หรือ

char a[size1][size2];

แทน.

ดู wikibooks เพื่ออาร์เรย์และพอยน์เตอร์


1
หมวกปิดสำหรับวิธีการที่แตกต่างกันของคุณ ... คนอย่างคุณทำให้กองซ้อนล้น ...
Sahu V Kumar

1

สวัสดีคุณสามารถลองร้องนี้:

 char arr[nb_of_string][max_string_length]; 
 strcpy(arr[0], "word");

ตัวอย่างที่ดีของการใช้อาร์เรย์ของสตริงใน c ถ้าคุณต้องการ

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


int main(int argc, char *argv[]){

int i, j, k;

// to set you array
//const arr[nb_of_string][max_string_length]
char array[3][100];

char temp[100];
char word[100];

for (i = 0; i < 3; i++){
    printf("type word %d : ",i+1);
    scanf("%s", word);
    strcpy(array[i], word);
}

for (k=0; k<3-1; k++){
    for (i=0; i<3-1; i++)
    {
        for (j=0; j<strlen(array[i]); j++)
        {
            // if a letter ascii code is bigger we swap values
            if (array[i][j] > array[i+1][j])
            {
                strcpy(temp, array[i+1]);
                strcpy(array[i+1], array[i]);
                strcpy(array[i], temp);

                j = 999;
            }

            // if a letter ascii code is smaller we stop
            if (array[i][j] < array[i+1][j])
            {
                    j = 999;
            }

        }
    }
}

for (i=0; i<3; i++)
{
    printf("%s\n",array[i]);
}

return 0;
}

0
char name[10][10]
int i,j,n;//here "n" is number of enteries
printf("\nEnter size of array = ");
scanf("%d",&n);
for(i=0;i<n;i++)
{
    for(j=0;j<1;j++)
    {
        printf("\nEnter name = ");
        scanf("%s",&name[i]);
    }
}
//printing the data
for(i=0;i<n;i++)
{
    for(j=0;j<1;j++)
    {
        printf("%d\t|\t%s\t|\t%s",rollno[i][j],name[i],sex[i]);
    }
    printf("\n");
}

ลองนี่สิ !!!


1
คุณช่วยอธิบายได้ไหมว่าทำไมคุณต้องใช้ for-loop กับตัวแปร j เช่น for (j = 0; j <1; j ++)
SouvikMaji

0

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

ฉันมีโค้ดขนาดสั้นลงรหัสดังนี้:

#define INIT_STRING_ARRAY(...)          \
    {                                   \
        char* args[] = __VA_ARGS__;     \
        ev = args;                      \
        count = _countof(args);         \
    }

void InitEnumIfAny(String& key, CMFCPropertyGridProperty* item)
{
    USES_CONVERSION;
    char** ev = nullptr;
    int count = 0;

    if( key.Compare("horizontal_alignment") )
        INIT_STRING_ARRAY( { "top", "bottom" } )

    if (key.Compare("boolean"))
        INIT_STRING_ARRAY( { "yes", "no" } )

    if( ev == nullptr )
        return;

    for( int i = 0; i < count; i++)
        item->AddOption(A2T(ev[i]));

    item->AllowEdit(FALSE);
}

char** evหยิบตัวชี้ไปยังสตริงอาเรย์และนับเลือกจำนวนของสตริงโดยใช้_countofฟังก์ชั่น (คล้ายกับsizeof(arr) / sizeof(arr[0]))

และมี Ansi พิเศษในการแปลง Unicode โดยใช้A2Tแมโคร แต่อาจเป็นทางเลือกสำหรับกรณีของคุณ


-6

วิธีที่ดีคือการกำหนดสตริงของคุณเอง

#include <stdio.h>
typedef char string[]
int main() {
    string test = "string";
    return 0;
}

มันง่ายจริงๆ


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