Hatena::Groupcside

Cside::StudyMemo このページをアンテナに追加 RSSフィード

メインブログに書くまでもない自分用メモを垂れ流す。日々是勉強也。

カテゴリー

2011-04-16

[]C言語:文字列とポインタ

配列で実現する文字列と、ポインタで実現する文字列がある。

char str[] = "Hello";
char *ptr  = "Hello";

printf("%s\n", str);  // 文字列リテラルを評価すると、その型はcharrへのポインタ型となり、
printf("%s\n", ptr);  // その文字列リテラルの先頭文字列へのポインタとなる。

int i;
for (i = 0; str[i]; i++) putchar(str[i]);
for (i = 0; ptr[i]; i++) putchar(ptr[i]);

配列としての文字列と、ポインタの文字列の性質の違い

str = "foo";  //=> error。配列には初期化値を代入できない
ptr = "foo";  //=> ok

ポインタをインクリメントすると、1つ後ろのオブジェクトを指すようになる

char *s = "Hello";
int len = 0;
while (*s++)
    putchar(*s);  //=> ello

printf("%s", str) と putchar(str) の違い

2011-04-09

[]続・C言語の基本

既に定義済みの変数を明示的に宣言

extern int num = 5;

引数なし・戻り値なしの関数の書き方

void func(void) {

関数の引数として渡した配列*1を、その関数内で書き換えないようにする

int func(const int[]) {

プログラム終了まで消滅しない変数を宣言(ブロックスコープであることは不変)

void sub(void){
    static int x = 0;
    printf("%d\n", x++);
}

int main(void) {
    for (int i = 0; i < 5; i++)
        sub();  //=> 0,1,2,3,4
}

型の最大値・最小値は limits.h で定義されている

#include <limits.h>
// UCHAR_MAX, UINT_MAX などなど…

数学関数を提供する math.h ヘッダ

#include <math.h>
printf("%f", sqrt(3.14));  // などなど

マクロで関数を定義

#define sqr(x)     ((x) * (x))  // intでもfloatでも使える関数
#define alert()	   (putchar('\a'))
#define putsa(str) (putchar('\a') , puts(str))  // ;でつないじゃ駄目。,で。

列挙体。使いどころが分からん…

enum Month {Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec};

int main(void) {
	printf("%d", Jan);  //=>  0
	printf("%d", May);  //=>  4
	printf("%d", Nov);  //=> 10
}

標準入力ストリームから文字を1つずつ読み取る getchar()。

(※ C言語での“文字”とは、その文字に与えられたコード(すなわち整数値)である。)

int main(void) {
	int ch;
	while (1) {
		ch = getchar();  // STDIN: '1' '2' '3' '4' '5'
		if (ch == EOF) break;
		printf("%d\n", ch);  //=>  49  50  51  52  53  10
        putchar(ch);         //=> '1' '2' '3' '4' '5'
	}
	return(0);
}

文字列

char ss[] = {'A', 'B', 'C', '\0'};
char ss[] = "ABC";  // equal

printf("%s", ss);

char str[] = "ABCD";  //=> str[4] と補完される
str[5] = "A";  //=> err

char name[40];
scanf("%s", name);  // 配列はポインタとして渡されるから&つけなくていい

文字列の長さ。for文でもいいけど。

unsigned str_length(const char str[])
{
	unsigned len = 0;
	while (str[len])  // ナル文字は偽
		len++;

    return (len);
}

大文字・小文字の変換

#include <ctype.h>  // toupper(), tolower()

文字列をコピーするstrcpy()。コピー元は文字型配列でもポインタの指す文字列リテラルでも文字列リテラルでもOK。

#include <string.h>

char str1[] = "ABCDEF";
char str2[] = "123";
char *p = "abcd";

strcpy(str1, str2);     /* 文字型配列に文字型配列をコピー */
printf("%s\n", str1);

strcpy(str1, p);        /* 文字型配列にポインタの指す文字列リテラルをコピー */
printf("%s\n", str1);

strcpy(str1, "xyz");    /* 文字型配列に文字列リテラルをコピー */
printf("%s\n", str1);

構造体

struct gstudent {
	char name[20];
	int  height;  // このheightやweightのことを「メンバ」という
	int  weight;
    int  year;
};

int main(void) {
	struct gstudent tanaka = {"Tanaka", 170, 60};  //=> こうして初期化も出来る
    tanaka.year = 20;

    return (0);
}

構造体のメンバ(->演算子)

struct student {
	int height;
	int weight;
};

void echo(struct student *someone) {
	printf("%d", someone->height);  // (*someone).height と同じ
}

int main(void) {
	struct student satoh = {170};
	echo(&satoh);  // "あえて" 単行&演算子(アドレス演算子)をつけている

	return (0);
}

構造体とtypedef

struct student1 {  // student1はタグ名
};

typedef struct {
} student2;        // student2は型名

int main(void) {
    struct student1 tanaka = {};
    student2        satoh  = {};  // こっちのが簡潔に書ける
}

構造体は配列と違って、構造体に構造体を代入が可能(同じ型である必要がある)。

struct student gx, gy;
gx = gy;

構造体の配列

typedef struct {
   char name[10]; 
} animal;

animal animals[] = {
    {"cat"},
    {"dog"},
};

TODO

  • ポインタと文字列
  • ファイル入出力
  • 標準ライブラリ関数

補足

ポインタについて

void echo(int num){  // 渡されたのはfooの実体(height:int型)なので、ここでは関節演算子はいらない
	printf("%d", num);
}

int main(void) {
	int height = 120;
	int *foo;
	foo = &height;
	
	echo(*foo);  // fooの実体:heightを渡している
	return (0);}
}
void echo(int *num){  // この引数はint型なので、ここでは関節演算子をつけなければならない
	printf("%d", *num);
}

int main(void) {
	int height = 120;
	int *foo;
	foo = &height;
	
	echo(foo);
    return (0);}
}

文字定数:文字を位置重引用符で囲んだもの。

putchar('a');  // putcharは文字定数を出力するもの
puthcar("a");  //=> error

*1:Cにおいて、引数にした配列は参照渡しになる

2011-03-07

[]C言語のキホン

覚えておいて損はなさそう*1なのでC言語を勉強。

  • 参照渡しは、perlで置き換えたら & が \ 、* が${}にあたるっぽいかな*2
#difine HEIGHT 180

void change_height (int *height) {
    *height = 180;
}
int main(void) {  // 引数ないときはvoid
    int height = 170;
    // ポインタの参照渡し
    int *foo = &height;
    printf("%d", *foo);  //=> 170
    printf("%p",  foo);  //=> アドレス番号
    change_height(foo);
    printf("%d", *foo);  //=> 180

    // intの参照渡し
    int bar = height;
    change_height(&bar);
    printf("%d", bar);  //=> 180
}
  • []を伴わず単独で現れた配列名は、その配列の先頭要素へのポインタとなる
int a[6] = {1,2,3,4,5,6};
printf("%d\n", *a);  //=> 1
  • (配列の要素へのポインタ) ± num で前後要素のポインタとなる*3
int a[6] = {1,2,3,4,5,6};
printf("%d\n", *(a + 1));  //=> 2
  • 配列を丸ごと代入はできない。forで回して1要素ずつ新規の配列に代入していくしかない
  • 関数間での配列の受け渡しは先頭要素へのポインタという形で行う。受け取る側の関数は、そのポインタに対して添字演算子[]を適用することで、あたかも配列を扱うように処理が行える。
void get_a (int a[]) {
    for(int i = 0; i < 5; i++) {
        printf("%d", a[i]);  //=> 1 2 3 4 5
    }
}

int main (void) {
    int a[5] = {1,2,3,4,5};
    get_a(a);
    return 0;
}
  • 挿入ソートの例
void insert_sort (int a[], int n) {
	int i, j, t;
	for (i = 1; i < n; i++) {
		j = i;
		while (j >= 1 && a[j-1] > a[j]) {
			t = a[j]; a[j] = a[j-1]; a[j-1] = t;
			j--;
		}
	}
}

int main (int argc, const char * argv[]) {
	int a[LENGTH] = {80, 77, 100, 11, 0, -1, 300, 44, 24, 91, 55};
	insert_sort(a, LENGTH);
	for (int i = 0; i < LENGTH; i++)
		printf("%d ", a[i]);
	return 0;
}

*1:というか、プログラマやるなら最低限の教養っぽいし

*2:かなりざっくりした例えだけど

*3:アドレス番号が隣接してるから、ってことかな?