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의 배열에 대한 14 개의 포인터라고 생각했다
fortran

74
내가 C 타입을 해독하는데 가장 유용한 조언 : "이름에서 시작하고, 읽을 수있을 때 오른쪽으로 읽고, 필요할 때 왼쪽으로": char (*a[2])[14]-시작할 때 a, 오른쪽으로 이동 : "2의 배열", 왼쪽으로 이동 : "포인터", 대괄호가 완성되었으므로 오른쪽을 읽으십시오 : "teen of ateen", 왼쪽을 읽으십시오 : "char"... 그것을 합치면 "a는 십 개의 문자 배열에 대한 두 개의 포인터 배열입니다"
Mark K Cowan

4
@dotancohen : 그 팁은 마침내 포인터 char *str대신 포인터를 쓰도록 설득했습니다 char* str. 델파이 / 파스칼 배경에서 온 더 복잡한 유형을 만날 때까지 나는 후자의 방법에 매우 익숙했습니다. 전자의 방식은 여전히 ​​나에게보기에 좋지 않지만 형식 표기법을보다 일관성있게 만듭니다 (IMO).
Mark K Cowan

@MarkKCowan, 놀라운 것! 감사! :)
Dr. Essen

답변:


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

이렇게하면 char각각 14 초의 두 개의 연속 배열이 할당 되고 그 후에 정적 문자열의 내용이 복사됩니다.


185

C에서 문자열 배열을 만드는 방법은 여러 가지가 있습니다. 모든 문자열의 길이가 동일하거나 최소한 최대 길이가 같은 경우 char의 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");

이니셜 라이저 목록도 추가 할 수 있습니다.

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

이니셜 라이저의 문자열 크기와 개수가 배열 차원과 일치한다고 가정합니다. 이 경우 각 문자열 리터럴 (자체는 0으로 끝나는 char 배열)의 내용이 strs에 할당 된 메모리에 복사됩니다. 이 접근법의 문제점은 내부 단편화 가능성입니다. 5 자 이하인 99 개의 문자열이 있지만 20 자 길이의 1 개의 문자열이있는 경우 99 개의 문자열은 15 개의 사용하지 않은 문자를 갖습니다. 그것은 공간 낭비입니다.

char의 2 차원 배열을 사용하는 대신 char에 대한 1 차원 배열의 포인터를 저장할 수 있습니다.

char *strs[NUMBER_OF_STRINGS];

이 경우 문자열에 대한 포인터를 보유하기 위해 메모리 만 할당했습니다. 문자열 자체의 메모리는 다른 곳에서 할당해야합니다 (정적 배열 또는malloc() 또는 calloc()). 이전 예제와 같이 이니셜 라이저 목록을 사용할 수 있습니다.

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

a를 char의 14 요소 배열에 대한 포인터의 2 요소 배열로 선언합니다.


3
@Slater : 예, malloc전화 결과 인 경우 가능 합니다.
John Bode

이 매우 자세한 답변에 감사드립니다. 이것은 정말로 나를 도왔다.
cokedude

1
왜 우리는 2D 배열로 선언 된 String 배열에만 strcpy를 사용할 수 있습니까? 표준 할당이 왜 실패합니까?
Andrew S

4
@AndrewS : 완전한 대답은 주석에 맞지 않지만 기본적으로 C가 배열 표현식을 처리하는 방식의 인공물입니다. 대부분의 상황에서 type 표현식은 type 표현식으로 T [N]변환되며 표현식의 T *값은 첫 번째 요소의 주소입니다. 따라서을 쓴다면 str = "foo"첫 번째 문자의 주소를 "foo"배열 에 할당하려고하는데 str작동하지 않습니다. 자세한 내용은 이 답변 을 참조하십시오.
John Bode

@JohnBode 작은 조정을 추가 할 수 있습니까? 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 C에서 가능하다고 믿지 않습니다.
Noldorin

6
C89와 C99에서 모두 가능합니다. const가 있거나 없는지 여부는 중요하지 않지만 전자가 선호됩니다.
avakar

1
음, const는 새롭고 외부 배열의 크기를 지정해야했지만 (이 경우 3) K & R C는 완벽하게 받아 들일 수 있습니다. 이 작업을 수행. 그것들을 "정규 배열"이라고 부릅니다. 물론 그것은 "연산자"가 없었고 strcpy_s는 나에게 새로운 것입니다.
TED

6
strcpy_s는 Microsoft 함수입니다. 표준 C가 아니기 때문에 피해야합니다.
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- 그들은 (문자열 리터럴이 저장되는) 정적 스토리지를 가리 키 때문에 당신이 할당되지 않은 문자열 리터럴 원한다면의 내용은 신뢰성, (. 즉 정의되지 않은 동작을) 변경할 수 없습니다 a2또는의 요소a3 -먼저 충분한 메모리를 동적으로 할당 한 다음 해당 요소가이 메모리를 가리킨 다음 문자를 복사해야합니다. 그런 다음 완료되면 메모리 할당을 해제해야합니다.

Bah-C ++이 이미 그립습니다.)

ps 예가 필요하면 알려주십시오.


Arduino 프로젝트를 위해 문자열 배열이 필요했습니다. 결국에는 a2 스타일을 사용했습니다. 처음에는 문자열 배열을 char a1 [] [2] = { "F3", "G3"... etc로 정의하는 a1 스타일을 시도했습니다. } 2 문자 길이의 문자열을 저장하기위한 것입니다. null 종결자를 잊어 버렸기 때문에 예기치 않은 출력이 발생했습니다. 각 문자열은 2자를 저장하기 위해 적어도 3의 크기를 가져야한다는 것을 의미합니다. A2 스타일을 사용하여, 나는 문자열의 길이를 지정할 필요가 없었다, 그리고 내가 :-) 그와 막대기로 결정했습니다 아니라, 그래서 같은 문자열 길이를 변화 수용 할 수
제로미 Adofo

char (* a3 []) [] = {& "blah", & "hmm"}; => g ++ Apple LLVM 버전 9.1.0에서는 작동하지 않지만 gcc에서는 작동합니다
1234

12

또는 arry (1 string) 문자를 포함하는 구조체 유형을 선언하면 구조체의 배열과 다중 요소 배열을 만듭니다.

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 : 전적으로 동의하지 않습니다. 이 경우 "char pointer"라는 유형의 일부입니다. 어쨌든 당신은 무엇을 말할 것입니다 ... 변수 이름의 일부입니까? 유능한 프로그래머가이 스타일을 사용하는 것을 보았습니다.
Noldorin

14
이 글을 읽는 다른 누군가를 위해서, 나는 Bjarne Stroustrup이 유형별로 *를 넣었다는 것을 지적하고 싶다.
MirroredFate

1
@MirroredFate : 맞습니다. 실제로, 내가 아는 것에서 C ++로 연습하는 것이 좋습니다. 의미 상 사용 방법 때문에 식별자로 식별자를 넣는 것은 의미가 없습니다. : /
Noldorin

16
@Noldorin char* foo, bar;유형은 bar무엇입니까?
mASOUD

10
C는 1972 년 Dennis Ritchie에 의해 개발되었으며 1988 년에 Brian Kernighan과 C는 사실상 C의 표준으로 보유하고있는 책인 K & R-The C Programming Language의 두 번째 판을 출판했습니다.
마리우스 리안

10

문자열이 정적 인 경우 다음을 사용하는 것이 가장 좋습니다.

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

기본 ANSI C의 일부는 아니지만 환경에서 구문을 지원할 가능성이 있습니다. 이러한 문자열은 변경할 수 없으므로 (읽기 전용) 많은 환경에서 문자열 배열을 동적으로 작성하는 것보다 적은 오버 헤드를 사용합니다.

예를 들어 소규모 마이크로 컨트롤러 프로젝트에서이 구문은 일반적으로 더 소중한 램 메모리 대신 프로그램 메모리를 사용합니다. 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은 0이 보장되지 않으므로 루프가 중단 될 때 루프가 끊어지지 않을 수 있습니다. 내가 틀렸다면 나를 바로 잡으십시오.
Palec

2
모름 :) 원하는 경우 while 문에서 NULL과 비교할 수 있습니다.
Sergey

9

문자열 리터럴은 const char *s입니다.

그리고 당신의 괄호 사용은 이상합니다. 당신은 아마 의미

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

상수 문자에 대한 두 개의 포인터 배열을 선언하고 두 개의 하드 코딩 된 문자열 상수를 가리 키도록 초기화합니다.



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
변수 j와 함께 for-loop가 필요한 이유, 즉 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배열 문자열에 대한 포인터를 선택하고 count는 _countof함수를 사용하여 문자열의 양을 선택합니다 . (와 유사 sizeof(arr) / sizeof(arr[0])).

그리고 A2T매크로를 사용하여 유니 코드 변환을위한 추가 Ansi가 있지만 귀하의 경우 선택 사항 일 수 있습니다.


-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.