[C/C++] Cプログラミングの落とし穴 4.5 外部の型のチェック P68

『Cプログラミングの落とし穴』(A.コーニグ, 1990)
[M1 Mac, Big Sur 11.6.8, clang 13.0.0, NO IDE]

3.3 仮引数としての配列宣言 P37におけるP68への参照を確認しました。

externにより他のファイルで変数を使う場合はポインタと配列をきちんと区別すべきという内容です。同じファイル内のポインタや配列への自動変換のような補助はありません。

下記例ではextern helloポインタになっているため、”配列helloへのポインタ”格納アドレスへアクセスしようとしますが、そのようなものは存在しないのでBus errorになります。

このようなミスは初級者を脱したばかりの中級者がやってしまいそうな感じがします。

#include <cppstd.h>
#include <test2.h>

char hello[] = "hello";

int main() {

    show();

    return 0;
}
#include <cppstd.h>

// extern char hello[]; // 正しくはこちら
extern char *hello;

void show() {

    printf("%s\n",hello);

}
Bus error: 10

//正常であれば
hello

[C/C++] Cプログラミングの落とし穴 3.2 ポインタ P34 malloc

『Cプログラミングの落とし穴』(A.コーニグ, 1990)
[M1 Mac, Big Sur 11.6.8, clang 13.0.0, NO IDE]

本の内容に従ってメモリ確保のコードを書いていたら、malloc関数で確保されるメモリ領域が引数で指定した通りにならないことが判明しました。

詳しく調べてはいませんが、引数が0以上であれば最低16バイトは確保されるようです。なおこれはclangでの検証結果であってgccなどではどうなっているのか不明です。

教科書等では収納するバイト数にヌル文字の1バイト分を加えた数を引数としましょうと教えているはずですが、実際は小さい数であればあまり意味がないということになります。

#include <cppstd.h>
#include <malloc/malloc.h>

int main() {
    char* s = "abc";
    char* t = "def";
    char* r1;
    char* r2;
    char* r3;

    cout << "strlen(s) " << to_string(strlen(s)) << endl;
    cout << "strlen(t) " << to_string(strlen(t)) << endl;

    r1 = (char*)malloc(strlen(s) + strlen(t) +1);
    r2 = (char*)malloc(0);
    r3 = (char*)malloc(10000);

    int size1 = malloc_size(r1); 
    int size2 = malloc_size(r2); 
    int size3 = malloc_size(r3);

    cout << "r1で確保されたサイズ " << to_string(size1) << endl;
    cout << "r2で確保されたサイズ " << to_string(size2) << endl;
    cout << "r3で確保されたサイズ " << to_string(size3) << endl;

    strcat(r1, s);
    strcat(r1, t);

    cout << "r1 " << r1 << endl;

    free(r1);
    free(r2);
    free(r3);

    return 0;
}
--------------------------------------------------
出力
--------------------------------------------------
strlen(s) 3
strlen(t) 3
r1で確保されたサイズ 16
r2で確保されたサイズ 16
r3で確保されたサイズ 10240
r1 abcdef

[C/C++] Cプログラミングの落とし穴 1.5 文字列と文字定数 P10

『Cプログラミングの落とし穴』(A.コーニグ, 1990)
[M1 Mac, Big Sur 11.6.8, clang 13.0.0, NO IDE]

この本ではC言語の特異性が論じられていてとても興味深いのですが、1.5もなかなかのインパクトでした。

単一引用符で1文字を囲むとASCIIコードになり、二重引用符で囲むと文字列へのポインタになるというのは目からウロコでした。string型は*が付いていなくてもポインタとしての性質を持つということになりますね。

今使っているclangコンパイラでは単一引用符で1文字を囲むとcharになり、複数文字を囲むとintつまりASCIIコードになります。

普段文字列を囲むときは二重引用符しか使いませんが、あえて単一引用符を使うと以下のようなコンパイルエラーになります。

int main() {
    string str1 = 'a';
    string str2 = 'abc';

    cout << "str1 " << str1 << endl;
    cout << "str2 " << str2 << endl;

    printf('a\n');

    return 0;
}
--------------------------------------------------
コンパイルエラー内容
--------------------------------------------------
src/test.cpp:4:12: error: no viable conversion from 'char' to 'std::string' (aka 'basic_string<char>')
    string str1 = 'a';
           ^      ~~~
src/test.cpp:5:12: error: no viable conversion from 'int' to 'std::string' (aka 'basic_string<char>')
    string str2 = 'abc';
           ^      ~~~~~
src/test.cpp:10:5: error: no matching function for call to 'printf'
    printf('a\n');
    ^~~~~~
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/stdio.h:170:6: note: candidate function not viable: no known conversion from 'int' to 'const char *' for 1st argument
int      printf(const char * __restrict, ...) __printflike(1, 2);
         ^