C++第3版 6章 練習問題解答

目次へ戻る

6.1

i=0;
while (i<max_length) {
    if (input_line[i] == '?') quest_count++;
    i++;
}
p=input_line;
while (p<input_line+max_length) {
    if (*p == '?') quest_count++;
    p++;
}

6.2

a = (((b + (c * d)) << 2) & 8)
a & (077 != 3)
(a == b) || ((a == c) && (c < 5))
c = (x != 0)
(0 <= i) < 7
(f(1,2)) + 3
a = (((-1) + (+ (b--))) - 5)
a = (b == (c++))
a = (b = (c = 0))
(a[4][2]) *= ((*b) ? c : ((*d)*2))
(a-b),(c=d)
ビット演算子と比較演算子の優先順位に注意

6.3

#include <iostream>
#include <strstream>
#include <string>
#include <map>
using namespace std;
#pragma warning(disable : 4786)

struct Count {
    int     n;
    double  sum;
    Count(){sum = n = 0;}
    void Add(double d){sum += d; ++n;}
    double Ave()const{return sum / n;}
};

int main()
{
    istrstream          data("ポン太 10.5 ハナ 22 ゴンベ 77.8"
                  " ポン太 36.4 ゴンタ 21 ゴンベ 62 ハナ 86.2");
    string              nam;
    double              val;
    Count               total;
    map<string,Count>   m;
    while (data >> nam >> val) {
        total.Add(val);
        m[nam].Add(val);
    }
    cout << "Total " << total.Ave() << " " << total.sum << endl;
    map<string,Count>::iterator i;
    for (i=m.begin();i!=m.end();++i)
        cout << i->first.c_str() << " " <<
        i->second.Ave() << " " << i->second.sum << endl;
}
pragma は VC++ の警告を避けるため。

6.4

#include <iostream>
using namespace std;

#define PRINTIT(P,S) cout << i << " " << S \
     << " " << j << " = " << (i P j) << endl;
int main()
{
    int     i,j;
    for (i=0;i<=1;++i) for (j=0;j<=1;++j) {
        PRINTIT(&,"&");
        PRINTIT(|,"|");
        PRINTIT(^,"^");
    }
}

6.5

意味が定義されていない動作 意味が実装によって定義される動作(処理系定義の動作) (ANSI で)意味が定義されていない動作は、 「未定義の動作」と「不定の動作」に2つに分かれる。 未定義の動作は再現性は保証されず、正常なプログラムとは言えない。 不定の動作は正常なプログラムだが、同じく再現性が保証されない。 処理系定義の動作は再現性は保証されるが、処理系によって結果が異なることがある。

6.6

移植性のないコードなどすぐ書いてしまうくせに、 わざと書くのは難しい。
struct Test {char c; int i;};

int main()
{
    int     i;
    float   f;

    char c = 255;
    (i = -1) >> 1;
#pragma pack()
    system("ls");
    i = -1 / static_cast<unsigned>(0x1000);
    i = -8/3;
    if ((f = 1.2) == 1.2) ;
    FILE* fp = fopen("a:\\test.txt","r");
    sizeof(Test) == sizeof(char) + sizeof(int);
    malloc(0);
}

6.7

実行結果は省略。
    printf("%d %d\n",++i,++i);
    ( i++ +1 ) * ( 2 + i );
    add( i + 1, i = j + 2 );
    myproc( getc(), getc() );
    x[i] = i++;

6.8

零割りでは、例外が発生する。 オーバーフロー、アンダーフローでは、値が 0 や INF になって処理は続く。 但し、errono が設定されているかもしれない。

6.9

ヘルプによると VC++ の優先順位は ANSI と違うようだ。
*(p++)
*(--p)
++(a--)
(int*)(p->m)
*(p.m)
*(a[i])

6.10

この問題は、前に考えたことがある。 プロトタイプ宣言は <cstring> と同じ。
#include <iostream>
using namespace std;

static size_t strlen(const char* s)
{
    int i = 0;
    while (*s++) ++i;
    return i;
}

static char* strcpy(char* s,const char* t)
{
    while ((*s++ = *t++) != 0) ;
    return s;
}

int main()
{
    char*  s = "test";
    char   x[10];
    strcpy(x,s);
    cout << strlen(s) << " " << x << endl;
}

6.11

最初の2つは、何もなし。 最後のは、「構文エラー : ';' が '=' の前に必要です」となる。 簡単なエラーとして ++1; とすると、「++には左辺値が必要です」となる。

6.12

main 関数のみ再褐する。(ヘッダも省略)
int main()
{
    istrstream          data("ポン太 10.5 ハナ 22 ゴンベ 77.8"
                   " ポン太 36.4 ゴンタ 21 ゴンベ 62 ハナ 86.2");
    string              nam;
    double              val;
    Count               total;
    map<string,Count>   m;
    vector<int>         v;
    while (data >> nam >> val) {
        total.Add(val);
        m[nam].Add(val);
        v.push_back(val);
    }
    cout << "Total " << total.Ave() << " " << total.sum << endl;
    sort(v.begin(),v.end());
    cout << "Center " << v[v.size()/2] << endl;
    map<string,Count>::iterator i;
    for (i=m.begin();i!=m.end();++i)
        cout << i->first.c_str() << " " <<
           i->second.Ave() << " " << i->second.sum << endl;
}

6.13

char* cat(const char* s,const char* t)
{
    char*   p = new char[strlen(s)+strlen(t)+1];
    sprintf(p,"%s%s",s,t);
    return p;
}

6.14

reverse は STL のアルゴリズム
char* rev(char* s)
{
    reverse(s,s+strlen(s));
    return s;
}

6.15

これは Tome Duff が Lucasfilm に在籍中に考えたものらしい。 from から to へのコピーをしている。 理由は、カウンタの減算と比較を約1/8に減らすため。

6.16

8進数と 16進数の時に、「+-」が付いたときの処理は省いた。 文字定数記法の処理は不必要と思うがよくわからない。
static int atoi(const char* s)
{
    if (!*s || (!isdigit(*s) && *s != '-' && *s != '+'))
        return 0;
    int     i;
    if (!strncmp(s,"0x",2))
        return sscanf(s,"%x",&i) == 1 ? i : 0;
    if (!strncmp(s,"0",1))
        return sscanf(s,"%o",&i) == 1 ? i : 0;
    return sscanf(s,"%d",&i) == 1 ? i : 0;
}

6.17

static char* itoa(int i,char b[])
{
    sprintf(b,"%d",i);
    return b;
}

6.18

大域変数を使うことは、私の性に合わないので全面的に書き直した。
#include <cmath>
#include <cctype>
#include <iostream>
#include <string>
#include <map>
using namespace std;
#pragma warning(disable : 4786)

struct CExpr {
    typedef double (*FType)(double);
    virtual ~CExpr(){};
    virtual double Value()const = 0;
    static map<string,double>   s_val;  // 変数とその値
    static map<string,FType>    s_func; // 関数
};

static inline void eatwhite(istream& cin)
{
    char    c;
    while (cin.get(c)) if (c != ' ') {cin.putback(c); break;}
}

class CBin : public CExpr {
protected:
    CExpr*      m_left;
    CExpr*      m_right;
    CBin(CExpr* p,CExpr* q) : m_left(p), m_right(q) {}
public:
    enum Type {Add,Sub,Mul,Div};
    ~CBin(){delete m_left; delete m_right;}
    static CExpr* New(CExpr* p,CExpr* q,Type t);
};

struct CAdd : public CBin {
    CAdd(CExpr* p,CExpr* q) : CBin(p,q) {}
    virtual double Value()const
        {return m_left->Value() + m_right->Value();}
};

struct CSub : public CBin {
    CSub(CExpr* p,CExpr* q) : CBin(p,q) {}
    virtual double Value()const
        {return m_left->Value() - m_right->Value();}
};

struct CMul : public CBin {
    CMul(CExpr* p,CExpr* q) : CBin(p,q) {}
    virtual double Value()const
        {return m_left->Value() * m_right->Value();}
};

struct CDiv : public CBin {
    CDiv(CExpr* p,CExpr* q) : CBin(p,q) {}
    virtual double Value()const{double d = m_right->Value();
        return m_left->Value() / (d ? d : 1) ;}
};

class CNega : public CExpr {
    CExpr*  m_ptr;
    CNega(CExpr* p) : m_ptr(p) {}
public:
    ~CNega(){delete m_ptr;}
    virtual double Value()const{return -m_ptr->Value();}
    static CExpr* New(CExpr* p){return p ? new CNega(p) : 0;}
};

class CNum : public CExpr {
    double  m_val;
public:
    CNum(double d) : m_val(d) {}
    virtual double Value()const{return m_val;}
};

class CVar : public CExpr {
    string  m_name;
public:
    CVar(const string& s) : m_name(s) {}
    virtual double Value()const{return CExpr::s_val[m_name];}
};

class CSet : public CExpr {
    string  m_name;
    CExpr*  m_ptr;
    CSet(const string& s,CExpr* p) : m_name(s), m_ptr(p) {}
public:
    ~CSet(){delete m_ptr;}
    virtual double Value()const
        {return CExpr::s_val[m_name] = m_ptr->Value();}
    static CExpr* New(string s,CExpr* p){return p ? new CSet(s,p) : 0;}
};

class CFunc : public CExpr {
    string  m_name;
    CExpr*  m_ptr;
    CFunc(const string& s,CExpr* p) : m_name(s), m_ptr(p) {}
public:
    ~CFunc(){delete m_ptr;}
    virtual double Value()const
        {return CExpr::s_func[m_name](m_ptr->Value());}
    static CExpr* New(string s,CExpr* p){return p ? new CFunc(s,p) : 0;}
};

map<string,double> CExpr::s_val;
map<string,CExpr::FType> CExpr::s_func;

CExpr* CBin::New(CExpr* p,CExpr* q,Type t)
{
    if (!p || !q) {
        delete p;
        delete q;
        return 0;
    }
    CExpr*  r = 0;
    switch (t) {
    case Add: r = new CAdd(p,q); break;
    case Sub: r = new CSub(p,q); break;
    case Mul: r = new CMul(p,q); break;
    case Div: r = new CDiv(p,q); break;
    }
    return r;
}

CExpr* GetExpr();
CExpr* GetPrim()
{
    CExpr*  p = 0;
    eatwhite(cin);
    char    c;
    cin.get(c);
    if (c == '-') {
        p = CNega::New(GetPrim());
    } else if (c == '(') {
        p = GetExpr();
        eatwhite(cin);
        if (cin.peek() == ')') cin.ignore();
        else {delete p; p = 0;}
    } else if (isdigit(c)) {
        cin.putback(c);
        double d;
        cin >> d;
        p = new CNum(d);
    } else if (isalpha(c)) {
        string  s(&c,1);
        while (cin.get(c) && isalpha(c)) s += c;
        if (c == ' ') {eatwhite(cin); cin.get(c);}
        if (c == '=') p = CSet::New(s,GetExpr());
        else if (c == '(') {
            p = CFunc::New(s,GetExpr());
            eatwhite(cin);
            if (cin.peek() == ')') cin.ignore();
            else {delete p; p = 0;}
        } else p = new CVar(s), cin.putback(c);
    }
    return p;
}

CExpr* GetTerm()
{
    CExpr*  p = GetPrim();
    while (p) {
        eatwhite(cin);
        int     c = cin.peek();
        if (c != '*' && c != '/') break;
        cin.ignore();
        p = CBin::New(p,GetPrim(),c == '*' ? CBin::Mul : CBin::Div);
    }
    return p;
}

CExpr* GetExpr()
{
    CExpr*  p = GetTerm();
    while (p) {
        eatwhite(cin);
        int     c = cin.peek();
        if (c != '+' && c != '-') break;
        cin.ignore();
        p = CBin::New(p,GetTerm(),c == '+' ? CBin::Add : CBin::Sub);
    }
    return p;
}

int main()
{
    CExpr::s_val["pi"] = 3.1415926535897932385;
    CExpr::s_val["e"] = 2.7182818284590452354;
    CExpr::s_func["sqrt"] = sqrt;
    CExpr::s_func["log"] = log;
    CExpr::s_func["sin"] = sin;
    CExpr::s_func["cos"] = cos;
    while (cin) {
        CExpr*  p =  GetExpr();
        if (p && cin.peek() == '\n') {
            cin.ignore();
            double d = p->Value();
            cout << d << endl;
            delete p;
            continue;
        }
        cout << "Error before [";
        char    c;
        while (cin.get(c)) {
            if (c == '\n') break;
            cout << c;
        }
        cout << "]\n";
    }
}

6.19

「エラーの行」の意味が良く分からないが、6.18 では、一応エラーの場所が判るようにしてある。

6.20

6.18参照のこと。

6.21

ちょっと難しい。パス。

6.22

チャレンジの解答を参照のこと。

6.23

コーディングスタイルを参照のこと。
前章目次次章