ckd_add, ckd_sub, ckd_mul în C

Adunare, scădere și înmulțire cu verificarea depășirii: funcțiile din stdckdint.h spun dacă rezultatul a încăput în tip.

Sintaxă
#include <stdckdint.h>

bool ckd_add(type1 *rezultat, type2 a, type3 b);
bool ckd_sub(type1 *rezultat, type2 a, type3 b);
bool ckd_mul(type1 *rezultat, type2 a, type3 b);

Ce face ckd_add, ckd_sub, ckd_mul

Dacă rezultatul unei operații cu int nu încape în tip (de exemplu INT_MAX + 1), C nu dă eroare: este comportament nedefinit, iar programul poate face orice. Verificarea manuală înainte de operație este ușor de greșit.

Antetul C23 <stdckdint.h> are trei macro: ckd_add, ckd_sub și ckd_mul. Primul argument este adresa unde se pune rezultatul. Valoarea returnată este false dacă rezultatul matematic a încăput în tipul destinației și true dacă a fost depășire. La depășire, în destinație ajunge rezultatul „trunchiat” (ca la tipurile fără semn), dar nu se produce comportament nedefinit.

Tipurile argumentelor pot fi diferite; contează tipul destinației. Funcțiile merg cu orice tip întreg, în afară de bool, char simplu și enum.

Parametri

Parametru Descriere
rezultat Pointer la variabila în care se scrie rezultatul (tipul ei decide dacă rezultatul „încape”).
a, b Operanzii, de orice tip întreg.

Ce returnează

false dacă rezultatul exact a încăput în *rezultat, true dacă a fost depășire.

Exemple

Exemplul 1: Adunare care depășește INT_MAX

Prima adunare încape, a doua nu. Programul află asta din valoarea returnată, fără comportament nedefinit.

#include <limits.h>
#include <stdckdint.h>
#include <stdio.h>

int main(void) {
    int total;
    if (!ckd_add(&total, 2000000000, 100000000)) {
        printf("Total: %d\n", total);
    }

    if (ckd_add(&total, INT_MAX, 1)) {
        printf("Depășire: INT_MAX + 1 nu încape într-un int\n");
    }
    return 0;
}

Rezultat

Total: 2100000000
Depășire: INT_MAX + 1 nu încape într-un int

Exemplul 2: Calculul sigur al memoriei pentru malloc

Înainte de malloc(n * dimensiune), produsul trebuie verificat: dacă depășește, s-ar cere mult mai puțină memorie decât crezi. Aici destinația este size_t.

#include <stdckdint.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

static void *aloca_tablou(size_t n, size_t dimensiune) {
    size_t total;
    if (ckd_mul(&total, n, dimensiune)) {
        printf("Cerere prea mare: %zu x %zu octeți\n", n, dimensiune);
        return nullptr;
    }
    printf("Aloc %zu octeți\n", total);
    return malloc(total);
}

int main(void) {
    double *preturi = aloca_tablou(1000, sizeof(double));
    void *urias = aloca_tablou(SIZE_MAX / 2, 4);

    free(preturi);
    free(urias);
    return 0;
}

Rezultat

Aloc 8000 octeți
Cerere prea mare: 9223372036854775807 x 4 octeți

Exemplul 3: Destinație de alt tip decât operanzii

Operanzii sunt int, dar destinația este unsigned char (0–255). 200 + 50 încape, 200 + 100 nu; la depășire, în destinație ajunge 300 modulo 256 = 44. La scădere, -5 nu încape într-un tip fără semn.

#include <stdckdint.h>
#include <stdio.h>

int main(void) {
    unsigned char luminozitate;
    bool depasire;

    depasire = ckd_add(&luminozitate, 200, 50);
    printf("200 + 50  -> %3d, depășire: %s\n", luminozitate, depasire ? "da" : "nu");

    depasire = ckd_add(&luminozitate, 200, 100);
    printf("200 + 100 -> %3d, depășire: %s\n", luminozitate, depasire ? "da" : "nu");

    unsigned int stoc;
    depasire = ckd_sub(&stoc, 10, 15);
    printf("10 - 15 într-un unsigned: depășire: %s\n", depasire ? "da" : "nu");
    return 0;
}

Rezultat

200 + 50  -> 250, depășire: nu
200 + 100 ->  44, depășire: da
10 - 15 într-un unsigned: depășire: da

Greșeli frecvente

  • Ignori valoarea returnată și folosești direct rezultatul. Rolul funcțiilor este tocmai verificarea; la depășire, rezultatul din destinație nu este cel matematic.
  • Dai ca prim argument variabila, nu adresa ei: ckd_add(total, a, b). Este nevoie de &total.
  • Folosești <stdckdint.h> cu un compilator vechi. Antetul există în GCC 14+ și Clang 18+; altfel, GCC și Clang au funcțiile echivalente __builtin_add_overflow și înrudite.

Comenzi înrudite

  • stdint.h și limits.h: Tipuri întregi cu dimensiune exactă (int32_t, uint8_t) și constantele cu valorile minime și maxime ale tipurilor (INT_MAX).
  • Operatori aritmetici (+ - * / % ++ --): Adunare, scădere, înmulțire, împărțire, restul împărțirii, atribuiri compuse și incrementare cu ++ / –.
  • Comportament nedefinit (undefined behavior): Situațiile pentru care standardul C nu spune ce se întâmplă: programul poate merge, poate da rezultate greșite sau se poate opri.
  • malloc: Cere sistemului un bloc de memorie de mărimea dată, în timpul rulării, și returnează adresa lui sau NULL dacă nu reușește.
  • bool, true, false: Tipul pentru valori adevărat/fals. În C23, bool, true și false sunt cuvinte cheie, fără să mai incluzi stdbool.h.
Comunitate & Schimb de cunoștințe

Întrebări & Exemple de Cod: ckd_add, ckd_sub, ckd_mul

Ai o nelămurire despre utilizarea comenzii ckd_add, ckd_sub, ckd_mul sau ai descoperit un truc / fragment de cod util? Distribuie-l cu ceilalți programatori!

💬

Fii primul care pune o întrebare sau distribuie un fragment de cod / prompt!

Nu au fost adăugate încă întrebări sau exemple de cod pentru această pagină. Dacă ai întâlnit o problemă sau ai un snippet util, adaugă-l mai jos pentru a-i ajuta pe ceilalți.

Comunitate Adaugă o întrebare sau distribuie un cod

Moderare & Siguranță:

Pentru a menține calitatea și securitatea informațiilor, fiecare întrebare sau fragment de cod/prompt este analizat (manual sau prin moderare asistată de AI) înainte de publicare.