C/C++」タグアーカイブ

Stack area

最近スタック領域とかエラそうなことを書いているけど。
よく考えたら実は何故スタック領域という名前なのかを知らない。
スタックってデータを後入れ先出しすることだよね。

#include <stdio.h>
#include <stdlib.h>

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

    /* 自動変数はスタック領域 */
    int   i;        /* 4 byte 確保 */
    char  str1[6];  /* 6 byte 確保 */
    char* str2;     /* 8 byte (64bit の場合)確保 */

    /* NULL文字'\0'が入るので 5 byte 以下 */
    sprintf(str1, "%s", "Ninja");

    /* 256 byte をヒープ領域に確保 */
    str2 = malloc(256);
    sprintf(str2, "%s", "Kawasaki");

    for (i=0; i<2; i++)
        printf("%s %s\n", str2, str1);

    /* ヒープ領域確保分は解放する(確保を手放す)必要あり */
    free(str2);

    /* 解放されても別スレッドに上書きされるまでは残っている */
    printf("%s\n", str2);

    return 0;
}

ということさえ解っていれば別に意味は知らずとも…
ではイカンよな、もう少し理解しなきゃ。

学校では教えてくれないこと | 技術コラム集(組込みの門) | ユークエスト

そういう意味だったのか、宣言順に積み上がっていくのね。
ただ x86_64 だと 8 バイトでは上手くいかなかったので 16 に。

#include <stdio.h>
#include <string.h>

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

    char a[16] = "元の文字列", b[16];

    printf("before a[]=<%s>\n", a);
    strcpy(b, "0123456789abcdef破壊");
    /*
     * ちなみにコチラだと Segmentation fault
     * sprintf("%s\n", b, "0123456789abcdef破壊");
    **/
    printf("after  a[]=<%s>\n", a);

    return 0;
}

buffer_over_run

なるほど、これは完全に後入れ先出しだ。

関数内の自動変数もスタックに入るから確かにコレが一番効率いいな。
スタックってそういう処理がしたい場合に利用するのか、うんうん。
それも解って得した気分。

しかしこういう gcc で解説してくれるサイトなんですが。
皆 stdio.h だけインクルードしているけど、筆者の環境では

strcpy を使う時は string.h
malloc を使う時には stdlib.h

と関数宣言されているヘッダを直インクルードしないと警告になる。
ビルドはできるけどね、警告が出るのって嫌じゃないですか。

GIOChannel

glib での読み書きは g_open ではなく GIOChannel で行うらしい。
もう情報が山ほどありすぎてワケワカだよ、独学の限界を感じる。
まだよく解っていないけどとりあえずテキストで試してみた。

#include <glib.h>

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

    GIOChannel* channel;
    GError*     error = NULL;
    gchar*      str;
    gsize       length;
#if !GLIB_CHECK_VERSION (2,35,0)
    g_type_init ();
#endif
    channel = g_io_channel_new_file("watch.txt", "r", &error);
    if (error)
    {
        g_warning (error->message);
        g_error_free (error);
        return 1;
    }
    g_io_channel_read_to_end(channel, &str, &length, &error);
    if (error)
    {
        g_warning (error->message);
        g_error_free (error);
        g_io_channel_unref(channel);
        return 1;
    }
    g_printf(str);

    g_free(str);
    g_io_channel_unref(channel);
    return 0;
}

iochannel

こういうことでいいのかな?
char 配列ならバイト単位だからバイナリにも応用できるな。

んで、いつものように vala のサンプルコードを見てみる。
GLib.IOChannel ? glib-2.0
g_io_add_watch でハンドラを用意してメインループを回している。
このコードだとこうするメリットが特に見当たらないんですけど。

GMainLoop, GMainContext和GSource学?笔? – ?匠Smith先生的?? – 博客?道 – CSDN.NET

こんなこともできるのか、端末からの読み書きに GMainLoop とは。
つまりメインループを回すことに意義があるってことかな。

ただバックスラッシュ(\)がスラッシュ{/}になっているのは何故?
そこを書き換えて g_type_init を消せば普通にビルドできます。
このコードを参考に色々弄ってみると面白いかも。

中国にも glib を使っている人がいるんだな、つか日本人は…

g_fopen = int

#include <stdio.h>
#include <glib.h>
 
int
main (int argc, char *argv[]) {

    FILE*  f;

    /* OK */
    f = fopen("suzuki.txt", "w"); //return pointer
    fprintf(f, "SUZUKI %s", "Hayabusa");
    fclose(f);

    /* Error (x86_64) */
    f = g_fopen("kawasaki.txt", "w"); // return int
    g_fprintf(f, "KAWASAKI %s", "Ninja");
    g_close(f, NULL);

    /* No Error (x86_64)
    gint n = g_fopen("kawasaki.txt", "w"); // return int
    g_close(n, NULL); */

    return 0;
}

g_fopen

おいおい、どういうことだよ。
devhelp をよく見ると「コレは Windows の表記だ」とあるが。
fopen にマッピングではなく stat に合わせているのか?

glibc は glib のサブセットだと思いこんでいたけど違うんだな。
つまり Linux では FILE* は使わないほうがいいということみたい。
x86(32bit) だと気が付かないで混乱するかも。

glib – C言語で、UTF-8 の文字列から Unicode のコードポイントを取得するやりかた – Qiita

g_utf8_to_ucs4_fast って fast だから速いのかな?
GLib ってまだまだ知らないことが沢山あるな。

ただコードポイントを取得なら Python3 のほうが簡単そう。
単文字を ord() すれば UCS-4 のまま値が取れそうだけど、試すか。

文字コード考え方から理解するUnicodeとUTF-8の違い | ギークを目指して
「ほげふが」が [U+307B] [U+3052] [U+3075] [U+304C]
になれば OK ということね。

#!/usr/bin/env python3

s = "ほげふが"
for a in s:
    print("[U+{0:04X}] ".format(ord(a)), end=" ")

python_ord

思ったとおり。
ワイド文字が UTF-16 になる Windows 版じゃ変換が必要だけど。
ごめん Windows Python3 でも IronPython でもできるわ

win_code_point

ipy_code_point

Linux は GLib が簡単に使えて Python も実用的。
やはりプログラミングするなら Linux ですよ。

Dynamic array Length

GLib 関数で得た gchar** つまり文字列の配列の要素数。
ってよく考えたら Vala は foreach できるじゃないか。

/* valac -C test.vala */

class Test {
    public static int main(string[] args) {
        var l = Environment.list_variables();
        foreach (var s in l) {
            stdout.printf("%s\n", s);
        }
        return 0;
    }
}

g_get_environ のバインドが何故か無いので g_getenv バインドで。
コレがビルドできるということは Vala は要素数を得ているということ。
ということで毎度のように -C オプションしてチェック。

gint 
get_array_length (gchar** array) {
	gint length = 0;
	if (array)
	    while (array[length])
		    length++;
	return length;
}

これだけ?
配列がメモリ上でどうなっているか知っていれば確かに納得だけーが。
(char**) つかヒープ領域つか動的配列専用になってしまうな。

(char[ ][ ]) のスタック変数には絶対に使ってはいけない。
コッチはゴロゴロ見付かる方法で、と使い分けてください。
(char**) (char[ ][ ]) の両対応ははたして可能なのか?

とにかくテストコードを書いて確認。

#include <glib.h>

gint 
get_array_length (gchar** array) {
	gint length = 0;
	if (array)
	    while (array[length])
		    length++;
	return length;
}

gchar**
create_array(void) {
    gchar** arg;
    arg = (gchar**)g_malloc0(sizeof(gchar*) * 4);
    arg[0] = g_strdup("YAMAHA");
    arg[1] = g_strdup("HONDA");
    arg[2] = g_strdup("KAWASAKI");
    arg[3] = g_strdup("SUZUKI");
    return arg;
}

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

    gchar** array;
    gint i, count;

    array = create_array();
    count = get_array_length(array);
    g_printf("count=%d\n", count);
    for (i=0; i<count; i++) {
        g_printf("%s\n", array[i]);
    }
    g_strfreev(array);

    return 0;
}

dynamic_array_length

上記は g_malloc0 でゼロ詰めしてセグメントエラーは一応回避している。
でも実際にアプリに使うとなると少し不安な感じ。

arg[2] = NULL;

等には完全に無力なわけで。
でもそれは配列を作る側の問題。

確実にいえるのは GLib 関数の戻り値に使うかぎりはコレで問題ない。
自前動的配列に使う場合はチョッピリ注意しよう。

g_strjoinv

ヒープ上に配列を作ってそのポインタを sizeof() してもポインタサイズしか得られない。
だとすると char** が戻って来るこんな関数の場合はどうなるんだ?

#include <glib.h>

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

    gchar** env_array;
    int i;
    long count;

    env_array = g_get_environ();
    count = sizeof(env_array) / sizeof(env_array[0]);
    g_printf("count = %ld\n", count); //=> count = 1
    i = 0;
    for (i; i<count; i++)
        g_printf("%s\n", env_array[i]);
    g_strfreev(env_array);

    return 0;
}

だよね、両方 8byte になるから 1 という結果に。
手段は無いかな、検索検索。
動的にメモリを確保した配列の要素数を調べるには 【OKWave】
自作配列なら自分で管理すればいいけど関数で戻って来るのはお手上げか。

Python なら list で得られるから単純にループでイケるのだが。

python_env

まてよ、上記のように全部出力する場合なら Python の

"\n".join(env_arraya)

みたくできれば問題なくね?

#include <glib.h>

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

    gchar** env_array;
    gchar* s;

    env_array = g_get_environ();
    s = g_strjoinv("\n", env_array);
    g_printf(s);
    g_free(s);
    g_strfreev(env_array);

    return 0;
}

strjoinv

こんなにアッサリ。
Python の join をバカにしている人、GLib ならむしろ自然だぞ。
破棄は g_strfreev に全部おまかせできるようです。

単純に全部くっつけるだけならコレでいいけど加工になると…
GList や GArray に変換は自力でやるしかなさそう。

GLib を使っても C 言語で文字列を扱うのは面倒臭いのは変わらない。
楽したけりゃ Python や Vala を、という結論で。