Inicjalizacja tablicy znaków C

119

Nie jestem pewien, co będzie w tablicy char po inicjalizacji w następujący sposób.

1. char buf[10] = "";
2. char buf[10] = " ";
3.char buf[10] = "a";

W przypadku 2 myślę, że buf[0]powinno być ' ', buf[1]powinno być '\0'i od buf[2]do buf[9]będzie losową zawartością. W przypadku 3 myślę, że buf[0]powinno być 'a', buf[1]powinno być „\ 0”, a od buf[2]do buf[9]będzie losową zawartością.

Czy to jest poprawne?

A w przypadku 1, co będzie w buf? buf[0] == '\0'a od buf[1]do buf[9]będzie losową zawartością?

lkkeepmoving
źródło
2
Mój kompilator nie akceptuje Twojego (poprawionego) kodu: „typu tablicy 'char [10]' nie można przypisać”.
Martin R
@MartinR teraz będzie działać ...
lkkeepmoving
1
@lkkeepmoving: char buf[10]; buf = "a";czy nie skompilować. - Proszę spróbować najpierw, a następnie skopiuj / wklej rzeczywisty kod w pytaniu. To oszczędza dużo pracy Tobie i wszystkim czytelnikom twojego pytania.
Martin R
@MartinR Przepraszamy za to. Myślałem, że mogę przypisać buf [] drugie, ale wygląda na to, że nie. Teraz kod działa.
lkkeepmoving

Odpowiedzi:

222

Nie jest to sposób inicjalizacji tablicy, ale dla:

  1. Pierwsza deklaracja:

    char buf[10] = "";

    jest równa

    char buf[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
  2. Druga deklaracja:

    char buf[10] = " ";

    jest równa

    char buf[10] = {' ', 0, 0, 0, 0, 0, 0, 0, 0, 0};
  3. Trzecia deklaracja:

    char buf[10] = "a";

    jest równa

    char buf[10] = {'a', 0, 0, 0, 0, 0, 0, 0, 0, 0};

Jak widać, nie ma przypadkowej zawartości: jeśli jest mniej inicjatorów, pozostała część tablicy jest inicjalizowana za pomocą 0. Dzieje się tak nawet wtedy, gdy tablica jest zadeklarowana wewnątrz funkcji.

ouah
źródło
47
Ze względu na osobę zadającą pytanie warto zwrócić uwagę, że standard C wymaga, aby każda częściowo pełna inicjalizacja tablicy była dopełniona zerami dla pozostałych elementów (przez kompilator). Dotyczy to wszystkich typów danych, nie tylko char.
niełuskany
4
@ouah, dlaczego na końcu buf [] nie ma znaku „\ 0”?
lkkeepmoving
14
@lkkeepmoving 0i '\0mają tę samą wartość.
ouah,
1
@lkkeepmoving Inicjalizacja i przypisanie to dwie różne bestie, dlatego C pozwala na podanie ciągu znaków jako inicjalizatora tablicy char, ale zabrania przypisywania tablic (jak powiedział ouah).
Lorenzo Donati - Codidact.com
3
@Pacerier char buff[3] = "abcdefghijkl";jest nieprawidłowy. char p3[5] = "String";jest również nieprawidłowy. char p[6] = "String";jest ważny i jest taki sam jak char p[6] = {'S', 't', 'r', 'i', 'n', 'g'};.
ouah
28

Edycja: OP (lub redaktor) po cichu zmienił niektóre pojedyncze cudzysłowy w oryginalnym pytaniu na podwójne cudzysłowy w pewnym momencie po udzieleniu tej odpowiedzi.

Twój kod spowoduje błędy kompilatora. Twój pierwszy fragment kodu:

char buf[10] ; buf = ''

jest podwójnie nielegalne. Po pierwsze, w C nie ma czegoś takiego jak pusty char. Możesz użyć podwójnych cudzysłowów, aby oznaczyć pusty ciąg, tak jak w przypadku:

char* buf = ""; 

To da ci wskaźnik do NULłańcucha, tj. Jednoznakowego łańcucha zawierającego tylko NULznak. Ale nie możesz używać pojedynczych cudzysłowów, które nie zawierają niczego - to jest nieokreślone. Jeśli chcesz wyznaczyć NULpostać, musisz to określić:

char buf = '\0';

Ukośnik odwrotny jest niezbędny do ujednoznacznienia znaku '0'.

char buf = 0;

prowadzi do tego samego, ale myślę, że to pierwsze jest nieco mniej dwuznaczne.

Po drugie, nie można zainicjować tablic po ich zdefiniowaniu.

char buf[10];

deklaruje i definiuje tablicę. Identyfikator tablicy bufjest teraz adresem w pamięci i nie można zmienić miejsca bufwskazującego poprzez przypisanie. Więc

buf =     // anything on RHS

jest nielegalne. Z tego powodu Twoje drugie i trzecie fragmenty kodu są nielegalne.

Aby zainicjować tablicę, musisz to zrobić w momencie definiowania:

char buf [10] = ' ';

poda 10-znakową tablicę, gdzie pierwszy znak to spacja, '\040'a reszta to NULnp '\0'. Gdy tablica jest zadeklarowana i zdefiniowana za pomocą inicjatora, elementy tablicy (jeśli istnieją) poza tymi z określonymi wartościami początkowymi są automatycznie uzupełniane 0. Nie będzie żadnej „przypadkowej zawartości”.

Jeśli zadeklarujesz i zdefiniujesz tablicę, ale jej nie zainicjujesz, jak w poniższym przykładzie:

char buf [10];

będziesz mieć losową zawartość wszystkich elementów.

gadatliwy
źródło
„Aby zainicjować tablicę, musisz to zrobić w momencie definiowania…” To i następna linia sprawia, że ​​jest to lepsze niż akceptowana odpowiedź.
Laurie Stearn
26
  1. Te są równoważne

    char buf[10] = "";
    char buf[10] = {0};
    char buf[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
  2. Te są równoważne

    char buf[10] = " ";
    char buf[10] = {' '};
    char buf[10] = {' ', 0, 0, 0, 0, 0, 0, 0, 0, 0};
  3. Te są równoważne

    char buf[10] = "a";
    char buf[10] = {'a'};
    char buf[10] = {'a', 0, 0, 0, 0, 0, 0, 0, 0, 0};
Steven Penny
źródło
8

Odpowiednia część projektu standardu C11 n1570 6.7.9 inicjalizacji mówi:

14 Tablica typu znakowego może być zainicjowana przez literał ciągu znaków lub literał ciągu znaków UTF-8, opcjonalnie ujęty w nawiasy. Kolejne bajty literału ciągu (w tym kończący znak null, jeśli jest miejsce lub jeśli tablica ma nieznany rozmiar) inicjują elementy tablicy.

i

21 Jeśli na liście w nawiasach klamrowych jest mniej inicjatorów niż elementów lub członków agregatu lub mniej znaków w literale ciągu używanym do zainicjowania tablicy o znanym rozmiarze niż elementów w tablicy, pozostała część agregacji powinny być inicjowane niejawnie tak samo jak obiekty, które mają statyczny czas trwania.

W ten sposób, jeśli jest wystarczająca ilość miejsca , dopisywany jest znak „\ 0” , a pozostałe znaki są inicjowane wartością, static char c;która zostanie zainicjowana w funkcji.

Wreszcie,

10 Jeśli obiekt, który ma automatyczny czas przechowywania, nie jest jawnie zainicjowany, jego wartość jest nieokreślona. Jeśli obiekt, który ma statyczny lub wątkowy czas trwania nie jest inicjowany jawnie, to:

[-]

  • jeśli ma typ arytmetyczny, jest inicjalizowany na zero (dodatnie lub bez znaku);

[-]

Tak więc, charbędąc typem arytmetycznym, reszta tablicy jest również zainicjowana zerami.

Antti Haapala
źródło
3

Co ciekawe, tablice można inicjalizować w dowolny sposób w dowolnym momencie w programie, pod warunkiem, że są członkami a structlub union.

Przykładowy program:

#include <stdio.h>

struct ccont
{
  char array[32];
};

struct icont
{
  int array[32];
};

int main()
{
  int  cnt;
  char carray[32] = { 'A', 66, 6*11+1 };    // 'A', 'B', 'C', '\0', '\0', ...
  int  iarray[32] = { 67, 42, 25 };

  struct ccont cc = { 0 };
  struct icont ic = { 0 };

  /*  these don't work
  carray = { [0]=1 };           // expected expression before '{' token
  carray = { [0 ... 31]=1 };    // (likewise)
  carray = (char[32]){ [0]=3 }; // incompatible types when assigning to type 'char[32]' from type 'char *'
  iarray = (int[32]){ 1 };      // (likewise, but s/char/int/g)
  */

  // but these perfectly work...
  cc = (struct ccont){ .array='a' };        // 'a', '\0', '\0', '\0', ...
  // the following is a gcc extension, 
  cc = (struct ccont){ .array={ [0 ... 2]='a' } };  // 'a', 'a', 'a', '\0', '\0', ...
  ic = (struct icont){ .array={ 42,67 } };      // 42, 67, 0, 0, 0, ...
  // index ranges can overlap, the latter override the former
  // (no compiler warning with -Wall -Wextra)
  ic = (struct icont){ .array={ [0 ... 1]=42, [1 ... 2]=67 } }; // 42, 67, 67, 0, 0, ...

  for (cnt=0; cnt<5; cnt++)
    printf("%2d %c %2d %c\n",iarray[cnt], carray[cnt],ic.array[cnt],cc.array[cnt]);

  return 0;
}
user3381726
źródło
1

Nie jestem pewien, ale zwykle inicjalizuję tablicę jako "" w takim przypadku nie muszę martwić się o zerowy koniec łańcucha.

main() {
    void something(char[]);
    char s[100] = "";

    something(s);
    printf("%s", s);
}

void something(char s[]) {
    // ... do something, pass the output to s
    // no need to add s[i] = '\0'; because all unused slot is already set to '\0'
}
Erric Rapsing
źródło
Nie powinieneś naprawdę używać niejawnej reguły int . Powinieneś określić typ dla main()(i powinieneś również użyć void, np int main(void) { ... }. C99 pozbył się tej reguły, więc ten kod nie będzie się kompilował dla C99 i późniejszych. Inną rzeczą wartą uwagi jest to, że zaczynając od C99, jeśli pominiesz returnw main, istnieje automatyczne return 0;umieszczenie / domniemanie przed }końcem main. Używasz niejawnej reguły int, która działa tylko przed C99, ale używasz niejawnej, returnktóra działa tylko z C99 i później ; te dwie są oczywiście sprzeczne .
RastaJedi