@blog.justoneplanet.info

日々勉強

iOSでOpenSSLを使って大きな数値計算をする

桁あふれしそうな数の計算をする。

■OpenSSLをiOS用にビルド

git clone https://github.com/x2on/OpenSSL-for-iPhone.git
cd OpenSSL-for-iPhone/
./build-libssl.sh

ビルドされたライブラリとヘッダーファイルをプロジェクトに追加する。

mkdir ../MyProject/Library/OpenSSL
cp -r include/ ../MyProject/Library/OpenSSL/include
cp -r lib/ ../MyProject/Library/OpenSSL/lib

Build Settings

Header Search Path

以下を追加する。

$(SRCROOT)/MyProject/Library/OpenSSL/include
Library Search Path

以下を追加する。

"$(SRCROOT)/MyProject/Library/OpenSSL/lib"

Build Phases

Link Binary With Libraries

libcrypto.aとlibssl.aを追加する。

■実装

#import <openssl/bn.h>

unsigned int rand = arc4random() % 100000000;
BIGNUM *random = BN_new();BN_set_word(random, rand);
BIGNUM *base = BN_new();BN_set_word(base, BASE);
BIGNUM *prime = BN_new();BN_set_word(prime, PRIME);
BIGNUM *remainder = BN_new();
BN_CTX *ctx;
ctx = BN_CTX_new();
BN_mod_exp(remainder, base, random, prime, ctx);
BN_CTX_free(ctx);

iOSでPythonが動けばいいのに。

参考

C言語のプリプロセッサ

プリプロセッサによりある種の言語仕様が与えられる。

■ファイルのインクルード

以下のようにすると、自身のファイルの場所から探索が始まる。

#include "filename"

以下のようにすると、コンパイラの規則に従った探索が行われる。stdio.hなどは以下の方法でインクルードする。

#include <filename>

■マクロ

#define NAME VALUE

NAMEとVALUEは任意であり以下のようにすることもできる。

#define forever for(;;)

以下のようにすることで使用できる。

int main (int argc, const char * argv[])
{
    forever
    printf("hogehoge\n");
}

永久にhogehogeが表示される。また、以下のように関数を定義することもできる

#define max(a, b) (a > b)? (a) : (b)

int main (int argc, const char * argv[])
{
    int d = max(1, 2);
    printf("こんにちは!%d円よこしてください\n", d);
}

内部では以下のようにインライン展開されている。

x = ((p + q) > (r + s))? (p + q) : (r + s);

以下のような場合は注意する必要がある。

#define sq(x) x * x

int main (int argc, const char * argv[])
{
    int z = 3;
    sq(z + 1);// 7 = z + 1 * z + 1 = 3 + 1 * 3 + 1
    sq((z + 1));// 16
}

以下のようにすることで正しい結果が期待できる。

#define sq(x) (x) * (x)

■未定義

以下のようにして未定義にすることができる。

#undef hello

void hello() {}

■条件付き取り込み

以下のようにすることで条件に応じた処理をすることができる。

#if !defined(INCLUDED_MAIN)
define INCLUDED_MAIN

int hoge();

#endif

インクルードガードとかでも使う。

ポインタを使ってみる

以下のように書いてみる。

int x = 1;
int *ip;
ip = &x;
printf("%p", ip);// 0xbfffef28

ipにはメモリのアドレスが入ってることが分かる。アドレスが指している値を読むには以下のようにする。

int x = 1;
int *ip;
ip = &x;
printf("%d", *ip);// 1

*ipを書き換えたらxの値はどうなるのか?

int x = 1;
int *ip;
ip = &x;
printf("%d", *ip);// 1
(*ip)++;
printf("%d", x);// 2

まぁ、当然書き換わる。

アドレスの操作

*が付いている場合はアドレスの操作になる。

int x = 1;
int *a = &x;

値の操作

以下のようにすると値を操作できる。

int x = 1;
int *a = &x;
a = 2;

■ヌルポインタ

アドレスを代入していないことを示すためにヌルポインタが存在する。

int main(int argc, char *argv[])
{
    int *a = NULL;
    int x = 2;
    a = &x;
    printf("%d", *a);// 2
}

以下のようにも記述できる。

int main(int argc, char *argv[])
{
    int *a = NULL;
    int x = 2;
    a = &x;
    printf("%d", *a);// 2
}

当然だけど全く関係ないアドレスにアクセスすればバグになる。

■関数の引数としてのポインタ

引数として与えられた変数の値を入れ替えるには以下のようにする。

swap(&x, &y);

cの関数において引数は全て値渡しとなるためアドレスを渡す必要があり、以下のようにすることで入れ替えすることができる。

void swap(int *x, int *y)
{
    int temp = *x;
    *x = *y;
    *y = temp;
}

■ポインタと配列

以下のように記述してみる。

int x[] = {1, 2, 3, 4, 5};
int *px;
px = &x[0];
printf("%d", *px);// 1
printf("%d", *(px + 1));// 2
printf("%d", *(px + 2));// 3
printf("%d", *(px + 3));// 4
printf("%d", *(px + 4));// 5

pxは配列xの0番目を参照し、+1ごとに配列の右を参照する。低レイヤーな言語を触るとメモリが見えるのが面白いよね。

ちなみに(やってはいけないが)-1すると以下のようになる。

printf("%p", (px - 1));//0xbfffef14
printf("%d", *(px - 1));//-1073746152

■文字ポインタと関数

以下のような文字定数は文字の配列でもある。

"This is a pen."

記憶領域においては末端文字\0が配列の最期にあるため1文字大きい。

char *str = "This is a pen.";

Cでは文字列全体を一つの単位として処理するような演算子は提供されていない。

ちなみ以下の記述において前者は文字列を変更することができるがアドレスが変わることはない。後者はアドレスを変える事ができるが文字列を変えることはできない。

char strA[] = "This is a pen.";
char *strB = "This is a pen.";

ちなみに以下のようにして上述の文字列を出力することができる。

char strA[] = "This is a pen.";
for(int i = 0; i < sizeof(strA) / sizeof(strA[0]); i++){
    printf("%c", strA[i]);
}
char *strB = "This is a pen.";
while(*strB != '\0'){
    printf("%c", *strB);
    strB++;
}

strcpy

配列
void strcpy(char *s, char *t)
{
    int i = 0;
    while((s[i] = t[i]) != '\0'){
        i++;
    }
}
ポインタ
void strcpy(char *s, char *t)
{
    while((*s = *t) != '\0'){
        s++;
        t++;
    }
}

ふむふむ。なるほど。

strcmp

配列
int strcmp(char *s, char *t)
{
    for(int i = 0; s[i] == t[i]; i++){
        if(s[i] == '\0'){
            return 0;
        }
    }
    return s[i] - t[i];
}
ポインタ
int strcmp(char *s, char *t)
{
    for(; *s == *t; s++, t++){
        if(*s == '\0'){
            return 0;
        }
    }
    return *s - *t;
}

ふむふむ。なるほど。

構造体を使ってみる

構造体は操作しやすくするために一つの名前でまとめられた事なった型の変数の集まりである。

■定義

struct point {
    int x;
    int y;
};

■宣言

以下のようにして使用する。

int main(int argc, char *argv[])
{
    struct point a;
    a.x = 10;
}

以下のように値を設定することもできる。

int main(int argc, char *argv[])
{
    struct point a = {10, 20};
}

同時に定義と宣言をする

以下のようにして定義と宣言を同時に行うことができる。

int main(int argc, char *argv[])
{
    struct {
        int x;
        int y;
    }a, b, c;
    
    a.x = 10;
    a.y = 10;
    b.x = 20;
    b.y = 20;
    c.x = 30;
    c.y = 30;
}

関数の戻り値としての構造体

以下のような構造体を定義する。

struct point {
    int x;
    int y;
};

関数の戻り値として上述の構造体を返すには以下のようにする。

struct point getPoint(int x, int y)
{
    struct point p = {x, y};
    return p;
}

以下のようにして関数getPointを使用する。

int main(int argc, char *argv[])
{
    getPoint(10, 20);
}

structは型のようなもんだし、クラスのような感じでさえある。

■応用

少し応用して以下のようにしてみる。

struct point {
    int x;
    int y;
};
struct triangle {
    struct point p1;
    struct point p2;
    struct point p3;
};

下のように各頂点を定義する。

int main(int argc, char *argv[])
{
    struct triangle a;
    a.p1.x = 5;
    a.p1.y = 1;
    a.p2.x = 7;
    a.p2.y = 6;
    a.p3.x = 9;
    a.p3.y = 1;
}

■ポインタ

以下のように変数を宣言することでstruct point型へのポインタを表現できる。

struct point **pp;

以下のようにしてみる。

struct point a, *pp;
a.x = 2;
a.y = 4;
pp = &a;
printf("%d, %d", (*pp).x, (*pp).y);// 2, 4

これはよく用いられる一方で演算子の優先度のためにカッコで括る必要がある。手間なので同義の以下の表現が使用できる。

printf("%d, %d", pp->x, pp->y);

XCodeでC言語を使って遊ぶ

Mac OS X ApplicationからCommand Line Toolを選択するととっつきやすい。

#include <stdio.h>

int main (int argc, const char * argv[])
{
    printf("こんにちは!\n%d円よこしてください", );
    return 0;
}
#include <stdio.h>

int main (int argc, const char * argv[])
{
    int cost = 100;
    printf("こんにちは!%d円よこしてください\n", cost);
    cost = 10000;
    printf("こんにちは!%d円よこしてください\n", cost);
    return 0;
}

■インクルードガード

main.c

#include <stdio.h>
#include "hoge.h"

int main (int argc, const char * argv[])
{
    printf("こんにちは!%d円よこしてください\n", hoge());
}

hoge.h

#ifndef INCLUDED_MAIN
#define INCLUDED_MAIN

int hoge();

#endif

hoge.c

#include "hoge.h"

int hoge()
{
    return 100;
}

Cで文字列を使ってみる

まぁ、簡単にまとめておく。

■文字変数

Cには文字列変数はなく文字変数のみが存在する。他の言語よりも扱いにくいけど低レイヤーを感じられる。

int main(int argc, char *argv[])
{
    char c = 'a';
    printf("%c", c);// a
}

当然、日本語は扱えない。低レイヤー万歳!

int main(int argc, char *argv[])
{
    char c = 'a';
    c++;
    printf("%c", c); // b
}

上述のようにするとASCIIでaの次にあるbが表示される。

■文字列

文字変数を並べれば文字列になるという考え方で配列によって表現される。

int main(int argc, char *argv[])
{
char str[] = "this is a pen.";
printf("%s", str);
}
[/sourcecode]

一応、配列なので以下のように書くこともできる。

int main(int argc, char *argv[])
{
char str[] = {‘t’, ‘h’, ‘i’, ‘s’, ‘ ‘, ‘i’, ‘s’, ‘ ‘, ‘a’, ‘ ‘, ‘p’, ‘e’, ‘n’, ‘.’, ‘\0’};
printf("%s", str);
}
[/sourcecode]

末端文字などの文化はObjective-Cにも受け継がれていたりする。

Cで関数を使ってみる

まぁ、とりあえず書いておく。

■定義

void hoge() {
    printf("%s", "hello");
}

以下のようにして引数を定義する。

void hoge(char str[]) {
    printf("%s", str);
}

まとめると以下のようになる。

戻り値型 関数名(引数の宣言) {
    処理
}

ちなみにC言語では配列を返すことができない。

また、Javaでは可能だがCでは以下のようにすることはできない。

int hoge(int a){
    return a;
}
double hoge (double a){
    return a;
}

プロトタイプ宣言

以下のように記述した場合の処理は実装次第である。

int main(int argc, char *argv[])
{
    printf("%d", hoge(6));
}
int hoge(int a){
    return a;
}

従って以下のようにプロトタイプ宣言をする。

int hoge(int a);
int main(int argc, char *argv[])
{
    printf("%d", hoge(6));
}
int hoge(int a){
    return a;
}

プロトタイプ宣言はヘッダファイルに記述すると良い。というかincludeしたファイルにはプロトタイプ宣言が記述されている。

Cのスコープ

あんまり書くこともない気がする。

int counter = 0;// グローバル

int main(int argc, char *argv[])
{
    int counter = 5;// ローカル
    printf("%d", counter);
}

ちなみにブロックスコープはちゃんと存在する。

int main(int argc, char *argv[])
{
    int counter = 5;// ローカル
    printf("%d", counter);
    if(1){
        int x = 10;
    }
    // この空間ではxは不可視
}

■static変数

関数に紐づいた静的な変数で呼び出しごとに初期化されない変数。

int count()
{
    static int counter = 0;
    return counter++;
}
int main(int argc, char *argv[])
{
    printf("%d", count());// 0
    printf("%d", count());// 1
    printf("%d", count());// 2
}

Cの配列を使ってみる

基本は以下のように型つきの入れ物を用意する。

int main(int argc, char *argv[])
{
    int ary[5];
}

以下のように初期化も同時に行える。

int main(int argc, char *argv[])
{
    int ary[5] = {1, 2, 3, 4, 5};
}

上述の場合、要素数は以下のように省略して良い。

int main(int argc, char *argv[])
{
    int ary[] = {1, 2, 3, 4, 5};
}

配列の要素数は以下のようにして求めることができる。

int main(int argc, char *argv[])
{
    int ary[5] = {1, 2, 3, 4, 5};
    printf("%lu", sizeof(ary) / sizeof(ary[0]));// 5 = 全体のサイズ / 1要素のサイズ
}

煩雑ですな。ループは普通にfor文とか使う。

makeをつかってみる

■GCC

makeの前にGCCでビルドする方法を試す。

ソースファイル

main.c
main(){
  hello();
}
hello.c
#include <stdio.h>
hello(){
  printf("Hello, World\n");
}

#include <stdio.h>が欠けていると以下のエラーが発生する。

incompatible implicit declaration of built-in function

ビルド

以下のコマンドでビルドする。

gcc -c main.c
gcc -c hello.c
gcc main.o hello.o -o hello

実行

以下のコマンドで実行してみる。

./hello
# Hello, World

■make

makeの前に前述でできたファイルを削除しておく。

rm hello main.o hello.o

煩雑なのでmakeを使う方法にする。まずはMakefileを生成する。

vi Makefile

以下のように記述する。

all: hello
hello: main.o hello.o

以下のようにしてmakeする。ターゲット名を省略した場合は最初のターゲットが実行される。慣習的にallを使用する。

make

同様に以下のコマンドで実行できる。

./hello
// Hello, World

make clean

cleanとはターゲットのことであり以下のようにして動作を定義できる。

clean:
	rm -f *.o hello

以下のコマンドで確認する。

make clean

ファイルが消えていることが確認できる。

コンパイラ

Makefileの先頭部分で以下のように記述することによってコンパイラを変更することができる。

GCC
CC = gcc
インテルコンパイラ
CC = icc