Dodaj bezpiecznie dwie liczby w C.


24

Wszyscy wiedzą, że C jest pięknym, bezpiecznym językiem programowania na wysokim poziomie. Jednak jako programista ustawiłeś następujące zadanie.

Napisz program, aby dodać dwie liczby.

  • Wejście: dwie liczby całkowite oddzielone spacjami.
  • Wyjście: Suma dwóch liczb na wejściu.

Rzecz w tym, że Twój kod musi być w 100% bezpieczny. Innymi słowy, musi działać poprawnie bez względu na wejście. Jeśli dane wejściowe są rzeczywiście dwiema liczbami całkowitymi oddzielonymi spacjami, z których każda ma długość mniejszą niż 100 cyfr, musi wyprowadzić sumę. W przeciwnym razie musi wygenerować komunikat o błędzie i zakończyć pracę bezpiecznie.

Jak trudne może być w końcu?

Ogólne podziękowania zostaną przekazane patologicznym przypadkom wejściowym, które łamią odpowiedzi innych ludzi :)

Kod musi się kompilować bez ostrzeżeń przy użyciu gcc -Wall -Wextra na Ubuntu.


Wyjaśnienie.

  • Dane wejściowe pochodzą ze standardowego wejścia.
  • Poziome białe znaki to tylko jedna spacja. Przed pierwszą liczbą nie powinno być niczego, a wprowadzanie danych powinno być zakończone znakiem nowej linii + EOF lub po prostu EOF.
  • jedyne prawidłowe dane wejściowe określone w formularzu Augmented Backus-Naur to:
    NONZERODIGIT = „1” / „2” / „3” / „4” / „5” / „6” / „7” / „8” / „9”
    POSITIVENUMBER = NONZERODIGIT * 98DIGIT
    NEGATIVENUMBER = "-" POSITIVENUMBER
    NUMBER = NEGATIVENUMBER / POSITIVENUMBER / „0”
    VALIDINPUT = NUMER SP NUMBER * 1LF EOF
  • Komunikatem o błędzie jest pojedyncza litera „E”, po której następuje nowy wiersz.
  • Kod musi zakończyć się czysto w czasie krótszym niż 0,5 s bez względu na wejście.
code-golf  c 

2
Uwielbiam to, jak wyraźna była twoja specyfikacja za pomocą jednego słowa „inaczej”. Bardzo dobrze napisane (moim zdaniem). Szkoda, że ​​wyzwanie jest tylko w C, ale rozumiem, dlaczego tak musi być.
Rainbolt

3
Bez ostrzeżeń + code-golf + C? To na pewno będzie interesujące!
Fors

1
standardowe lub wiersz poleceń?
piskliwy ossifrage

2
Czy możemy uzyskać ściśle akceptowalny format wejściowy? Takich jak brak dodatkowych białych znaków (lub cokolwiek innego niż liczba) przed pierwszą liczbą, dowolna ilość poziomych białych znaków (tj. Poziome tabulatory i spacje) między dwiema liczbami, i nic poza dosuwem linii lub znakiem powrotu karetki wraz z dosuwem linii po drugim numerze numer?
Fors

3
Czy również oznacza wartość oddzieloną spacją 1) oddzieloną jednym znakiem o wartości ASCII 32, 2) oddzieloną dowolną liczbą znaków o wartości ASCII 32, 3) oddzieloną dowolnym znakiem białych znaków ASCII (32, 10, 9, ewentualnie 13 ?), 4) oddzielone dowolną liczbą pojedynczych białych znaków od zestawu białych znaków ASCII, lub 5) oddzielone dowolną liczbą dowolnych kombinacji zestawu białych znaków ASCII?
użytkownik12205

Odpowiedzi:


4

6610 bajtów (unminified)

Program „Good boy” C spełniający wszystkie kryteria wyzwania. Używa uzupełnienia 10s dla liczb ujemnych. W zestawie także uprząż testowa i walizki testowe.

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

Oto mała uprząż testowa i kilka przypadków testowych na początek. Możesz oderwać się od nadmiernego używania perla. System, na którym został opracowany, nie miał nowoczesnego basha.

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


Mały zestaw przypadków testowych:

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

Naprawiono dwa ostatnie duże testy liczby ujemnej. Nie testowali liczb ujemnych.
Scott Leadley

Otrzymuję wiele ostrzeżeń z -Wall -Wextra z twojego kodu.

@Lembik Inne plakaty mogą kopiować leksykon i naprawiać ostrzeżenia kompilatora. Implementuje bezpieczny leksykon i wykonuje arytmetykę o dowolnej precyzji (niejako). Poświęciłem trochę wysiłku na złe kodowanie (patrz komentarze w kodzie), więc powinieneś dostać wiele ostrzeżeń kompilatora. Co powiesz na złotą gwiazdę dla leksyk i szybki bagażnik z tyłu za ignorowanie innych kryteriów?
Scott Leadley,

To wydaje się bardzo rozsądne :) Rozważ to zrobione.

1
@Lembick Zastąpiony wszystkimi rozwiązaniami C przy użyciu arytmetyki 10s uzupełnienia. Przeniesiono rozwiązanie „zły przykład” bcdo oddzielnej odpowiedzi.
Scott Leadley,

3

289

EDYCJA : Ten kod działa tylko dla dodatnich liczb całkowitych. Zasady zmieniły się, odkąd opublikowałem tę odpowiedź.

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

Wersja nieposortowana i skomentowana:

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

Czy odrzucasz dane dłuższe niż 99 cyfr? Wydaje mi się, że powinieneś.
John Dvorak

@JanDvorak, tak robię.
Michael M.

Otrzymuję kilka ostrzeżeń dotyczących kompilacji:./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

Nie chciałem podawać, że liczby są dodatnie. Przeprosiny.

@ace nie mam to ostrzeżenie tutaj, jest zastąpienie (s>99|c<48|c>57)przez (s>99||c<48||c>57)poprawić?
Michael M.

2

442

Jest dość długi, więc w weekend mogę go pograć w golfa. Zakłada, że ​​dane wejściowe pochodzą ze standardowego wejścia, zakończonego EOF (bez nowej linii), separator to tylko jeden znak o wartości ASCII 32 (to znaczy ' 'znak).

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

Komunikatem o błędzie będzie pojedynczy znak „E”, a po nim nowy wiersz.

Z nowymi liniami i odrobiną wcięcia: (następująca wersja jest czytelna, więc możesz pominąć tutaj)

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

Wersja do odczytu (niektóre instrukcje zostały nieco zmienione, aby były bardziej czytelne, ale to, co robią, powinno być takie samo):

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

Chodzi goto fail;o to, żeby drwić z Apple.

Użyłem wersji gcc gcc (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3i nie ma żadnych ostrzeżeń.


Co powiesz na -1 1 jako dane wejściowe.

@Lembik drukuje komunikat o błędzie, ponieważ wejściem powinno być „ dodatnie liczby całkowite oddzielone spacjami ”.
user12205

Och przepraszam, spieprzyłem to. Myślałem, że to zmieniłem.

Obecnie twój kod zawsze wyświetla dla mnie literę E. Próbowałem na przykład „1 1”. Czy możesz również wprowadzić dane wejściowe ze standardowego wejścia?

@Lembik Dane wejściowe pochodzą ze standardowego wejścia ( getchar()zawsze pobierane ze standardowego wejścia). Zakłada się, że jest zakończony EOF bez nowej linii . Możesz to przetestować, wpisując [1] [spacja] [1] [Ctrl + D] [Ctrl + D] lubecho -n '1 1' | program
użytkownik12205

0

633 bajtów

Program „Bad boy” C, który spełnia połowę wyzwania. Nadużywa C, rzuca wiele ostrzeżeń, ale działa ... tak jakby. Arbitralna precyzja arytmetyczna jest faktycznie wykonywana przez 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);}

Wersja nieuprawniona

/*
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);
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.