Dnes a zítra probíhá vývojářská konference Google I/O 2025. Sledovat lze na YouTube a na síti 𝕏 (#GoogleIO).
V Bostonu probíhá konference Red Hat Summit 2025. Vybrané přednášky lze sledovat na YouTube. Dění lze sledovat na síti 𝕏 (#RHSummit).
Společnost Red Hat oficiálně oznámila vydání Red Hat Enterprise Linuxu 10. Vedle nových vlastností přináší také aktualizaci ovladačů a předběžné ukázky budoucích technologií. Podrobnosti v poznámkách k vydání.
Tuto sobotu 24. května se koná historicky první komunitní den projektu Home Assistant. Zváni jsou všichni příznivci, nadšenci a uživatelé tohoto projektu. Pro účast je potřebná registrace. Odkazy na akce v Praze a v Bratislavě.
Troy Hunt představil Have I Been Pwned 2.0, tj. nový vylepšený web služby, kde si uživatelé mohou zkontrolovat, zda se jejich hesla a osobní údaje neobjevily v únicích dat a případně se nechat na další úniky upozorňovat.
Microsoft představil open source textový editor Edit bežící v terminálu. Zdrojové kódy jsou k dispozici na GitHubu pod licencí MIT.
V Seattlu a také online probíhá konference Microsoft Build 2025. Microsoft představuje své novinky. Windows Subsystem for Linux je nově open source. Zdrojové kódy jsou k dispozici na GitHubu pod licencí MIT.
Z příspěvku Turris Sentinel – co přinesl rok 2024 na blogu CZ.NIC: "Za poslední rok (únor 2024 – únor 2025) jsme zachytili 8,3 miliardy incidentů a to z 232 zemí a z jejich závislých území. Tyto útoky přišly od 6,2 milionu útočníků (respektive unikátních adres). SMTP minipot je stále nejlákavější pastí, zhruba 79 % útoků bylo směřováno na tento minipot, 16 % útoků směřovalo na minipot Telnet, 3 % útoků směřovaly na minipot HTTP a 2 % na minipot FTP. Dále jsme zaznamenali 3,2 milionu unikátních hesel a 318 tisíc unikátních loginů, které útočníci zkoušeli."
Byla vydána (Mastodon, 𝕏) nová verze 3.0.4 svobodné aplikace pro úpravu a vytváření rastrové grafiky GIMP (GNU Image Manipulation Program). Přehled novinek v oznámení o vydání a v souboru NEWS na GitLabu. Nový GIMP je již k dispozici také na Flathubu.
Byla vydána nová stabilní verze 7.4 webového prohlížeče Vivaldi (Wikipedie). Postavena je na Chromiu 136. Přehled novinek i s náhledy v příspěvku na blogu.
int x = 1; int *ip; /* deklarace ukazatele */ ip = &x; /* ip je 0x7ffd87ef82b4 */ *ip = 0; /* x je 0 */Zvýšení hodnoty, na kterou ip ukazuje:
(*ip)++; /* x je 1 */ ++*ip; /* x je 2 */Ukazatel ukazuje na specifický datový typ, s výjimkou ukazatele na void, ten může ukazovat na jakýkoliv typ, ale nesmí být dereferencován. Rovněž nesmí být dereferencován ukazatel, který má hodnotu NULL.
int *ip = &x;
int a[10]; int *pa; pa = &a[0];Nyní přiřazení
x = *pa;zkopíruje obsah a[0] do x.
pa = &a[0];a
pa = a;jsou ekvivalentní.
int *pa = a;
int a = 3; int b = 5; swap(&a, &b); void swap(int *px, int *py) { int temp; temp = *px; *px = *py; *py = temp; }Pokud funkci předáváme pole, předáváme adresu prvního prvku.
int strlen(char *s) { int n; for (n = 0; *s != '\0'; s++) n++; return n; }Všechna tato volání fungují:
strlen("hello world"); /* retezcova konstanta */ strlen(array); /* char array[100]; */ strlen(ptr); /* char *ptr; */Parametr char *s ve funkci strlen můžeme napsat i jako char s[], nicméně dáváme přednost prvnímu způsobu, jelikož přesněji vyjadřuje, že je parametrem ukazatel.
f(&a[2]);nebo
f(a+2);předají funkci f adresu podpole, které začíná na a[2].
printf("hello, world\n");Ale lze je definovat i jako běžné proměnné:
char *greeting; greeting = "hello, world";nebo přímo
char *greeting = "hello, world";Na rozdíl od pole se znaky v řetězcových konstantách nesmějí měnit, změna znaku způsobí nedefinované chování. Nicméně je možné změnit adresu ukazatele, aby ukazoval někam jinam.
char *colors[] = { "red", "green", "blue", NULL };
char *estrdup(char *s) { char *t; if ((t = (char *) malloc(strlen(s)+1)) == NULL) { printf("estrdup(\"%.20s\") failed!\n", s); return NULL; } strcpy(t, s); return t; }Funkce malloc alokuje dostatek místa, včetně nulového znaku, pro řetězec (pole znaků) s a funkce strcpy tento řetězec na toto místo zkopíruje.
#include <stdio.h> #include <stdlib.h> #include <string.h> #define MAXLINES 5000 char *lines[MAXLINES]; int readlines(char *lines[], int maxlines); void printlines(char *lines[], int n); int main() { int nlines; if ((nlines = readlines(lines, MAXLINES)) < 0) { printf("readlines() failed!\n"); return 1; } printlines(lines, nlines); return 0; } #define MAXBUF 1000 char *estrdup(char *); int readlines(char *lines[], int maxlines) { char buf[MAXBUF], *s; int nlines = 0; while (fgets(buf, MAXBUF, stdin) != NULL) { if (nlines >= maxlines || (s = estrdup(buf)) == NULL) return -1; if (s[strlen(s)-1] == '\n') s[strlen(s)-1] = '\0'; lines[nlines++] = s; } return nlines; } void printlines(char *lines[], int n) { while (n-- > 0) printf("%s\n", *lines++); }Definice char *lines[MAXLINES] představuje pole ukazatelů a funkce estrdup ukazatelům během načítání jednotlivých řádků přiděluje paměť.
int readlines(char *lines[], int maxlines)je možná napsat i jako:
int readlines(char **lines, int maxlines)Vzhledem k tomu, že název pole je adresa prvního prvku a v tomto případě je prvním prvkem ukazatel, předáváme adresu ukazatele, což odpovídá parametru char **lines (ukazatel na ukazatel typu char).
long strtol(const char *s, char **endp, int base)Funkce get_long načítá řetezec ze standardního vstupu a poté jej převede na číslo typu long. Pokud řetězec obsahuje nečíselné znaky, funkce vrátí chybu.
int get_long(long *x) { enum { MAX = 20 }; char s[MAX], *endp; if (fgets(s, MAX, stdin) == NULL) { printf("get_long() exit 1\n"); exit(1); } if (s[strlen(s)-1] == '\n') s[strlen(s)-1] = '\0'; errno = 0; *x = strtol(s, &endp, 10); if (errno != 0 || *endp != '\0' || *s == '\0') { printf("bad input!\n"); return 0; } return 1; }Možné použití funkce get_long:
long x; while (!get_long(&x)) ;Pole ukazatelů lze vytvořit i dynamicky. Funkce alloc2d alokuje dvourozměrné pole typu long.
long **alloc2d(size_t r, size_t c) { long i, **p; p = (long **) malloc(r * sizeof(long *)); for (i = 0; i < r; i++) p[i] = (long *) malloc(c * sizeof(long)); return p; }Parametry r a c představují počty řádků (rows) a sloupců (columns).
char str[10]; char *(*fn)(char *, const char *) = strcpy; (*fn)(str, "Hello");Funkce vracející ukazatel na funkci:
char *(*get_strcpy(void))(char *, const char *) { return strcpy; }Pole ukazatelů na funkce:
void (*funcs[])(void) = { func1, func2, func3, NULL }; process(funcs); void process(void (*funcs[])(void)) { while (*funcs != NULL) (*(*funcs++))(); }Následující příklad ukazuje interface pro matici. Funkce apply má jako parametr ukazatel na funkci, takže vlastně vkládáme další kód do funkce. Ve funkci apply jsou dvě smyčky for, které používáme při práci s maticí, a při inicializaci nebo výpisu matice tyto smyčky v ostatních funkcích již nemusíme opakovat.
main.c #include "matrix.h" int main() { MT *m; m = MT_new(5, 5); MT_print(m, "%ld\t"); MT_set_all(m, 7); MT_print(m, "%ld\t"); return 0; } matrix.c #include <stdio.h> #include <stdlib.h> #include "matrix.h" static long **alloc2d(size_t r, size_t c); static MT *alloc(void); static void apply(MT *m, void (*fn)(MT *, int, int, void *), void *arg); static void set_all(MT *m, int i, int j, void *arg); static void print(MT *m, int i, int j, void *arg); struct MT { int rows; int cols; long **data; }; MT *MT_new(int r, int c) { MT *m; m = alloc(); m->rows = r; m->cols = c; m->data = alloc2d(r, c); MT_set_all(m, 0); return m; } static MT *alloc(void) { return (MT *) malloc(sizeof(MT)); } static void apply(MT *m, void (*fn)(MT *, int, int, void *), void *arg) { int i, j; for (i = 0; i < m->rows; i++) for (j = 0; j < m->cols; j++) (*fn)(m, i, j, arg); } static void set_all(MT *m, int i, int j, void *arg) { m->data[i][j] = *(int *) arg; } static void print(MT *m, int i, int j, void *arg) { printf((char *) arg, m->data[i][j]); } void MT_set_all(MT *m, int n) { apply(m, set_all, &n); } void MT_print(MT *m, void *arg) { apply(m, print, arg); } matrix.h #ifndef MT_H #define MT_H typedef struct MT MT; MT *MT_new(int r, int c); void MT_set_all(MT *m, int n); void MT_print(MT *m, void *arg); #endifUživatel neinkluduje celý typ struktury, ale pouze její typedef, což je neúplný typ, takže uživatel nemá přístup k položkám struktury, jsou tedy privátní. Ukazatel na void nesmí být dereferencován, proto je nutné jej přetypovat na správný typ a až poté tento typ dereferencovat.
(*p).xnebo pohodlnější
p->xFunkce na uvolnění paměti by mohla mít parametr ukazatel na ukazatel, takže by nastavila ukazatel pro interface na NULL.
void MT_free(MT **m) { int i; for (i = 0; i < (*m)->rows; i++) free((*m)->data[i]); free((*m)->data); free(*m); *m = NULL; } int main() ... MT_free(&m); /* m je NULL */Mimochodem
(*m)->rowsmůžeme napsat i jako
(**m).rowsA syntaxe pro přístup k hodnotám u 2D polí přes ukazatele je
*(*(a+i)+j)
Tiskni
Sdílej:
V případě ukazatelů na struktury existují dvě možnosti, jak přistupovat k položkám struktur:Jsem jediný, komu chybí(*p).xnebo pohodlnějšíp->x
p-->x
(ekvivalent zmíněného (*m)->rows
→ m-->rows
)? Šlo by na to napsat makro? m-->rows
se parsuje jako (m--)->rows
a asi by mi i víc smyslu dávalo m->>rows
. Pocit chybějícího operátoru mám spíš u konstrukcí typu (*p)[idx]
protožem-->rows
se parsuje jako(m--)->rows
Oprava: mělo tam být "…jako (m--) > rows
".
děte si mršit c++ jóóó??? :O >:C
jestli to teda eště jako nějak víc de :D :D ;D ;D
Ukazatel je špecialná premenná, ktorá pracuje s adresami.
const
(hlavně ten nekonstantní pointer ukazující do string literal) nebo zbytečné přetypovávání pointerů vracených malloc()
.
Kromě chybějících const všude možně (například strlen v základní knihovně a všechny ostatní mají u vstupních proměnných všude consco se prakticnosti tyce bych mel jednu pripominku: kvuli tem miliardam dotazum na internetu, jak to s tim const hlavne u tech pointru na char vlastne je, jsem toho nazoru, ze my, kteri jsme s K&R zacinali se bez tech const dost dobre muzeme obejit. To ze me ten const u strlen-funkce ubezpecuje, ze ta funkce ten string pri zjistovani te delky nezmeni mi nijak zvlast nevzrusuje a ani neuklidnuje, nas starsi by v zivote nenapadlo, ze by nekdo mohl napsat funkci, ktera by to delela.
co si funkce dělá s parametrem vevnitř (kopií proměnné předávanou volajícím), mi může být ukradenéNóóó... úplně jedno to taky není, z tohohle důvodu byl přidán
restrict
, kterým se vývojář zaklíná, že nebude vytvářet aliasy (kopie pointerů apod.), což umožňuje optimalizace přístupu do paměti / lepší cachování...
(Je to tak trochu směrem, kterým šel Rust, kde jsou pravidla aliasingu ještě o dost striktnější a jejich dodržování tvrdě vymáháno kompilátorem, v C jde jen o optimalizační hint a kompilátor maximálně háže warningy. Paradoxně Rust aktuálně ten optimalizační potenciál nevyuživá, protože v LLVM to je rozbité printk()
v jedné funkci modifikoval data, která ta funkce vůbec neměla co měnit - a která za určitých okolností byla opravdu read only. Kdyby tam byl const
, tak by ho chyba při překladu hned trkla, že dělá něco špatného, a řešil by to jinak (nebo by tam ten ladící příkaz vůbec nedal).
Výrazy s poli a indexy jsou ekvivalentní výrazům s ukazateli a posunem, tudíž je možné napsat pole a[i] jako *(a+i)Můžeš to napsat i jako
i[a]
... kolegové to jistě ocení Nicméně výrazy jakoNo, proměnná to je, ale to přiřazení neprojde, protože, AFAIKa=pa
neboa++
nejsou správné, jelikož jméno pole není proměnná.
IMHO nejnázornější příklad ukazující, že pole a pointer opravdu není totéž (i když to spolu úzce souvisí), je
T *ptr; T array[10]; printf("%zu, %zu\n", sizeof(ptr), sizeof(array));
void boo(char *bar) {…}
Pointer a polia su to iste.Ne, nejsou, jsou to různé datové typy.
Rozdíl tam bude, protože v prvním případě "Nazdar" bude globální konstanta, která v závislosti na kompilátoru může být i v read only sekci. Navíc překladač může dělat i taková kouzla, že např.
char *str1 = "Nazdar"; char *str2 = "zdar";
použije jen jeden řetězec "Nazdar" a druhý pointer nechá ukazovat do něj. To je možné právě díky tomu, že string literal je automaticky only. Proto je v takovém případě lepší použít const
, aby vás překladač varoval, pokud byste ho zkusil přepisovat.
Oproti tomu to druhé je inicializace lokálního pole znaků, které je defaultně přepisovatelné. V praxi ale samozřejmě závisí na zbytku kódu, co s tím řetězcem dělá, protože po optimalizaci tam ve skutečnosti žádný string "Nazdar" nikde být ani nemusí.
<flame-war>
Chápu, že článek se věnuje C a ne C++, ale to jsem vážně jediný, komu vadí psát tu hvězdičku k názvu proměnné a kdo by ji psal raději k typu? </flame-war>
Tohle je bohužel věc, která je navržená dost nešťastně. Na jednu stranu je pravda, že psát hvězdičku k typu je logičtější, hlavně když je tam inicializace:
T *p = &x; // kam že to tu adresu přiřazujeme? T* p = &x; // tady je to jasné, do p
Jenže problém nastává u vícenásobných deklarací:
T* p, x; // tohle vypadá, jako by p a x měly stejný typ T *p, x; // ale mají ho *p a x T* p, *q; // a kam napsat tu druhou hvězdičku tady?
Osobně se ale těm násobným deklaracím s pointery snažím vyhýbat.