Làm cách nào để tạo một chuỗi các chuỗi trong C?


263

Tôi đang cố gắng tạo một chuỗi các chuỗi trong C. Nếu tôi sử dụng mã này:

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

gcc cho tôi "cảnh báo: gán từ loại con trỏ không tương thích". cách chính xác để làm điều này là gì?

chỉnh sửa: Tôi tò mò tại sao điều này sẽ đưa ra một cảnh báo trình biên dịch vì nếu tôi làm printf(a[1]);, nó sẽ in chính xác "hmm".

c  arrays  string 

12
Chỉ để ghi lại, char (*a[2])[14]là một mảng gồm hai con trỏ đến một mảng gồm 14 ký tự.
avakar

4
Tôi nghĩ rằng đó là mười bốn con trỏ đến mảng hai ký tự xD
fortran

74
Lời khuyên hữu ích nhất tôi từng đọc để giải mã các loại C: "Bắt đầu bằng tên, đọc đúng khi bạn có thể, bên trái khi bạn phải": char (*a[2])[14]- bắt đầu tại a, di chuyển sang phải: "mảng hai", di chuyển sang trái: "con trỏ tới", khung hoàn thành để đọc bên phải: "mảng forteen", đọc bên trái: "char" ... Đặt nó lại với nhau và chúng ta có "một mảng gồm hai con trỏ tới mảng cheen forteen"
Mark K Cowan

4
@dotancohen: Mẹo đó là điều cuối cùng đã thuyết phục tôi viết con trỏ char *strthay vì char* str. Đến từ nền tảng Delphi / Pascal, tôi đã rất quen với cách thứ hai cho đến khi tôi gặp các loại phức tạp hơn. Cách trước đây vẫn có vẻ xấu đối với tôi, nhưng làm cho ký hiệu loại phù hợp hơn (IMO).
Mark K Cowan

@MarkKCowan, một điều tuyệt vời! Cảm ơn! :)
Tiến sĩ Essen

Câu trả lời:


232

Nếu bạn không muốn thay đổi chuỗi, thì bạn có thể làm

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

Khi bạn làm như thế này, bạn sẽ phân bổ một mảng gồm hai con trỏ tới const char. Những con trỏ này sau đó sẽ được đặt thành địa chỉ của chuỗi tĩnh "blah""hmm".

Nếu bạn muốn có thể thay đổi nội dung chuỗi thực tế, bạn phải làm một cái gì đó như

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

Điều này sẽ phân bổ hai mảng liên tiếp charmỗi 14 giây, sau đó nội dung của các chuỗi tĩnh sẽ được sao chép vào chúng.


185

Có một số cách để tạo một chuỗi các chuỗi trong C. Nếu tất cả các chuỗi sẽ có cùng độ dài (hoặc ít nhất có cùng độ dài tối đa), bạn chỉ cần khai báo một mảng char 2 chiều và gán nếu cần:

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

Bạn cũng có thể thêm một danh sách các công cụ khởi tạo:

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

Điều này giả định kích thước và số chuỗi trong trình khởi tạo khớp với kích thước mảng của bạn. Trong trường hợp này, nội dung của từng chuỗi ký tự (chính nó là một mảng char kết thúc bằng 0) được sao chép vào bộ nhớ được phân bổ cho strs. Vấn đề với phương pháp này là khả năng phân mảnh nội bộ; nếu bạn có 99 chuỗi có 5 ký tự trở xuống, nhưng 1 chuỗi dài 20 ký tự, 99 chuỗi sẽ có ít nhất 15 ký tự không sử dụng; đó là một sự lãng phí không gian

Thay vì sử dụng mảng char 2-d, bạn có thể lưu trữ một mảng con trỏ 1-d vào char:

char *strs[NUMBER_OF_STRINGS];

Lưu ý rằng trong trường hợp này, bạn chỉ phân bổ bộ nhớ để giữ các con trỏ tới các chuỗi; bộ nhớ cho các chuỗi phải được phân bổ ở nơi khác (dưới dạng mảng tĩnh hoặc bằng cách sử dụng malloc()hoặc calloc()). Bạn có thể sử dụng danh sách trình khởi tạo như ví dụ trước:

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

Thay vì sao chép nội dung của các hằng chuỗi, bạn chỉ cần lưu trữ các con trỏ tới chúng. Lưu ý rằng hằng chuỗi có thể không ghi được; bạn có thể gán lại con trỏ, như vậy:

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

Nhưng bạn không thể thay đổi nội dung của chuỗi; I E,

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

có thể không được phép

Bạn có thể sử dụng malloc()để phân bổ động bộ đệm cho từng chuỗi và sao chép vào bộ đệm đó:

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

BTW,

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

Khai báo một mảng 2 phần tử con trỏ thành mảng 14 phần tử của char.


3
@Slater: có, nếu đó là kết quả của một malloccuộc gọi.
John Bode

Cảm ơn bạn cho câu trả lời rất chi tiết này. Điều này thực sự giúp tôi.
cokedude

1
Tại sao chúng ta chỉ có thể sử dụng strcpy trên mảng Chuỗi được khai báo là mảng 2D. Tại sao bài tập tiêu chuẩn thất bại?
Andrew S

4
@AndrewS: Câu trả lời hoàn chỉnh sẽ không phù hợp với một nhận xét, nhưng về cơ bản, đó là một sự giả tạo về cách C xử lý các biểu thức mảng; trong hầu hết các trường hợp, một biểu thức kiểu T [N]được chuyển đổi thành biểu thức kiểu T *và giá trị của biểu thức là địa chỉ của phần tử đầu tiên. Vì vậy, nếu bạn đã viết str = "foo", bạn sẽ cố gắng gán địa chỉ của ký tự đầu tiên "foo"cho mảng str, không hoạt động. Xem câu trả lời này để biết thêm chi tiết.
John Bode

@JohnBode bạn có thể vui lòng thêm các tinh chỉnh nhỏ? char *strs[NUMBER_OF_STRINGS] = {0}; Điều này giúp ngăn ngừa các vấn đề trong tương lai bằng cách khởi tạo strsđến NULL. Rất nhiều người đọc bài đăng này khi google tìm kiếm trên chuỗi các chuỗi trong C.
cokedude

94

Hành động! Chuỗi không đổi:

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

Nếu tôi nhớ chính xác.

Ồ, và bạn muốn sử dụng strcpy để gán, không phải toán tử =. strcpy_s an toàn hơn, nhưng nó không theo tiêu chuẩn C89 hay C99.

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

Cập nhật: Thomas nói strlcpylà con đường để đi.


Đó có phải là C99 không? Tôi không tin điều đó có thể xảy ra ở ANSI C.
Noldorin

6
Có thể có trong cả C89 và C99. Nó cũng không quan trọng dù nó có const hay không, mặc dù cái trước được ưa thích hơn.
avakar

1
Chà, const là mới và bạn đã từng phải chỉ định kích thước của mảng bên ngoài (3 trong trường hợp này), nhưng nếu không thì điều này hoàn toàn chấp nhận được K & R C. Tôi có một cuốn sách C cũ có bản quyền 1984 có một phần chỉ ra cách làm cái này. Họ gọi nó là "mảng rách rưới". Tất nhiên nó không có "toán tử" và strcpy_s là một cái mới đối với tôi.
TED

6
strcpy_s là một chức năng của Microsoft. Có lẽ nên tránh vì nó không ở trong tiêu chuẩn C.
Cromulent

5
strcpy_s và các "chức năng an toàn" khác được tiêu chuẩn hóa thành ISO / IEC TR 24731 (đây là một tiêu chuẩn được công bố ISO và như vậy không có sẵn trực tuyến miễn phí; dự thảo gần đây nhất là open-std.org/jtc1/sc22/wg14/www /docs/n1225.pdf )
Pavel Minaev

14

Dưới đây là một số tùy chọn của bạn:

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

Ưu điểm của a2là sau đó bạn có thể thực hiện các thao tác sau với chuỗi ký tự

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

Và đối với a3bạn có thể làm như sau:

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

Đối với a1bạn sẽ phải sử dụng strcpy()(tốt hơn nữa strncpy()) ngay cả khi gán chuỗi ký tự. Lý do là a2, và a3là các mảng của con trỏ và bạn có thể làm cho các phần tử của chúng (tức là con trỏ) trỏ đến bất kỳ bộ lưu trữ nào, trong khia1 là một mảng 'mảng ký tự' và vì vậy mỗi phần tử là một mảng "sở hữu" bộ lưu trữ của riêng nó ( có nghĩa là nó bị phá hủy khi đi ra khỏi phạm vi) - bạn chỉ có thể sao chép nội dung vào bộ lưu trữ của nó.

Điều này cũng mang đến cho chúng ta nhược điểm của việc sử dụng a2a3- vì chúng trỏ đến lưu trữ tĩnh (nơi lưu trữ chuỗi ký tự), nội dung không thể thay đổi đáng tin cậy (viz. Hành vi không xác định), nếu bạn muốn gán các ký tự không phải chuỗi cho các yếu tố của a2hoặca3 - trước tiên bạn sẽ phải tự động phân bổ đủ bộ nhớ và sau đó các phần tử của chúng trỏ đến bộ nhớ này, sau đó sao chép các ký tự vào nó - và sau đó bạn phải chắc chắn phân bổ bộ nhớ khi hoàn tất.

Bah - tôi nhớ C ++ rồi;)

ps Hãy cho tôi biết nếu bạn cần ví dụ.


Tôi cần mảng chuỗi cho một dự án Arduino. Cuối cùng, tôi sử dụng kiểu a2. Ban đầu, tôi đã thử kiểu a1 xác định mảng chuỗi của mình là char a1 [] [2] = {"F3", "G3" ... vv. } vì nó được dự định để lưu trữ các chuỗi dài 2 ký tự. Điều này mang lại đầu ra bất ngờ vì tôi quên bộ kết thúc null có nghĩa là mỗi chuỗi phải có kích thước ít nhất là 3 để lưu trữ 2 ký tự. Sử dụng kiểu a2, tôi không cần chỉ định độ dài của chuỗi và nó cũng có thể phù hợp với độ dài chuỗi khác nhau, vì vậy tôi đã quyết định gắn bó với điều đó :-)
Jeromy Adofo

char (* a3 []) [] = {& "blah", & "hmm"}; => không hoạt động trong g ++ Apple LLVM phiên bản 9.1.0, nhưng nó hoạt động trong gcc
1234

12

Hoặc bạn có thể khai báo một kiểu cấu trúc, chứa một ký tự arry (1 chuỗi), chúng tạo ra một mảng của các cấu trúc và do đó là một mảng nhiều phần tử

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 
 }

Một trong những lợi thế của điều này so với bất kỳ phương pháp nào khác là điều này cho phép bạn quét trực tiếp vào chuỗi mà không phải sử dụng strcpy;


10

Trong ANSI C:

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

8
@Zifre: Tôi hoàn toàn không đồng ý. Nó rất là một phần của loại - một "con trỏ char" trong trường hợp này. Dù sao thì bạn sẽ nói gì ... đó là một phần của tên biến? Tôi đã thấy nhiều lập trình viên có thẩm quyền sử dụng phong cách này.
Noldorin

14
Chỉ cần cho bất cứ ai khác đọc điều này, tôi muốn chỉ ra rằng Bjarne Stroustrup đặt * theo loại ...
MirroredFate

1
@MirroredFate: Đúng. Thật vậy, nó được khuyến khích thực hành trong C ++ từ những gì tôi biết. Về mặt ngữ nghĩa, tôi không có ý nghĩa gì khi đặt nó theo định danh, vì cách nó được sử dụng. : /
Noldorin

16
@Noldorin char* foo, bar;là loại bargì?
MOUOUD

10
C được phát triển bởi Dennis Ritchie vào năm 1972 và năm 1988, ông và Brian Kernighan đã xuất bản phiên bản thứ hai của K & R - Ngôn ngữ lập trình C, một cuốn sách được coi là tiêu chuẩn thực tế cho C. Họ đặt * bởi định danh.
Marius Lian

10

Nếu các chuỗi là tĩnh, tốt nhất bạn nên tắt:

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

Mặc dù không phải là một phần của ANSI C cơ bản, rất có thể môi trường của bạn hỗ trợ cú pháp. Các chuỗi này là bất biến (chỉ đọc) và do đó, trong nhiều môi trường sử dụng ít chi phí hơn là tự động xây dựng một chuỗi chuỗi.

Ví dụ, trong các dự án bộ điều khiển vi mô nhỏ, cú pháp này sử dụng bộ nhớ chương trình thay vì (thường) bộ nhớ ram quý hơn. AVR-C là một môi trường ví dụ hỗ trợ cú pháp này, nhưng hầu hết các môi trường khác cũng vậy.


10

Nếu bạn không muốn theo dõi số lượng chuỗi trong mảng và muốn lặp lại chúng, chỉ cần thêm chuỗi NULL vào cuối:

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

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

Tôi tin rằng điều này chỉ hợp lệ trong C ++. Trong C, NULL không được đảm bảo bằng 0, do đó vòng lặp có thể không bị hỏng khi cần. Đúng nếu tôi đã sai lầm.
Palec

2
Không có ý tưởng :) Bạn có thể so sánh với NULL trong câu lệnh while nếu bạn thích.
Serge

9

Các chuỗi ký tự là const char *s.

Và việc sử dụng dấu ngoặc đơn của bạn là số lẻ. Bạn có thể có nghĩa là

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

trong đó khai báo một mảng gồm hai con trỏ thành các ký tự không đổi và khởi tạo chúng để trỏ đến hai hằng chuỗi được mã hóa cứng.


3

Mã của bạn đang tạo ra một loạt các con trỏ hàm. Thử

char* a[size];

hoặc là

char a[size1][size2];

thay thế.

Xem wikibook để mảngcon trỏ


1
ngả mũ vì cách tiếp cận khác nhau của bạn ... Những người như bạn tạo ra chồng chất ...
Sahu V Kumar

1

xin chào bạn có thể thử dưới đây:

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

một ví dụ hay về việc sử dụng, mảng các chuỗi trong c nếu bạn muốn

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

Ở đây thử cái này !!!


1
bạn có thể giải thích tại sao bạn cần vòng lặp for với biến j, nghĩa là for (j = 0; j <1; j ++) không?
SouvikMaji

0

Tôi đã thiếu một chuỗi các chuỗi động hơn, trong đó số lượng chuỗi có thể thay đổi tùy theo lựa chọn thời gian chạy, nhưng nếu không thì các chuỗi phải được sửa.

Tôi đã kết thúc đoạn mã mã như thế này:

#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** evchọn con trỏ tới các chuỗi mảng và đếm số lượng chuỗi sử dụng _countofhàm. (Tương tự sizeof(arr) / sizeof(arr[0])).

Và có thêm Ansi để chuyển đổi unicode bằng A2Tmacro, nhưng đó có thể là tùy chọn cho trường hợp của bạn.


-6

Một cách tốt là xác định một chuỗi tự của bạn.

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

Nó thực sự đơn giản.


4
Bạn đang thiếu một ;, và làm thế nào điều này tạo ra một chuỗi các chuỗi ?
keyer
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.