戻る

2004年07月26日

ポインタ覚書

ポインタ演算

ポインタの定義を演算をテストする。



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

                /* ポインタの参照先を表す */
                printf("%d\n", *pInt);
                printf("%d\n", *(pInt + 1));

                /* ++, --,*,などの単項演算子は、右から解析される */
                /* ポインタの参照するデータに+1をする。*/
                printf("%d\n", ++*pInt);
                printf("%d\n", (*pInt)++);

                /* ポインタに+1をするので、次のポインタを表す。*/
                printf("%d\n", *pInt++);
                return 0;
}
]]>

アドレスの計算

mallocとfreeを作ってみる。

[s-okita@localhost KandR]$ cat alloc_afree.c
#include 

#define ALLOC_SIZE                      (10000)

static char allocbuf[ALLOC_SIZE];
static char *allocp = allocbuf;

char *alloc(int n)
{
                if ( allocbuf + ALLOC_SIZE - allocp >= n )
                {
                                allocp += n;
                                printf("allocate:%p", allocp -n);
                                return allocp - n;
                }
                return NULL;
}

void afree(char *p)
{
                if ( allocbuf <= p && allocp + ALLOC_SIZE > p )
                {
                                allocp = p;
                                printf("free\n");
                }
}

int strlen_(char *src)
{
                char *p = src;
                while ( *p != '\0' )
                                p++;
                return p - src;
}
 
[s-okita@localhost KandR]$ cat alloc_afree.c
#include 

#define ALLOC_SIZE                      (10000)

static char allocbuf[ALLOC_SIZE];
static char *allocp = allocbuf;

char *alloc(int n)
{
                if ( allocbuf + ALLOC_SIZE - allocp >= n )
                {
                                allocp += n;
                                printf("allocate:%p", allocp -n);
                                return allocp - n;
                }
                return NULL;
}

void afree(char *p)
{
                if ( allocbuf <= p && allocp + ALLOC_SIZE > p )
                {
                                allocp = p;
                                printf("free\n");
                }
}

int strlen_(char *src)
{
                char *p = src;
                while ( *p != '\0' )
                                p++;
                return p - src;
}


[s-okita@localhost KandR]$ cat alloc_afree_driver.c
#include 
#include 
#include "alloc_afree.h"

int main(int argc, char *argv[])
{
                char *data = NULL;

                printf("start\n");
                data = alloc(12);
                printf("alloc\n");

                if ( data == NULL )
                {
                                printf("error: no allocate\n");
                                return 1;
                }

                strcpy(data, "hello world");

                printf("%p\n", data);
                printf("%s\n", data);
                printf("%d\n", strlen_(data));
                afree(data);

                return 0;
}

コンパイル
[s-okita@localhost KandR]$ gcc -g -c alloc_afree.c
[s-okita@localhost KandR]$ gcc -g -o alloc_afree_driver alloc_afree_driver.c alloc_afree.o
 

 

文字列とポインタ

strlenを作ってみる。
[s-okita@localhost KandR]$ cat strlen.c
#include 

int strlen_(char *s)
{
                int n;
                for (n = 0; *s != '\0'; s++)
                                n++;
                return n;
}

int strlen_2(char s[])
{
                int n;
                for (n = 0; *s != '\0'; s++)
                                n++;
                return n;
}

void strcpy_(char *dest, char *src)
{
                int i = 0;
                /*
                while (src[i] != '\0')
                {
                                dest[i] = src[i];
                                i++;
                }
                dest[i] = src[i];
                */

                /* 配列で書く
                while ( (dest[i] = src[i]) != '\0')
                                i++;
                */

                /* ポインタで書く
                while ( (*dest = *src) != '\0' )
                {
                                src++;
                                dest++;
                }
                 */

                /* ポインタで書く.その2
                while ( ( *dest++ = *src++ ) != '\0')
                                ;
                while (0) {
                                printf("noprint");
                }
                while (1) {
                                printf("print\n");
                                break;
                }
                */

                while ( *dest++ = *src++ )
                                ;
}

int main(int argc, char *argv[])
{
/*              printf("%d\n", strlen_("hello"));
                printf("%d\n", strlen_2("hello"));
                */
                char dest[6];
                strcpy_(dest, "HELLO");
                printf("%s\n", dest);
                return 0;
}
 

 

strcmpを作ってみる。

[s-okita@localhost KandR]$ cat strcmp.c
#include 

/*
 * マイナスの値: srcの方が大きい値
 * 0
 * プラスの値: srcの方が小さい値
 */
int strcmp_(char dest[], char src[])
{
                int i;
                for (i = 0; dest[i] == src[i]; i++)
                {
                                if (src[i] == '\0')
                                                return 0;
                }
                return dest[i] - src[i];
}

int strcmp_p(char *dest, char *src)
{
                for (; *dest == *src; dest++, src++)
                {
                                if (*src == '\0')
                                                return 0;
                }

                return *dest - *src;
}

int main(int argc, char *argv[])
{
                char *a = "a";
                char *b = "b";

                printf("%d\n", strcmp_(a, b));
                printf("%d\n", strcmp_p(a, b));
                return 0;
}
 

stackを作ってみる

[s-okita@localhost KandR]$ cat stack.c
#include 

static int stack[10];
static int *sp = stack;

void push(int val)
{
        *sp++ = val;
}

int pop()
{
                return *--sp;
}

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

                val = 100;

                push(val);
                result = pop();

                printf("result=%d\n", result);

                return 0;
}
 

ポインタ配列

可変長のデータ(文字列)をうまく扱う。

 

 

 

ポインタと配列

シンタックスシュガーを体験してみる。

#include <stdio.h>

int main(int argc, char *argv[])
{
        int i;
        int *pArray;
        int array[] = {
                1,2,3,4,5
        };
        /* pArray = &array[0] と等価である。*/
        pArray = array;

/*
 * 配列でのアクセス
 */
        /* 通常の配列(array)アクセス*/
        for (i = 0; i < 5; i++)
        {
                printf("%d\n", array[i]);
        }

        /* 配列は、インデックスではなく、ポインタとオフセットでもかける */
        for (i = 0; i < 5; i++)
        {
                printf("%d\n", *(array + i));
        }

#include 

int main(int argc, char *argv[])
{
        int i;
        int *pArray;
        int array[] = {
                1,2,3,4,5
        };
        /* pArray = &array[0] と等価である。*/
        pArray = array;

/*
 * 配列でのアクセス
 */
        /* 通常の配列(array)アクセス*/
        for (i = 0; i < 5; i++)
        {
                printf("%d\n", array[i]);
        }

        /* 配列は、インデックスではなく、ポインタとオフセットでもかける */
        for (i = 0; i < 5; i++)
        {
                printf("%d\n", *(array + i));
        }

/*
 * ポインタでのアクセス
 */
        /* 通常のポインタでのアクセス */
        for (i = 0; i < 5; i++)
        {
                printf("%d\n", *(pArray + i));
        }

        /* pArrayがポインタであれば、添え字付きでアクセスしてもかまわない */
        for (i = 0; i < 5; i++)
        {
                printf("%d\n", pArray[i]);
        }

        /* これでもかまわない */
        for (i = 0; i < 5; i++)
        {
                printf("%d\n", i[pArray]);
        }


        return 0;
}
 

つまり、ポインタと配列は、indexもポインタ演算も行える。しかし明確な違いとして、ポインタは変数であり、配列名は変数ではない。そのため、変数にデータを代入する array = pArrayのような実行できるが構文は正しくない

 

関数の引数

char *とchar [] は等価である。

#include 

int strlen_(char *s)
{
                int n;
                for (n = 0; *s != '\0'; s++)
                                n++;
                return n;
}

int strlen_2(char s[])
{
                int n;
                for (n = 0; *s != '\0'; s++)
                                n++;
                return n;
}

int main(int argc, char *argv[])
{
                printf("%d\n", strlen_("hello"));
                printf("%d\n", strlen_2("hello"));
                return 0;
}
 

 

文字列定数

char *test = "Hello World"

"Hello World"の部分を文字列定数と呼ぶ。これは文字の配列である。また暗黙のうちにナル文字(\0)が付加されるためcharの領域は文字数+1(ナル文字分)となる。"Hello World"の場合、char12個分である。

文字列定数は、プログラムで現れた場合、先頭へのポインタとして扱われる。

char *hello = "Hello World";

char hello2[] = "Hello World";

上記は明確に異なる。helloは文字列定数へのポインタのためポインタを変更することは可能だが定数を変更することが出来ない。それに対してhello2は、メモリ領域を確保して、"Hello World"をそこに書き込むので変更が可能。

 

ポインタ配列対多次元配列

ポインタ配列のメモリ領域と多次元配列のメモリ領域を理解する。

 

ポインタ配列(ポインタのポインタ)

char *test[] = { "Jan", "Feb", "Mar", "Apr"};

char *への配列であり、char*は初期化されている。

メモリマップ

test 

| address0 | ---->  Jan\0
| address1 | ---->  Feb\0
| address2 | ---->  Mar\0
| address3 | ---->  Apr\0

test配列は、文字列定数へのポインタを保持しているだけである。つまり配列自体は、ポインタの領域しか確保しない。

 

多次元配列

char test[][4] = { "Jan", "Feb", "Mar", "Apr"};

char[4]型への配列である。

この場合、コンパイラが[1][4]分つまり1row,4column分の領域を確保する。

メモリマップ

test

|Jan\0|Feb\0|Mar\0|Apr\0|

 

char test[][10] = { "Jan", "Feb", "Mar", "Apr"};

上記のようにすると、文字列数はナル文字(\0)を含めて1columnでchar型4個分だが、コンパイラは10個分用意する。

test

|Jan\0      |Feb\0     |Mar\0      |Apr\0      |

 

関数の仮引数の書き方によっては、Warningが出る。

#include 

/* vector */
void test_func(char *v[])
{
}

void test_func2(char ndimension[][])
{
}

int main(int argc, char *argv[])
{
        char test_ary[5][3];
        test_func(test_ary);
        test_func2(test_ary);

        return 0;
}


[s-okita@localhost business]$ gcc -Wall test.c
test.c: In function `main':
test.c:15: warning: passing arg 1 of `test_func' from incompatible pointer type
[s-okita@localhost business]$ 

上記の場合、test_funcはcharポインタが格納された1次元のテーブルを定義している。ため、main関数内のtest_func(test_ary)では、test_aryが2次元配列として定義されているためWarningが発生する。

 

ARGument Count と ARGument Vector

標準規格では、argv[argc]はヌルポインタであることが要求されている。

argv[0]は、コマンド名

argv[argc-1]は、最後の引数。引数が存在しないときはargv[1-1] つまりargv[0]なのでコマンド名となる。

 

#include 
#include 

#define BUF_SIZE                                (1024)

/* result two dimension */
void getXsv2Dimention(char *src, const char separator,
                                int maxColumn, char target[][BUF_SIZE]);

/* result vector type */
void getXsv2Vector(char *src, const char separator,
                                int maxColumn, char *target[], int targetElementSize);

/* X separate value convert X separate value */
void getXsv2Xsv(char *src, const char srcSeparator,
                                char *dest,const char destSeparator);
char *getElement(char *src, int columnNumber);

/* debug */
void test_view(char *dest);
void view_vector(char **vector, int size, int elementSize);
void view_vector2(char *vector[], int size, int elementSize);
void view_vector3(char vector[][], int size, int elementSize);
void test_vector();


int main(int argc, char *argv[])
{
/*
                char *hello = "HELLO,WORLD";
                char target[BUF_SIZE];
                getXsv2Xsv(hello, ',', target, '\0');
                test_view(target);
                printf("%s", getElement(target,0));
                printf("%s", getElement(target,1));
                printf(getElement(target,2));
*/
                printf("***************************\n");

                test_vector();
                return 0;
}



void view_vector(char **vector, int size, int elementSize)
{

                printf("***view_vector\n");
                printf("%p\n", vector);
                printf("***view_vector\n");
}



void view_vector2(char *vector[], int size, int elementSize)
{

                printf("***view_vector2\n");
                printf("%p\n", vector);
                printf("%p\n", &vector[0]); // same

/*
 * char型のポインタ(文字列)がvector配列に入っているのに
 * %dで10進のint型を要求している。
 */
                // printf("%d\n", vector[0]);
                printf("vector address%p\n", &vector);
                printf("***view_vector2\n");
}


/**
 * ポインタのポインタ
 */
void view_vector3(char vector[][], int size, int elementSize)
{

                printf("***view_vector3\n");
                printf("%p\n", vector);
                printf("**p%c\n", **(vector));
                printf("**p%c\n", **(vector));
                // TODO
                // printf("%c\n", vector[0][1]);
                printf("***view_vector3\n");
}


void test_vector()
{
                char *inputMessage = "HELLO,WORLD,";
                char vector[2][BUF_SIZE];
                printf("vector start\n");
                printf("vector pointer%p\n", vector);

                memset(vector , 0x41, BUF_SIZE);
                memset(vector + 1, 0x42, BUF_SIZE);
/*
                printf("%p\n", vector);
                printf("%c\n", vector[0][0]);
                printf("%c\n", vector[0][1]);
                printf("%c\n", vector[0][2]);
                printf("%c\n", vector[1][0]);
                printf("%c\n", vector[1][1]);
                printf("%c\n", vector[1][2]);


                view_vector3(vector, 2, BUF_SIZE);
                view_vector2((char **)vector, 2, BUF_SIZE);
                view_vector((char **)vector, 2, BUF_SIZE);
*/
                getXsv2Vector(inputMessage, ',', 2, (char **)vector, BUF_SIZE);
}

int parse(char *src, const char separator)
{
                int cnt = 0;
                char *tmp = src;

                while ( *tmp != separator )
                {
                                cnt++;
                                tmp++;
                }
                return cnt;
}

/* result vector type */
void getXsv2Vector(char *src, const char separator,
                                int maxColumn, char *target[], int targetElementSize)
{
                int work = 0;
                int i;

                char *tmp = (char *)target;

                work = parse(src + work, separator);
                strncpy(tmp, src, work);
                strncpy(tmp+work, "\0", 1);
                printf("%s\n",tmp);

                tmp += targetElementSize;

                work = parse(src + work +1, separator);
                strncpy(tmp, src + 5 + 1, work);
                strncpy(tmp+work, "\0", 1);
                printf("%s\n",tmp);

                for ( i = 0; i < maxColumn; i++)
                {
                                printf("loop :%d\n", i);
                }

}



/* X separate value convert X separate value */
void getXsv2Xsv(char *src, const char srcSeparator,
                                char *dest,const char destSeparator)
{
                int len;

                len = strlen(src);


                memset(dest, 0, BUF_SIZE);
                strncpy(dest, src, len + 1);

                if ( srcSeparator == destSeparator )
                                return;

                /*for (; *dest != '\0'; dest++)*/
                while ( *dest++ != '\0' )
                {
                                if (*dest  == srcSeparator)
                                                *dest = destSeparator;
                }
}

char *getElement(char *src, int columnNumber)
{
                char *tmp = src;
                int cnt = 0;
                int i;

                if ( columnNumber == cnt )
                                return src;

                for ( i = 0; i < BUF_SIZE; )
                {
                                if ( *tmp++ == '\0' )
                                                cnt++;
                                if (columnNumber == cnt )
                                                return tmp;
                }
                return NULL;
}

void test_view(char *dest)
{
                int i;
                for ( i = 0; i < 12; i++, dest++)
                                printf("%c\n", *dest);

}
 

関数へのポインタ

関数へのポインタを書いてみる
#include 

void hello();
void invoke_hello(void (*hello)(void));

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

                invoke_hello(&hello);
                /* 関数名のみの記述は、関数のアドレスを表す
                 * これは、配列名が配列の先頭アドレスを表すのと同様.*/
                invoke_hello(hello);
                return 0;
}

void invoke_hello(void (*hello)(void))
{
        (*hello)();

}

void hello() {
                printf("hello\n");
}

 

型を指定して関数へのポインタを書いてみる。

#include 

void hello();
void invoke_hello(void (*hello)(void));

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

                /* 関数名のみの記述は、関数のアドレスを表す
                 * これは、配列名が配列の先頭アドレスを表すのと同様.*/
                invoke_hello(hello);

                /*
                 * 型を指定する方法
                 */
                invoke_hello((void (*)())hello);
                return 0;
}

void invoke_hello(void (*hello)(void))
{
        (*hello)();

}

void hello() {
                printf("hello\n");
}
 

関数名は関数のアドレスである。

型が同様であれば、以下のような条件で呼び出す関数を異なるように出来る。

#include 

void hello();
void goodbye();
void invoker(void (*p_func)(void));

int main(int argc, char *argv[])
{
                int i = 1;

                invoker((void (*)())(i ? hello : goodbye ));

                --i;

                invoker((void (*)())(i ? hello : goodbye ));

                return 0;
}

void invoker(void (*p_func)(void))
{
        (*p_func)();

}

void hello() {
                printf("hello\n");
}

void goodbye() {
                printf("goodbye\n");
}
 

関数へのポインタに引数をつける。

引数をつける場合は、関数へのポインタと、関数へのポインタへ渡す引数を、別に用意しなければならない。この例の場合、

void invoker(void (*p_func)(int i), int i)

関数は、第一引数に関数のポインタであるvoid (*p_func)(int i)を指定している。これはあくまでもp_func変数がvoid (*)(int i)型なのでこの型の部分に引数を設定することができない。つまり以下のようにすることは出来ない。

void invoker(void (*p_func)(999))

そのため、関数のポインタ「void (*p_func)(int i)」へint型の引数「(int i)の部分」を渡すために第二引数を用意する。

#include 

void hello(int i);
void goodbye(int i);
void invoker(void (*p_func)(int i), int i);

int main(int argc, char *argv[])
{
                int i = 1;

                invoker((void (*)(int))(i ? hello : goodbye ), i);

                --i;

                invoker((void (*)(int))(i ? hello : goodbye ), i);

                return 0;
}

void invoker(void (*p_func)(int i), int i)
{
        (*p_func)(i);

}

void hello(int i) {
                printf("hello:%d\n", i);
}

void goodbye(int i) {
                printf("goodbye:%d\n", i);
}