C dilinde bir dizeyi nasıl oluştururum?


263

C dizeleri bir dizi oluşturmaya çalışıyorum. Bu kodu kullanırsanız:

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

gcc bana "uyarı: uyumsuz işaretçi türünden atama" veriyor. Bunu yapmanın doğru yolu nedir?

edit: Eğer bunu yaparsam printf(a[1]);, doğru "hmm" yazdırır çünkü bu neden bir derleyici uyarı vermek merak ediyorum .

c  arrays  string 

12
Sadece kayıt için, char (*a[2])[14]14 karakterlik bir diziye iki işaretçi dizisidir.
avakar

4
İki karakter dizisi için on dört işaretçi olduğunu düşündüm xD
fortran

74
C türlerini deşifre etmek için şimdiye kadar okuduğum en faydalı tavsiye: "Addan başla, mümkün olduğunda sağa oku, gerektiğinde sola": char (*a[2])[14]- şuradan başla, asağa taşı: "iki dizi", sola taşı: "işaretçi", sol okuma "forteen dizisi": braket doğru okumak o kadar tamamlamak araya geldiğinde ise ... "karakter" ve sahip olduğumuz "bir forteen karakterlerin diziler için iki işaretçiler dizidir"
Mark K Cowan

4
@dotancohen: Bu ipucu sonunda beni işaretçiler yazmaya char *strdeğil de ikna etmeye ikna etti char* str. Delphi / Pascal geçmişinden gelince, daha karmaşık türlerle karşılaşana kadar ikinci yola çok alışkındım. Eski yol bana hala çirkin görünüyor, ancak tip gösterimini daha tutarlı hale getiriyor (IMO).
Mark K Cowan

@ MarkKCowan, inanılmaz biri! Teşekkürler! :)
Dr Essen

Yanıtlar:


232

Dizeleri değiştirmek istemiyorsanız,

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

Bunu yaptığınızda, iki işaretçi dizisi tahsis edeceksiniz const char. Bu işaretçileri sonra statik dizeleri adreslerine ayarlanacaktır "blah"ve "hmm".

Gerçek dize içeriğini değiştirmek istiyorsanız, aşağıdaki gibi bir şey yapmanız gerekir

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

Bu, charher biri 14 saniyelik iki ardışık dizi tahsis edecektir , bundan sonra statik dizelerin içeriği bunlara kopyalanacaktır.


185

C'de bir dizgi dizisi oluşturmanın birkaç yolu vardır. Tüm dizeler aynı uzunlukta olacaksa (veya en azından aynı maksimum uzunluğa sahip olacaksa), 2 boyutlu karakter dizisini bildirmeniz ve gerekirse atamanız yeterlidir:

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

Başlatıcıların bir listesini de ekleyebilirsiniz:

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

Bu, başlatıcıdaki dizelerin boyutunun ve sayısının dizi boyutlarınızla eşleştiğini varsayar. Bu durumda, her dize değişmezinin içeriği (kendisi sıfır sonlu bir char dizisidir) strs'ye ayrılan belleğe kopyalanır. Bu yaklaşımdaki sorun, iç parçalanma olasılığıdır; 5 veya daha az karakter içeren 99 dizeniz varsa, ancak 20 karakter uzunluğunda 1 dizeniz varsa, 99 dizede en az 15 kullanılmayan karakter olacaktır; bu bir yer kaybı.

2-b karakter dizisi kullanmak yerine, 1-b işaretçiler dizisini char olarak kaydedebilirsiniz:

char *strs[NUMBER_OF_STRINGS];

Bu durumda, yalnızca işaretçileri dizelere tutmak için bellek ayırdığınızı unutmayın; (statik diziler olarak kullanarak veya kendileri başka bir yerde tahsis edilmelidir dizeleri bellek malloc()veya calloc()). Başlatıcı listesini önceki örnek gibi kullanabilirsiniz:

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

Dize sabitlerinin içeriğini kopyalamak yerine, yalnızca işaretçileri saklıyorsunuz. Dize sabitlerinin yazılamayabileceğini unutmayın; işaretçiyi aşağıdaki gibi yeniden atayabilirsiniz:

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

Ancak dizenin içeriğini değiştiremeyebilirsiniz; yani

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

izin verilmeyebilir.

Sen kullanabilirsiniz malloc()dinamik olarak her dizesi için tampon tahsis etmek ve bu tampon kopyalamak:

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

BTW,

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

A'yı, 2 elemanlı char dizisine işaret eden 2 elemanlı bir dizi olarak bildirir.


3
@Slater: evet, eğer bir mallocçağrının sonucu ise .
John Bode

Bu çok ayrıntılı cevap için teşekkür ederim. Bu bana gerçekten yardımcı oldu.
cokedude

1
Neden sadece 2B dizi olarak bildirilen String dizilerinde strcpy kullanabiliriz? Standart atama neden başarısız oluyor?
Andrew S

4
@AndrewS: Yanıtın tamamı bir yoruma sığmaz, ancak temel olarak C, dizi ifadelerini nasıl ele aldığını gösterir; çoğu durumda, tür ifadesi tür ifadesine T [N]dönüştürülür ve ifadenin T *değeri ilk öğenin adresidir. Bu nedenle str = "foo", yazarsanız "foo"dizinin ilk karakterinin adresini strçalışmaz. Daha fazla ayrıntı için bu cevaba bakınız.
John Bode

@JohnBode küçük ayarlamayı ekler misiniz? char *strs[NUMBER_OF_STRINGS] = {0}; Bu başlatılıyor ileride çıkabilecek sorunları önlemeye yardımcı olur strsiçin NULL.
Do go

94

Ah! Sabit dizeler:

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

Eğer doğru hatırlıyorsam.

Oh, ve atama için strcpy kullanmak istiyorsunuz , = işleci değil. strcpy_s daha güvenlidir, ancak ne C89'da ne de C99 standartlarındadır.

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

Güncelleme: Thomas , strlcpygidilecek yol olduğunu söylüyor .


Bu C99 mu? ANSI C'de mümkün olduğuna inanmıyorum
Noldorin

6
Hem C89 hem de C99'da mümkündür. Birincisi tercih edilmesine rağmen, const ile veya onsuz olması da önemli değildir.
avakar

1
Const yeni ve dış dizinin boyutunu belirtmek zorunda kaldınız (bu durumda 3), ama aksi takdirde bu tamamen kabul edilebilir K&R C'dir. Telif hakkıyla korunan eski bir C kitabım var. Bunu yap. Buna "düzensiz dizi" diyorlar. Tabii ki "operatörleri" yoktu ve strcpy_s benim üzerimde yeni bir tane.
TED

6
strcpy_s bir Microsoft işlevidir. Muhtemelen kaçınılmalıdır çünkü standart C'de değildir.
Cromulent

5
strcpy_s ve diğer "güvenli işlevler" ISO / IEC TR 24731 olarak standartlaştırılmıştır (ISO tarafından yayınlanan bir standarttır ve bu nedenle çevrimiçi olarak ücretsiz olarak sunulmamaktadır ; en son taslak açık- std.org/jtc1/sc22/wg14/www /docs/n1225.pdf )
Pavel Minaev

14

İşte seçeneklerinizden bazıları:

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

Bunun avantajı, a2dize değişmezleriyle aşağıdakileri yapabilmenizdir

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

Ve a3sizin için aşağıdakileri yapabilirsiniz:

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

Çünkü dize değişmez değerleri atarken bile (daha iyisi ) a1kullanmanız gerekecektir . Bunun nedeni , ve işaretçiler dizileridir ve öğelerini (yani işaretçiler) herhangi bir depolamayı gösterebilirsiniz, oysa 'dizi karakter dizisi' dizisidir ve bu nedenle her öğe kendi deposuna "sahip" bir dizidir ( yani kapsam dışına çıktığında imha edilir) - yalnızca depolama alanına bir şeyler kopyalayabilirsiniz.strcpy()strncpy()a2a3a1

Bu aynı zamanda bizi kullanmanın dezavantajını da beraberinde getirir a2ve a3- içeriği dizgisel olmayan değişmezleri atamak istiyorsanız, içeriği güvenilir bir şekilde değiştirilemeyen (yani tanımlanmamış davranış) statik depolamaya (dize değişmezlerinin depolandığı yer) işaret ettikleri için a2veya a3- öğelerini önce dinamik olarak yeterli miktarda bellek ayırmanız ve ardından öğelerinin bu belleği göstermesi ve ardından karakterleri belleğe kopyalamanız gerekir - ve sonra bitince belleği yeniden yerleştirdiğinizden emin olmanız gerekir.

Bah - Zaten C ++ özledim;)

ps Örneklere ihtiyacınız varsa bana bildirin.


Bir Arduino projesi için dizi dizilerine ihtiyacım vardı. Sonunda a2 stilini kullandım. Başlangıçta karakter dizisi char a1 [] [2] = {"F3", "G3" ... vb tanımlayan a1 tarzı denedim. } 2 karakter uzunluğunda dizeleri saklamayı amaçladığı için. Boş-sonlandırıcı her dize 2 karakter saklamak için en az 3 boyutunda olması gerektiği anlamına unuttum çünkü bu beklenmedik çıktı verdi. A2 stilini kullanarak, dizenin uzunluğunu belirtmek zorunda değildim ve bu da değişen dize uzunluklarını da barındırabilirdi, bu yüzden buna sadık kalmaya karar verdim :-)
Jeromy Adofo 18:19

char (* a3 []) [] = {& "blah", & "hmm"}; => g ++ Apple LLVM sürüm 9.1.0'da çalışmaz, ancak gcc'de çalışır
1234

12

Veya bir arry karakteri (1 dize) içeren bir yapı türü bildirebilir, yapıların bir dizisini ve böylece çok öğeli bir diziyi oluştururlar

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 
 }

Bunun diğer herhangi bir yönteme göre avantajlarından biri, bunun kullanmak zorunda kalmadan doğrudan dizeye taramanıza izin vermesidir strcpy;


10

ANSI C'de:

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

8
@Zifre: Tamamen katılmıyorum. Bu tür bir parçasıdır - bu durumda bir "char pointer". Yine de ne söylerdiniz ... bu değişken adının bir parçası mı? Birçok yetkili programcının bu stili kullandığını gördüm.
Noldorin

14
Sadece bunu okuyan herkes için, Bjarne Stroustrup'un * türüne göre koyduğunu belirtmek isterim ...
MirroredFate

1
@MirroredFate: Doğru. Gerçekten de, bildiğim kadarıyla C ++ 'da pratik yapılması önerilir. Anlamsal olarak, kullanım şekli nedeniyle tanımlayıcı tarafından koymak benim için hiçbir anlam ifade etmiyor. : /
Noldorin

16
@Noldorin char* foo, bar;türü barnedir?
mASOUD

10
C, Dennis Ritchie tarafından 1972'de geliştirildi ve 1988'de kendisi ve Brian Kernighan, C için fiili standart olarak birçok kitap olan K&R - C Programlama Dili'nin ikinci baskısını yayınladılar.
Marius Lian

10

Dizeler statikse, aşağıdakileri yapabilirsiniz:

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

Temel ANSI C'nin bir parçası olmasa da, ortamınız sözdizimini destekliyor olabilir. Bu dizeler değişmezdir (salt okunur) ve bu nedenle birçok ortamda dinamik olarak bir dize dizisi oluşturmaktan daha az ek yük kullanır.

Örneğin, küçük mikro denetleyici projelerinde, bu sözdizimi (genellikle) daha değerli ram belleği yerine program belleği kullanır. AVR-C, bu sözdizimini destekleyen örnek bir ortamdır, ancak diğerlerinin çoğunda olduğu gibi.


10

Dizideki dizelerin sayısını izlemek ve bunların üzerinde yineleme yapmak istemiyorsanız, sonuna NULL dizesi ekleyin:

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

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

Bu sadece C ++ geçerli olduğunu düşünüyorum. C'de NULL değerinin sıfır olduğu garanti edilmez, bu nedenle döngü gerektiğinde kırılmayabilir. Eğer Yanlışsam beni düzelt.
Palec

2
Fikrim yok :) İsterseniz while deyiminde NULL ile karşılaştırabilirsiniz.
Sergey

9

Dize değişmez değerleri const char *s'dir.

Ve parantez kullanımınız tuhaf. Muhtemelen demek istiyorsun

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

sabit karakterlere iki işaretçi dizisini bildirir ve bunları iki sabit kodlu dize sabitine işaret edecek şekilde başlatır.


3

Kodunuz bir dizi işlev işaretçisi oluşturuyor. Deneyin

char* a[size];

veya

char a[size1][size2];

yerine.

Diziler ve işaretçiler için wikibooksları görün


1
Farklı yaklaşımınız için şapkalar çıkarıyor ... Sizin gibi insanlar taşmak için yığın yapıyor ...
Sahu V Kumar

1

merhaba bu feryat deneyebilirsiniz:

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

güzel bir örnek, isterseniz c dizeleri dizisi

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

İşte bunu deneyin !!!


1
for-loop'a neden j değişkeni ile ihtiyacınız olduğunu açıklayabilir misiniz, yani, (j = 0; j <1; j ++)?
SouvikMaji

0

Ben dizeleri miktarı çalışma zamanı seçimine bağlı olarak değişebilir dizeleri bir şekilde daha dinamik dizi eksik, ama aksi takdirde dizeleri düzeltilmelidir.

Kod kod pasajını şu şekilde kodladım:

#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** evdizgiyi dizi dizelerine alır ve sayım _countofişlevi kullanarak dize sayısını alır . (Buna benzer sizeof(arr) / sizeof(arr[0])).

Ayrıca A2Tmakro kullanarak ek Ansi'den unicode'a dönüşüm var , ancak bu sizin durumunuz için isteğe bağlı olabilir.


-6

İyi bir yol, bir dizeyi kendiniz tanımlamaktır.

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

Gerçekten bu kadar basit.


4
Eksik bir a ;ve bu bir dizi dizeyi nasıl oluşturur ?
keyser
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.