Додайте два числа безпечно, на С


24

Всім відомо, що C - це чудова, безпечна мова програмування високого рівня. Однак вам, як кодеру, поставлено наступне завдання.

Напишіть програму, щоб додати два числа.

  • Введення: Два цілих числа, розділених пробілом.
  • Вихід: сума двох чисел у введенні.

Поворот у тому, що ваш код повинен бути на 100% безпечним. Іншими словами, він повинен вести себе належним чином, незалежно від того, який вхід є. Якщо вхід є справді двома цілими числами, розділеними пробілом, обидва з яких мають довжину менше 100 цифр, він повинен вивести суму. В іншому випадку воно повинно вивести повідомлення про помилку та безпечно вийти.

Наскільки важко це може бути?

Загальні кудо будуть надані для патологічних випадків, які порушують відповіді інших людей :)

Код повинен компілюватися без попереджень, використовуючи gcc -Wall -Wextra на ubuntu.


Уточнення.

  • Введення з stdin.
  • Горизонтальний пробіл - це лише один пробіл. Перед першим номером не повинно бути нічого, і вхід повинен бути завершений або новим рядком + EOF, або просто EOF.
  • Єдиний коректний вклад, зазначений у формі Доповнений Бекус-Наура , є:
    NONZERODIGIT = "1" / "2" / "3" / "4" / ​​"5" / "6" / "7" / "8" / "9"
    POSITIVENUMBER = NONZERODIGIT * 98DIGIT
    NEGATIVENUMBER = "-" POSITIVENUMBER
    NUMBER = NEGATIVENUMBER / POSITIVENUMBER / "0"
    VALIDINPUT = ЧИСЛО SP NUMBER * 1LF EOF
  • Повідомлення про помилку - це одна буква "E", після якої новий рядок.
  • Код повинен закінчуватися чисто менш ніж за 0,5 с, незалежно від входу.
code-golf  c 

2
Мені подобається, наскільки чітко стала ваша специфікація одним вживанням слова "інакше". Дуже добре написано (на мою думку). Шкода, що виклик є лише у С, але я можу зрозуміти, чому це має бути саме так.
Rainbolt

3
Без попереджень + код-гольф + С? Це неодмінно буде цікаво!
Форс

1
stdin чи командний рядок?
писклива косточка

2
Чи можемо ми отримати чітко прийнятний формат введення? Наприклад, немає зайвого пробілу (або нічого, що не число) перед першим номером, будь-яка кількість горизонтального пробілу (тобто горизонтальних вкладок і пробілів) між двома номерами, і нічого, крім передачі рядка або перевезення, не повертаються разом із передачею рядка після другого номера номер?
Форс

3
Також, чи означає розділене пробілом значення 1), розділене одним символом зі значенням 32, 2 ASCII), розділене довільним числом символів зі значенням ASCII 32, 3), розділене будь-яким одним символом пробілу ASCII (32, 10, 9, можливо 13 ?), 4) відокремлений довільним числом одного символу пробілів від набору символів пробілів ASCII або 5) відокремлений довільним числом будь-якої комбінації безлічі символів пробілів ASCII?
користувач12205

Відповіді:


4

6610 байт (не змінено)

Програма C "хороший хлопчик", яка відповідає всім критеріям виклику. Використовує доповнення 10s для від’ємних чисел. Також включені тестовий джгут та тестові кейси.

/*
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);
}

Ось невеликий тестовий ремінь та кілька тестових випадків для початку роботи. Не соромтеся вирвати надмірне використання перла. Система, на якій вона була розроблена, не мала сучасного удару.

#!/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


Невеликий набір тестових випадків:

"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"

Виправлені останні два великі мінус-тести. Вони не перевіряли негативні цифри.
Скотт Лідлі

Я отримую багато попереджень із -Wall -Wextra від вашого коду.

@Lembik Інші афіші можуть сміливо копіювати лексеми та виправляти попередження компілятора. Він реалізує безпечний лексер і робить арифметику довільної точності (на зразок). Я витратив певні зусилля на неправильне кодування (див. Коментарі в коді), тому вам слід отримати багато попереджень компілятора. Як щодо золотої зірки для лексера та швидкого черевика ззаду для нехтування іншими критеріями?
Скотт Лідлі

Це здається дуже розумним :) Вважайте це зробленим.

1
@ Лембік Замінено всім розчином C, використовуючи арифметику доповнення 10s. Перенесено "поганий приклад" рішення, використовуючи bcдля роздільної відповіді.
Скотт Лідлі

3

289

EDIT : Цей код працює лише для натуральних чисел. Правила змінилися з моменту опублікування цієї відповіді.

#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;}

Безгольова та коментована версія:

#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;
}

Чи відхиляєте ви введення довше 99 цифр? Мені здається, слід.
Джон Дворак

@JanDvorak, так, я.
Майкл М.

Я отримую кілька попереджень про компіляцію:./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

Я не мав на увазі, щоб цифри були позитивними. Вибачення.

@ace У мене немає на це попередження тут, замінює (s>99|c<48|c>57)на (s>99||c<48||c>57)виправлення цього?
Михайло М.

2

442

Це досить довго, тож я можу гольфу його ще далі у вихідні. Припустимо, що введення відбувається зі stdin, що закінчується EOF (без нового рядка), роздільником є ​​лише один символ значення ASCII 32 (тобто ' 'символ).

#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");}

Повідомлення про помилку буде одним символом "E", а за ним новий рядок.

Додано нові рядки та трохи відступу: (слідує читана версія, тому сміливо пропускайте сюди)

#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");
}

Версія для читання (деякі твердження дещо змінені, щоб зробити її зручнішою для читання, але те, що вони роблять, має бути однаковим):

#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;
}

Тут goto fail;справа, щоб знущатися над Apple.

Я використовував версію gcc gcc (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3, і попереджень немає.


Як щодо -1 1 як вхід.

@Lembik виводить повідомлення про помилку , тому що вхід повинен бути «Два розділеним пробілу позитивних цілих чисел.»
користувач12205

О, вибачте, що я це заглушив. Я думав, що це змінив.

Наразі ваш код завжди видає E для мене. Я спробував "1 1", наприклад. Ви також можете зробити вхід зі СТДІН?

@ Lembik Вхід із stdin ( getchar()завжди отримується від stdin). Передбачається, що закінчено EOF без нового рядка . Ви можете перевірити це, або ввівши [1] [пробіл] [1] [Ctrl + D] [Ctrl + D], абоecho -n '1 1' | program
user12205

0

633 байт

Програма C "поганий хлопчик", яка відповідає половині викликів. Зловживає C, кидає багато попереджень, але працює ... подібне. Довільна арифметика точності насправді виконується компанією 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);}

Немініфікована версія

/*
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);
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.