Zajímavé úlohy pro programátory v C a C++ #2

Od Petr Zemek, 2009-01-03

Tak jsem opět zde a s druhou úlohou. Tentokrát si dáme něco "napříč spektrem norem" a budeme programovat jak v C, tak v C++. Myslím si, že vás to zaujme :).

Zadání

Napište program, který po zkompilování vypíše, podle jaké normy byl zkompilován. Berte v potaz normy ANSI-C (c89), ISO-C (c99) a ISO-C++ (c++98).

Vzorový výstup (GCC):

$ gcc -std=c89 -pedantic -o prog prog.c
$ ./prog
c89
$ gcc -std=c99 -pedantic -o prog prog.c
$ ./prog
c99
$ g++ -std=c++98 -pedantic -o prog prog.c
$ ./prog
c++98

Existuje více správných řešení, takže pokud už někdo s řešením přišel před vámi, můžete zkusit vymyslet řešení nové.

Omezení

Celý program musí být v 1 souboru, musí jej být možné přeložit podle všech tří zmíněných norem a spustit přesně jako ve vzorovém výstupu (lze ovšem využít i jiný překladač). Dále by program měl být implementačně nezávislý (tj nepoužívejte konstrukty s nespecifikovaným či nedefinovaným chováním). Nesmíte použít žádná předdefinovaná makra překladačem (jako __STDC__ apod.) ani direktivy pro podmíněný překlad (kromě direktivy #include a #define pro vytvoření svých vlastních symbolických konstant), neboli nic, co by splnění tohoto úkolu příliš usnadnilo. Rád zodpovím případné dotazy, co se těchto omezení týče.

"Prémiové body"

Pro zisk (bezvýznamných :]) prémiových bodů upravte stávající program tak, aby "rozeznal", že byl přeložen i podle (draftu) normy C++0x (pro odzkoušení lze využít např. překladač GCC 4.4, který část tohoto draftu implementuje). Mě se nepodařilo přijít na to, jak toho docílit, takže v případě, že se to někomu povede, tak se alespoň přiučím :).

Řešení

Jedno z možných řešení:

#include <stdio.h>
 
int x[100];
 
int main() {
   int a;
   struct x {int i;};
 
   a = 2 //* */
      +2;
   switch (a) {
      case 4:
         if (sizeof(x) == sizeof(struct x))
            printf("c++98\n");
         else
            printf("c99\n");
         break;
      case 1:
         printf("c89\n");
      break;
   }
 
   return 0;
}

Teď se podíváme, jak toto řešení funguje. Rozdíl mezi C99 a C++98 je řešen pomocí kódu:

int x[100];
int main() {
    struct x {int i;};
    // ...
    if (sizeof(x) == sizeof(struct x))
        printf("c++98\n");
    else
        printf("c99\n");
    // ...
}

V C99 (i ANSI-C) jsou totiž identifikátory struktur umisťovány do odlišného "prostoru jmen" (nejedná se ovšem o namespaces, jak je znáte z C++) oproti ostatním datovým typům, takže při práci s nimi je třeba kvalifikovat prostor jmen pomocí klíčového slova struct. Takže pokud vytvoříme globální proměnnou x typu pole čísel typu int a dále lokální proměnnou typu struct x, tak v případě C99 (a ANSI-C) nedojde k zastínění identifikátoru x globální proměnné (v případě C++98 ovšem ano), tudíž sizeof(x) == sizeof(struct x) vrátí jiný výsledek v případě C99/ANSI-C a C++98.

Rozdíl mezi ANSI-C a C99 je řešen pomocí kódu:

int main() {
   int a;
 
   a = 2 //* */
      +2;
   switch (a) {
      case 4:
         // C99 nebo C++98
         break;
      case 1:
          printf("c89\n");
      break;
   }
 
   return 0;
}

Zde se využívá toho, že v ANCI-C neexistují řádkové komentáře //, takže při překladu podle tohoto standardu bude výsledkem a = 2 / +2;, což je 1. V případě C99 a C++98 bude (díky tomu, že se lexikálního analyzátor vždy snaží zpracovat co nejdelší sekvenci znaků, která ještě tvoří lexém) výpočet jiný, čili a = 2 +2;, což je 4.

Nepřenositelná řešení

Někoho by možná mohlo napadnout využít (pro odlišení C99 a C++98) tyto odlišnosti:

sizeof(’c’) == sizeof(int);  // C99
sizeof(’c’) == sizeof(char); // C++98

nebo

enum e { A };
sizeof(A) == sizeof(int); // C99
sizeof(A) == sizeof(e);   // v C++98 se sizeof(A) může lišit od sizeof(int)

Problém je zde s nepřenositelností výše zmíněných konstrukcí. C99 ani C++98 totiž nepředepisuje přesnou velikost jednotlivých datových typů (pouze předepisuje několik pravidel, např. že char má alespoň 8 bitů a int má alespoň 16 bitů a platí sizeof(int) >= sizeof(char)) a tudíž (teoreticky) může nastat situace, že v dané implementaci bude platit sizeof(char) == sizeof(int) a první "řešení" nebude fungovat. Obdobným neduhem trpí druhé "řešení".

Obsah tohoto pole je soukromý a nebude veřejně zobrazen.

Filtrované HTML (využíváno)

  • Povolené HTML značky: <a href hreflang> <em> <strong> <cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <table>
  • Zvýraznění syntaxe kódu lze povolit přes následující značky: <code>, <blockcode>, <bash>, <c>, <cpp>, <haskell>, <html>, <java>, <javascript>, <latex>, <perl>, <php>, <python>, <ruby>, <rust>, <sql>, <text>, <vim>, <xml>, <yaml>.
  • Řádky a odstavce se zalomí automaticky.
  • Webové a e-mailové adresy jsou automaticky převedeny na odkazy.
CAPTCHA
1 + 3 =
Vyřešte tento jednoduchý matematický příklad a vložte výsledek. Např. pro 1+3 vložte 4.
Nějak se mi tady rozmohl spam, takže poprosím o ověření.

Libor (neověřeno)

15 years 10 months zpět

#include <stdio.h>
 
int x = 1;
 
int main()
{
	struct x {int y[42];};
	char* version = (char*) "c99"; 42 //*  */ 89; version = "c89"
	;
	printf("%s\n", (sizeof(x) == sizeof(int)) ? version : "c++98");
	return 0;
}
gcc -std=c89 -pedantic -o rozdily rozdily.c && ./rozdily
c89
gcc -std=c99 -pedantic -o rozdily rozdily.c && ./rozdily
c99
g++ -std=c++98 -pedantic -o rozdily rozdily.c && ./rozdily
c++98