Agregue dos números de manera segura, en C

24

Todos saben que C es un lenguaje de programación encantador, seguro y de alto nivel. Sin embargo, usted, como codificador, establece la siguiente tarea.

Escribe un programa para sumar dos números.

  • Entrada: dos enteros separados por espacios.
  • Salida: la suma de los dos números en la entrada.

El giro es que su código debe ser 100% seguro. En otras palabras, debe comportarse correctamente sin importar la entrada. Si la entrada es de hecho dos enteros separados por espacios, los cuales tienen menos de 100 dígitos, debe generar la suma. De lo contrario, debe generar un mensaje de error y salir con seguridad.

¿Qué tan difícil puede ser después de todo?

Se darán felicitaciones generales a los casos de aportes patológicos que rompan las respuestas de otras personas :)

El código debe compilarse sin advertencias utilizando gcc -Wall -Wextra en ubuntu.


Aclaración.

  • La entrada es de stdin.
  • El espacio en blanco horizontal es solo un carácter de espacio único. No debe haber nada antes del primer número y la entrada debe terminarse con newline + EOF o simplemente EOF.
  • La única entrada válida, especificada en el formulario Backus-Naur aumentado , es:
    NONZERODIGIT = "1" / "2" / "3" / "4" / ​​"5" / "6" / "7" / "8" / "9"
    POSITIVENUMBER = NONZERODIGIT * 98DIGIT
    NEGATIVENUMBER = "-" POSITIVENUMBER
    NÚMERO = NEGATIVENUMBER / POSITIVENUMBER / "0"
    VALIDINPUT = NUMBER SP NUMBER * 1LF EOF
  • El mensaje de error es la letra única 'E', seguida de una nueva línea.
  • El código debe terminar limpiamente en menos de 0.5s sin importar cuál sea la entrada.

fuente
2
Me encanta lo clara que se volvió tu especificación con un solo uso de la palabra "de lo contrario". Muy bien escrito (en mi opinión). Lástima que el desafío esté solo en C, pero puedo ver por qué tiene que ser así.
Rainbolt
3
Sin advertencias + código-golf + C? ¡Esto seguramente será interesante!
Fors
1
stdin o línea de comando?
aprensivo ossifrage
2
¿Podríamos obtener un formato de entrada estrictamente aceptable? Por ejemplo, sin espacios en blanco adicionales (o cualquier cosa que no sea un número) antes del primer número, cualquier cantidad de espacios en blanco horizontales (es decir, tabulaciones y espacios horizontales) entre los dos números, y nada excepto un salto de línea o un retorno de carro junto con un salto de línea después del segundo ¿número?
Fors
3
Además, el espacio separado significa 1) separado por un carácter con valor ASCII 32, 2) separado por un número arbitrario de caracteres con valor ASCII 32, 3) separado por cualquier carácter de espacio en blanco ASCII (32, 10, 9, posiblemente 13 ?), 4) separados por un número arbitrario de un solo carácter de espacio en blanco del conjunto de caracteres de espacio en blanco ASCII, o 5) separados por un número arbitrario de cualquier combinación del conjunto de caracteres de espacio en blanco ASCII?
usuario12205

Respuestas:

4

6610 bytes (sin minificar)

Programa "Good boy" C que cumple con todos los criterios de desafío. Utiliza el complemento de 10 para números negativos. También se incluye un arnés de prueba y casos de prueba.

/*
Read input from STDIN. The input must conform to VALIDINPUT:

    NONZERODIGIT = "1" / "2" / "3" / "4" / "5" / "6" / "7" / "8" / "9"
    POSITIVENUMBER = NONZERODIGIT *98DIGIT
    NEGATIVENUMBER = "-" POSITIVENUMBER
    NUMBER = NEGATIVENUMBER / POSITIVENUMBER / "0"
    VALIDINPUT = NUMBER SP NUMBER *1LF EOF

Check syntax of input. If input is correct, add the two numbers and print
to STDOUT.

LSB => least significant byte
MSB => most significant byte
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdbool.h>
#include <assert.h>
#define NUL ('\0')
/*
    maximum characters in VALIDINPUT:
        '-'     1
        POSITIVENUMBER  MAXDIGITS
        ' '     1
        '-'     1
        POSITIVENUMBER  MAXDIGITS
        LF      1
*/
#define MAXDIGITS (99)
#define MAXVALIDINPUT (2*MAXDIGITS+4)

void die() { printf("E\n"); exit(1); }

/*
    Add two NUMBERs and print the result to STDOUT.  The NUMBERS have
been separated into POSITIVENUMBERS and sign information.

Arguments:
    first       - pointer to LSB of 1st POSITIVENUMBER
    firstSize   - size of 1st POSITIVENUMBER
    firstNegative   - is 1st # negative?
    second      - pointer to LSB of 2nd POSITIVENUMBER
    secondSize  - size of 2nd POSITIVENUMBER
    secondNegative  - is 2nd # negative?
    carry       - carry from previous place?

Returns:
    sum[]       - sum
    addNUMBERs()    - carry to next place?

- Don't use complementDigit(popDigit(p,s),n). Side-effects generate two pops.
*/
#define popDigit(p,s) ((s)--,(*(p++)-'0'))
#define complementDigit(c,n) ((n) ? 9-(c) : (c))

#define pushSum(c) (*(--sumPointer)=(c))
#define openSum() (pushSum(NUL))
#define closeSum() ;
char    sum[MAXVALIDINPUT];
char    *sumPointer = sum+sizeof(sum);
int addNUMBERs(char *first, int firstSize, bool firstNegative,
        char *second, int secondSize, bool secondNegative,
        int previousCarry) {
    int firstDigit, secondDigit;
    int mySum;
    int myCarry;

    /*
        1st half of the problem.

        Build a stack of digits for "first" and "second"
    numbers. Each recursion of addNUMBERs() contains one digit
    of each number for that place. I.e., the 1st call holds
    the MSBs, the last call holds the LSBs.

        If negative, convert to 10s complement.
    */
    assert((firstSize > 0) && (secondSize > 0));
    if (firstSize > secondSize) {
        firstDigit = popDigit(first, firstSize);
        firstDigit = complementDigit(firstDigit, firstNegative);
        secondDigit = 0;
    } else if (secondSize > firstSize) {
        firstDigit = 0;
        secondDigit = popDigit(second, secondSize);
        secondDigit = complementDigit(secondDigit, secondNegative);
    } else {
        //  same size
        firstDigit = popDigit(first, firstSize);
        firstDigit = complementDigit(firstDigit, firstNegative);
        secondDigit = popDigit(second, secondSize);
        secondDigit = complementDigit(secondDigit, secondNegative);
    }

    //  recursion ends at LSB
    if ((firstSize == 0) && (secondSize == 0)) {
        //  if negative, add 1 to complemented LSB
        if (firstNegative) {
            firstDigit++;
        }
        if (secondNegative) {
            secondDigit++;
        }
        myCarry = previousCarry;
    } else {
        myCarry = addNUMBERs(first, firstSize, firstNegative,
            second, secondSize, secondNegative,
            previousCarry);
    }

    /*
        2nd half of the problem.

        Sum the digits and save them in first[].
    */
    mySum = firstDigit + secondDigit + ((myCarry) ? 1 : 0);
    if ((myCarry = (mySum > 9))) {
        mySum -= 10;
    }
    pushSum(mySum + '0');
    return(myCarry);
}

//  Handle the printing logic.
void addAndPrint(char *first, int firstSize, bool firstNegative,
        char *second, int secondSize, bool secondNegative,
        int previousCarry) {

    openSum();
    addNUMBERs(first, firstSize, firstNegative,
        second, secondSize, secondNegative,
        previousCarry)
    closeSum();
    if (*sumPointer<'5') {
        //  it's positive
        for (; *sumPointer=='0'; sumPointer++) {} // discard leading 0s
        //  if all zeros (sumPointer @ NUL), back up one
        sumPointer -= (*sumPointer == NUL) ? 1 : 0;
        printf("%s\n", sumPointer);
    } else {
        //  it's negative
        char    *p;

        //  discard leading 0s (9s in 10s complement)
        for (; *sumPointer=='9' && *sumPointer; sumPointer++) {}
        //  if -1 (sumPointer @ EOS), back up one
        sumPointer -= (*sumPointer == NUL) ? 1 : 0;
        for (p=sumPointer; *p; p++) {
            *p = '0' + ('9' - *p); // uncomplement
            //  special handling, +1 for last digit
            *p += (*(p+1)) ? 0 : 1;
        }
        printf("-%s\n", sumPointer);

    }
    return;
}

/*
    Lex a number from STDIN.

Arguments:
    bufferPointer - pointer to a pointer to a buffer, use as
            **buffer = c;   // put "c" in the buffer
            *buffer += 1;   // increment the buffer pointer
            (*buffer)++;    // also increments the buffer pointer

All sorts of side-effects:
    - getc(stdin)
    - ungetc(...,stdin)
    - modifies value of **bufferPointer
    - modifies value of *bufferPointer

Returns:
    lexNUMBER() - number of bytes added to *bufferPointer,
            *1 if POSITIVENUMBER,
            *-1 if NEGATIVENUMBER
    *bufferPointer - points to the LSB of the number parsed + 1
*/
#define pushc(c) (*((*bufferPointer)++)=c)
bool lexNUMBER(char **bufferPointer) {
    char    c;
    int size = 0;
    bool    sign = false;

    /* lex a NUMBER */
    if ((c=getchar()) == '0') {
        pushc(c);
        c = getchar();
        size++;
    } else {
        if (c == '-') {
            sign = true;
            c = getchar();
            // "-" isn't a digit, don't add to size
        }
        if (c == '0') {
            die();
        }
        for (size=0; isdigit(c); size++) {
            if (size >= MAXDIGITS) {
                die();
            }
            pushc(c);
            c = getchar();
        }
    }
    if (size < 1) {
        die();
    }
    ungetc(c,stdin);        // give back unmatched character
    return (sign);
}

int main() {
    int c;
    char    buffer[MAXVALIDINPUT];
    char    *bufferPointer;
    char    *first, *second;
    int firstSize, secondSize;
    bool    firstNegative, secondNegative;

    bufferPointer = buffer + 1; // hack, space for leading digit
    //  parse 1st number
    first = bufferPointer;
    firstNegative = lexNUMBER(&bufferPointer);
    firstSize = bufferPointer - first;
    *(bufferPointer++) = NUL;   // hack, space for EOS
    bufferPointer++;        // hack, space for leading digit
    //  parse separating blank
    if ((c=getchar()) != ' ') {
        die();
    }
    //  parse 2nd number
    second = bufferPointer;
    secondNegative = lexNUMBER(&bufferPointer);
    secondSize = bufferPointer - second;
    *(bufferPointer++) = NUL;   // hack, space for EOS
    //  parse end of input
    c = getchar();
    if (! ((c == EOF) || ((c == '\n') && ((c=getchar()) == EOF))) ) {
        die();
    }
    //  Some very implementation-specific massaging.
    *(--first) = '0';       // prefix with leading 0
    *(first+(++firstSize)) = NUL;   // add EOS
    *(--second) = '0';      // prefix with leading 0
    *(second+(++secondSize)) = NUL; // add EOS
    //  add and print two arbitrary precision numbers
    addAndPrint(first, firstSize, firstNegative,
        second, secondSize, secondNegative, false);
    return(0);
}

Aquí hay un pequeño arnés de prueba y algunos casos de prueba para comenzar. Siéntase libre de arrancar el uso excesivo de perl. El sistema en el que se desarrolló no tenía una fiesta moderna.

#!/bin/bash
#
#   testharness.sh
#
# Use as: bash testharness.sh program_to_be_tested < test_data
#
# Each line in the test data file should be formatted as:
#
#   INPUT = bash printf string, must not contain '"'
#   OUTPUT = perl string, must not contain '"'
#           (inserted into the regex below, use wisely)
#   TESTNAME = string, must not contain '"'
#   GARBAGE = comments or whatever you like
#   INPUTQUOTED = DQUOTE INPUT DQUOTE
#   OUTPUTQUOTED = DQUOTE OUTPUT DQUOTE
#   TESTQUOTED = DQUOTE TESTNAME DQUOTE
#   TESTLINE = INPUTQUOTED *WSP OUTPUTQUOTED *WSP TESTQUOTED GARBAGE
TESTPROGRAM=$1
TMPFILE=testharness.$$
trap "rm $TMPFILE" EXIT
N=0         # line number in the test file
while read -r line; do
    N=$((N+1))
    fields=$(perl -e 'split("\"",$ARGV[0]);print "$#_";' "$line")
    if [[ $fields -lt 5 ]]; then
        echo "skipped@$N"
        continue
    fi
    INPUT=$(perl -e 'split("\"",$ARGV[0]);print "$_[1]";' "$line")
    OUTPUT=$(perl -e 'split("\"",$ARGV[0]);print "$_[3]";' "$line")
    TESTNAME=$(perl -e 'split("\"",$ARGV[0]);print "$_[5]";' "$line")
    printf -- "$INPUT" | $TESTPROGRAM > $TMPFILE
    perl -e "\$t='^\\\s*$OUTPUT\\\s*\$'; exit (<> =~ \$t);" < $TMPFILE
    if [[ $? -ne 0 ]]; then     # perl -e "exit(0==0)" => 1
        echo "ok $TESTNAME"
    else
        echo -n "failed@$N $TESTNAME," \
            "given: \"$INPUT\" expected: \"$OUTPUT\" received: "
        cat $TMPFILE
        echo
    fi
done


Un pequeño conjunto de casos de prueba:

"0 0"       "0" "simple, 0+0=0"
"1 1"       "2" "simple, 1+1=2"

""      "E" "error, no numbers"
"0"     "E" "error, one number"
"0  0"      "E" "error, two/too much white space"
"0 0 "      "E" "error, trailing characters"
"01 0"      "E" "error, leading zeros not allowed"

"-0 0"      "E" "error, negative zero not allowed
"0 -0"      "E" "error, negative zero not allowed here either

"1 1\n"     "2" "LF only allowed trailing character

"\0001 1\n"    "E" "error, try to confuse C string routines #1"
"1\00 1\n"  "E" "error, try to confuse C string routines #2"
"1 \0001\n"    "E" "error, try to confuse C string routines #3"
"1 1\000\n"    "E" "error, try to confuse C string routines #4"
"1 1\n\000"    "E" "error, try to confuse C string routines #5"

"-1 -1"     "-2"    "add all combinations of -1..1 #1"
"-1 0"      "-1"    "add all combinations of -1..1 #2"
"-1 1"      "0" "add all combinations of -1..1 #3"
"0 -1"      "-1"    "add all combinations of -1..1 #4"
"0 0"       "0" "add all combinations of -1..1 #5"
"0 1"       "1" "add all combinations of -1..1 #6"
"1 -1"      "0" "add all combinations of -1..1 #7"
"1 0"       "1" "add all combinations of -1..1 #8"
"1 1"       "2" "add all combinations of -1..1 #9"

"0 123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "0+99 digits should work"

"100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" "200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" "99 digits+99 digits should work"

"500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" "1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" "test for accumulator overflow"

"-123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 0" "-123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" "0+negative 99 digits work"

"-100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" "-200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" "99 digits+99 digits (both negative) should work"

"-500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" "-1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" "test for negative accumulator overflow"

"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 0" "E" "error, 100 digits"
Scott Leadley
fuente
Se corrigieron las dos últimas pruebas de números negativos grandes. No estaban probando números negativos.
Scott Leadley
Recibo muchas advertencias con -Wall -Wextra de su código.
@Lembik Otros carteles pueden copiar el lexer y corregir las advertencias del compilador. Implementa un lexer seguro y hace aritmética de precisión arbitraria (más o menos). Dediqué algo de esfuerzo a la codificación errónea (ver comentarios en el código), por lo que debería recibir muchas advertencias del compilador. ¿Qué tal una estrella dorada para el lexer y una bota rápida en la parte trasera por no tener en cuenta los otros criterios?
Scott Leadley
Eso parece muy razonable :) Considéralo hecho.
1
@Lembick Reemplazado con todas las soluciones C usando aritmética complementaria de 10s. Se movió la solución de "mal ejemplo" bcpara separar la respuesta.
Scott Leadley
3

289

EDITAR : Este código funciona solo para enteros positivos. Las reglas han cambiado desde que publico esta respuesta.

#include <stdio.h>
#include <stdlib.h>
int r[101],s;void x(int i){r[i]|=32;if(r[i]>41)r[i+1]++,r[i]-=10,x(i+1);}void f(int b){int c=getchar();if(c!=b){if(s>99||c<48||c>57)exit(puts("E"));f(b);r[s]+=c-48;x(s++);}}int main(){f(32);s=0;f(10);for(s=100;s--;)if(r[s])putchar(r[s]+16);return 0;}

Versión no comentada y comentada:

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

//global variables are automatically init to zero
int result[101]; //101 because 2 numbers of 100 digits can give a 101-digits result
int currentNumber;

void reportAddition(int i) {
    result[i]|=0x20; //flag "active" value, 6th bit
    if(result[i]>9+0x20) {
        result[i+1]++;
        result[i]-=10;
        reportAddition(i+1);
    }
}

void addNumber(int endingChar) {
    int c=getchar();
    if(c!=endingChar) {
        if(currentNumber>99||c<'0'||c>'9') //error
            exit(puts("Error"));
        addNumber(endingChar);
        result[currentNumber]+=c-'0';
        reportAddition(currentNumber); //handle case when addition give a value greater than 9
        currentNumber++;
    }
}

int main() {

    addNumber(' '); //add first number
    currentNumber=0;
    addNumber('\n'); //add second

    for(currentNumber=100;currentNumber--;)
        if(result[currentNumber])
            putchar(result[currentNumber]+'0'-0x20); //display char
    return 0;
}
Michael M.
fuente
¿Rechaza entradas de más de 99 dígitos? Me parece que deberías.
John Dvorak
@ JanDvorak, sí, lo hago.
Michael M.
Consigo varias señales de advertencia de compilación:./tmp.c: In function ‘f’: ./tmp.c:3:1: warning: suggest parentheses around comparison in operand of ‘|’ [-Wparentheses] ./tmp.c:3:1: warning: suggest parentheses around comparison in operand of ‘|’ [-Wparentheses] ./tmp.c: In function ‘main’: ./tmp.c:3:1: warning: control reaches end of non-void function [-Wreturn-type]
user12205
No quise especificar que los números fueran positivos. Disculpas
@ace No tengo esta advertencia aquí, ¿está reemplazando (s>99|c<48|c>57)por (s>99||c<48||c>57)arreglar eso?
Michael M.
2

442

Es bastante largo, así que puedo jugar golf durante el fin de semana. Asume que la entrada es desde stdin, terminada en EOF (sin nueva línea), el separador es solo un carácter de valor ASCII 32 (es decir, ' 'carácter).

#include<stdio.h>
char a[102],b[102],c[102],*p=a,d;int i,j=101,l,L,k,F=1;int main(){while(~(k=getchar())){if(47<k&&k<58){p[i++]=k;if(i==101)goto f;}else if(k==32&&F)p=b,l=i,F=0,i=0;else goto f;}L=i;for(i=(l<L?l:L)-1;i+1;i--){c[j]=(L<l?b[i]-48+a[i+l-L]:a[i]-48+b[i+L-l]);if(c[j--]>57)c[j]++,c[j+1]-=10;}for(i=(L<l?l-L-1:L-l-1);i+1;i--)c[j--]=(L<l?a[i]:b[i]);for(i=0;i<102;i++)if(c[i]&&(c[i]-48||d))d=putchar(c[i]);return 0;f:return puts("E");}

El mensaje de error será un solo carácter 'E', seguido de una nueva línea.

Con nuevas líneas y un poco de sangría agregada: (sigue una versión legible, así que siéntase libre de saltar aquí)

#include<stdio.h>
char a[102],b[102],c[102],*p=a,d;
int i,j=101,l,L,k,F=1;
int main(){
    while(~(k=getchar())){
        if(47<k&&k<58){
            p[i++]=k;
            if(i==101)goto f;
        }else if(k==32&&F)p=b,l=i,F=0,i=0;
        else goto f;
    }
    L=i;
    for(i=(l<L?l:L)-1;i+1;i--){
        c[j]=(L<l?b[i]-48+a[i+l-L]:a[i]-48+b[i+L-l]);
        if(c[j--]>57)c[j]++,c[j+1]-=10;
    }
    for(i=(L<l?l-L-1:L-l-1);i+1;i--)c[j--]=(L<l?a[i]:b[i]);
    for(i=0;i<102;i++)if(c[i]&&(c[i]-48||d))d=putchar(c[i]);
    return 0;
    f:return puts("E");
}

La versión legible (algunas declaraciones están ligeramente alteradas para que sea más legible, pero lo que hacen debería ser lo mismo):

#include <stdio.h>
char num1[102],num2[102],sum[102]; //globals initialised to 0
char *p=num1;
int outputZero=0, noExtraSpace=1;
int i=0, j=101, len1, len2, ch;
#define min(x,y) (x<y?x:y)
int main(){
    while((ch=getchar())!=-1) { //assumes EOF is -1
        if('0'<=ch && ch<='9') {
            p[i]=ch;
            i++;
            if(i==101) goto fail; //if input too long
        } else if(ch==' ' && noExtraSpace) {
            p=num2;
            len1=i;
            noExtraSpace=0;
            i=0;
        } else goto fail; //invalid character
    }
    len2=i;
    for(i=min(len1, len2)-1; i>=0; i--) {
        //add each digit when both numbers have that digit
        sum[j]=(len2<len1?num2[i]-'0'+num1[i+len1-len2]:num1[i]-'0'+num2[i+len2-len1]);
        if(sum[j]>'9') { //deal with carries
            sum[j-1]++;
            sum[j]-=10;
        }
        j--;
    }
    for(i=(len2<len1?len1-len2-1:len2-len1-1); i>=0; i--) {
        //copy extra digits when one number is longer than the other
        sum[j]=(len2<len1?num1[i]:num2[i]);
        j--;
    }
    for(i=0; i<102; i++) {
        if(sum[i] && (sum[i]-'0' || outputZero)) {
            putchar(sum[i]);
            outputZero=1;
            //if a digit has been output, the remaining zeroes must not be leading
        }
    }
    return 0;
    fail:
    puts("Error");
    return 0;
}

La goto fail;cosa está aquí para burlarse de Apple.

La versión de gcc que utilicé es gcc (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3, y no hay advertencias.

usuario12205
fuente
¿Qué pasa con -1 1 como entrada?
@Lembik imprime un mensaje de error porque la entrada debe ser "Dos enteros positivos separados por espacios ".
user12205
Oh lo siento, lo arruiné. Pensé que había cambiado eso.
Actualmente su código siempre genera E para mí. Intenté "1 1" por ejemplo. ¿También podría hacer que la entrada provenga de stdin?
@Lembik La entrada es de stdin (getchar() siempre se obtiene de stdin). Se supone que tiene terminación EOF sin nueva línea . Puede probar esto, ya sea ingresando [1] [espacio] [1] [Ctrl + D] [Ctrl + D] oecho -n '1 1' | program
user12205
0

633 bytes

Programa "Bad boy" C que cumple con la mitad del desafío. Abusa de C, lanza muchas advertencias, pero funciona ... más o menos. La aritmética de precisión arbitraria en realidad se realiza mediante bc.

#include<stdio.h>
#include<string.h>
#include<ctype.h>
#define g() getc(stdin)
#define z(c) b[i++]=c,b[i]='\0'
x(){puts("E\n");exit(1);}main(){int c,y=0,i=0;char b[232]="",*h="echo ",*t=" | bc -q | tr -d '\\\\\\012'";strcat(b,h);i=strlen(h);if((c=g())=='0'){z(c);y=1;c=g();}else{if(c=='-'){z(c);c=g();}c!='0'||x();ungetc(c,stdin);for(y=0;isdigit(c=g());y++){z(c);y<99||x();}}y>0||x();c==' '||x();z('+');y=0;if((c=g())=='0'){z(c);y=1;c=g();}else{if(c=='-'){z(c);c=g();}c!='0'||x();do{if(!isdigit(c))break;z(c);y++;y<=99||x();}while(c=g());}y>0||x();strcat(b+i,t);i+=strlen(t);if(!((c==-1)||((c=='\n')&&((c=g())==-1))))x();system(b);}

Versión no minificada

/*
Read input from STDIN. The input must conform to VALIDINPUT:

    NONZERODIGIT = "1" / "2" / "3" / "4" / "5" / "6" / "7" / "8" / "9"
    POSITIVENUMBER = NONZERODIGIT *98DIGIT
    NEGATIVENUMBER = "-" POSITIVENUMBER
    NUMBER = NEGATIVENUMBER / POSITIVENUMBER / "0"
    VALIDINPUT = NUMBER SP NUMBER *1LF EOF

Check syntax of input. If input is correct, use the shell command
"echo NUMBER+NUMBER | bc -q" to do the arbitrary precision integer arithmetic.

NB, this solution requires that the "normal" bc be 1st in the PATH.


    Fun C language features used:
    - ignore arguments to main()
    - preprocessor macros
    - pointer arithmetic
    - , operator
    - ?: operator
    - do-while loop
    - for loop test does input
    - implicit return/exit
    - short is still a type!
    - ungetc()
    - use int as bool and 0 & 1 as magic numbers
    - implicit type coersion

5/5/14, Stop fighting the syntax graph. Get rid of "positive" flag.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define MAXDIGITS (99)
#define pushc(c) (buffer[index++]=c,buffer[index]='\0')
#define pushs(s) (strcat(buffer+index,s),index+=strlen(s))

int die() { printf("E\n"); exit(1); }

int main () {
    int c;
    /*
    buffer budget:
    5               "echo "
    1+MAXDIGITS (include "-")   NUMBER
    1               "+"
    1+MAXDIGITS (include "-")   NUMBER
    25              " | bc -q | tr -d '\\\012'"
    1               NUL
    */
    char    buffer[5+1+MAXDIGITS+1+1+MAXDIGITS+25+1] = "";
    short   index = 0;
    short   digits;

    pushs("echo ");
    // parse 1st number
    digits = 0;
    if ((c=getchar()) == '0') {
        pushc(c);
        digits = 1;
        c = getchar();
    } else {
        if (c == '-') {
            // "-" doesn't count against digits total
            pushc(c);
            c = getchar();
        }
        (c != '0') || die();
        ungetc(c,stdin);
        for (digits=0; isdigit(c=getchar()); digits++) {
            pushc(c);
            (digits<MAXDIGITS) || die();
        }
    }
    (digits>=1) || die();
    // parse separating blank
    (c == ' ') || die();
    pushc('+');
    // parse 2nd number
    digits = 0;
    if ((c=getchar()) == '0') {
        pushc(c);
        digits = 1;
        c = getchar();
    } else {
        if (c == '-') {
            // "-" doesn't count against digits total
            pushc(c);
            c = getchar();
        }
        (c != '0') || die();
        do {
            if (!isdigit(c)) {
                break;
            }
            pushc(c);
            digits++;
            (digits<=MAXDIGITS) || die();
        } while(c=getchar());
    }
    (digits>=1) || die();
    pushs(" | bc -q | tr -d '\\\\\\012'");
    // parse end of input
    if (! ((c == EOF) || ((c == '\n') && ((c=getchar()) == EOF))) ) {
        die();
    }
    // add two arbitrary precision numbers and print the result to STDOUT
    system(buffer);
}
Scott Leadley
fuente