C はじめました - hello_world.c

去年の暮れから C に興味を持ち出した。まぁ、正確には Apche モジュールなんだけど。
残念ながらパンダ本は未だに手に入ってないわけだけど、気休めに買った gcc と K&R の本をそこそこ読んだので実際に C を書いてみようかなぁと。

何よりもまず、.emacs ですよね。

;; cc-mode
(require 'cc-mode)
;; Kernighan & Ritchie style
(setq c-default-style "k&r")

(add-hook 'c-mode-common-hook
            '(lambda ()
             (progn
               (c-toggle-hungry-state 1)
               (define-key c-mode-base-map "\C-m" 'newline-and-indent)
               (setq c-basic-offset 4 indent-tabs-mode nil))))

hello_world.c

#include <stdio.h>

int main(int argc, char *argv[]) {
    printf("Hello World!\n");
    return 0;
}

gcc hello_world.c -o hello_world でコンパイル。実行。

[travail@mina]~/c% gcc hello_world.c -o hello_world
[travail@mina]~/c% ./hello_world
Hello World!

LL な Perl でしかまともなプログラムを書いたことのない僕にしては、この「コンパイル」という作業にちょっと感動します。
で、Hello World も済ませたし、次に何書こうか考えたものの特に何もないのでとりあえず DB に接続してみようかなぁと。
リモートホストの DB(saycheese) につないで、とあるテーブル(thumbnail)から10件 SELECT して、とあるカラムのデータ(small サイズのサムネイル)をファイル(ファイル名は id)に書き込んで終了。
何となく getopt_long を使いたかったので、mysql のユーザ、パスワード、ホストをオプションで取る。
というプログラム。のつもり。

#include <stdlib.h> /* for exit(), free() */
#include <stdio.h>  /* for printf(), sprintf(), fwrite() */
#include <string.h> /* for strdup() */
#include <getopt.h> /* for getopt_long() */
#include <mysql/mysql.h>

#define DB "saycheese"
#define PORT 3306
#define SOCKET "/var/lib/mysql/mysql.sock"
#define OPT 0

char *host;
char *user;
char *password;

/* prototypes */
void options_parse(int argc, char *argv[]);

int main(int argc, char *argv[]) {
    options_parse(argc, argv);

    int count = 0;
    MYSQL *mysql;
    MYSQL_RES *res;
    MYSQL_ROW row;

    mysql = mysql_init(NULL);
    if (!mysql_real_connect(mysql, host, user, password, DB, PORT, SOCKET, OPT)) {
        printf("ERROR %d: %s)\n", mysql_errno(mysql), mysql_error(mysql));
        exit(-1);
    }

    char *sql =
        "SELECT id, created_on, modified_on, url, extension, small FROM thumbnail LIMIT 10";
    mysql_query(mysql, sql);
    res = mysql_store_result(mysql);

    for (count = mysql_num_rows(res); count > 0; count--) {
        /* for fwrite() secend argument 'length' */
        row = mysql_fetch_row(res);
        unsigned int length = ((unsigned long *)mysql_fetch_lengths(res))[5];

        /* open file and write it down */
        FILE *fp;
        char filename[256];
        sprintf(filename, "%s.%s", row[0], row[4]);
        if ((fp = fopen(filename, "w+")) == NULL) {
            printf("Can't open %s\n", filename);
        } else {
            fwrite(row[5], length, 1, fp);
            fclose(fp);
        }
    }

    mysql_close(mysql);
    mysql_free_result(res);
    /* for strdup(host|user|password) in options_parse() */
    if (host) free(host);
    if (user) free(user);
    if (password) free(password);

    return 0;
}

void options_parse(int argc, char *argv[]) {
    static struct option longopts[] = {
        {"host", required_argument, NULL, 'h'},
        {"user", required_argument, NULL, 'u'},
        {"password", required_argument, NULL, 'p'},
        {0, 0, 0, 0}
    };

    int opt;
    while (1) {
        opt = getopt_long(argc, argv, "h:u:p:", longopts, NULL);
        if (opt == -1) break;

        switch (opt) {
        case 'h':
            host = strdup(optarg);
            break;
        case 'u':
            user = strdup(optarg);
            break;
        case 'p':
            password = strdup(optarg);
            break;
        }
    }
}

コンパイル、実行。

[travail@mina]~/c% gcc saycheese.c -o saycheese
/tmp/ccsfFlXE.o: In function `main':
saycheese.c:(.text+0x35): undefined reference to `mysql_init'
saycheese.c:(.text+0x80): undefined reference to `mysql_real_connect'
saycheese.c:(.text+0x8f): undefined reference to `mysql_error'
saycheese.c:(.text+0x9c): undefined reference to `mysql_errno'
saycheese.c:(.text+0xd5): undefined reference to `mysql_query'
saycheese.c:(.text+0xe0): undefined reference to `mysql_store_result'
saycheese.c:(.text+0xee): undefined reference to `mysql_num_rows'
saycheese.c:(.text+0x101): undefined reference to `mysql_fetch_row'
saycheese.c:(.text+0x10f): undefined reference to `mysql_fetch_lengths'
saycheese.c:(.text+0x1c3): undefined reference to `mysql_close'
saycheese.c:(.text+0x1ce): undefined reference to `mysql_free_result'
collect2: ld はステータス 1 で終了しました

「終了しました」とか言われてもよくわからないので「"undefined reference to" mysql_init」でグーグルさんに訊ねてみる。

4.1 向けのリファレンスだけど、「リンク時に libmysqlclient.so へのパスを教えてあげてね」とのこと。
どうもこれっぽいので、改めてコンパイル、実行。

[travail@mina]~/c% gcc saycheese.c -o saycheese -L/usr/lib/mysql -lmysqlclient
[travail@mina]~/c% ./saycheese -u travail -p ******* -h 192.168.1.1
[travail@mina]~/c% ls
10.jpg  13.jpg  16.jpg  9.jpg          libmemcached*   saycheese.c
11.jpg  14.jpg  6.jpg   hello_world*   libmemcached.c
12.jpg  15.jpg  8.jpg   hello_world.c  saycheese*
[travail@mina]~/c% ./saycheese -u travail -p *******
ERROR 1049: Unknown database 'saycheese')
[travail@mina]~/c% ./saycheese -u travail
ERROR 1045: Access denied for user 'travail'@'localhost' (using password: NO))
[travail@mina]~/c% ./saycheese
ERROR 1045: Access denied for user 'travail'@'localhost' (using password: NO))

できたっぽい。
ちゃんと mysql のエラーも取れてるみたい。
ちょっと気になるのは -u でユーザを指定しなくても travail で DB につなぎに行っているところ。
mysql_real_connect の引数で user が NULL はまたは "" であった場合は現在のユーザを想定するって。
ちなみに、host が NULL もしくは、"" だったら localhost にするって。
詳しくは Web で。

Perl で言う青本みたいなリファレンスってないんですかね?
「あぁ、こんな関数(マクロ)があるのか」なんて思って使ってみようとしてもヘッダを include してないからエラーが出たり。
それをいちいちグーグルさんに訊ねるのも煩わしいので。
今だけですかね?そのうち覚えますかね?
あと、ソースで「ここ変」とか「その書き方冗長」とかありますかね?

Comments:2

shot 2008年2月25日 15:21

wxWidgetsいいよ!wxWidgetsサイコー!

さわ 2008年2月25日 23:14

> shot さん
何?GUI?

Comment Form

Trackbacks:0

TrackBack URL for this entry
http://hibinokoto.jp/mt/mt-tb.cgi/252
Listed below are links to weblogs that reference
C はじめました - hello_world.c from 日々のこと

Home > 仕事のこと > C はじめました - hello_world.c

Search
Feeds

Return to page top