ACM-ICPC

C言語あれこれ


C言語における面白そうな話をいろいろと。使えるかどうかは別・・・

コンマ演算子

C言語にはコンマ演算子というものがあります。これは、左辺と右辺をこの順に計算して右辺を返します。演算順位は3項演算子〜?〜:〜よりも弱く、C言語の演算子の中では最弱です。

たとえば、
x = ( 1 + 2, 3 + 4);
とすると、xに7が代入されます。

ただし、同じ事をしようとして、
x = 1 + 2, 3 + 4;
とすると、
( x = 1 + 2), (3 + 4);
と解釈されるので注意してください。

これを使うと、データ入力部分
scanf("%d", &n);
while(n != 0){
  ・・・
  scanf("%d", &n);
}
は、次のように書くことができます。
while(scanf("%d", &n), n != 0){
  ・・・
}

ただし可読性の観点から、好みの問題かと・・・

クイックソート関数

あまり知られていないようですが、C言語のライブラリstdlib.hには、クイックソートをする関数qsortが用意されています。
void qsort(void* base, size_t n, size_t size, int(*func)(const void*, const void*));

base:ソートする領域の先頭のアドレス
n:データの要素数
size:データの1つの大きさ(バイト)
func:比較関数

この関数を使うには、比較関数を用意する必要があります。2つの要素のアドレスを渡されたとき、その要素を比較して、左が大きければ正の値、右が大きければ負の値、等しければゼロを返す関数です。

単に整数を比較するのであればこのようになります。
int cmp_fumc(const void* p, const void* q){
  return *((int *)p) - *((int *)q);
}
戻り値はint型にしてください。それと、引数はconst void*でなければならないので、キャストして使います。

構造体struct cellのメンバvalue(double型)を比較するのであればこのようになります。
int cmp_fumc(const void* p, const void* q){
  double x, y;
  x = ((struct cell*)p) -> value;
  y = ((struct cell*)q) -> value;
  if(x > y)
    return 1;
  else if(x < y)
    return -1;
  else
    return 0;
}

qsortは昇順にソートするので、降順にソートしたいときは返す値の符号を逆にします。

比較した結果が等しい(比較関数が0を返す)場合、どちらが先になるかは定義されません。どちらが先になるか明示したい場合は、必ず正か負の値を返すように条件を書く必要があるでしょう。

では、実際に使ってみます。
#include <stdio.h>
#include <stdlib.h>

struct student{
  char name[16];
  int math, english;
};

int cmp_fumc(const void* p, const void* q){
  return ((struct student*)q)->math - ((struct student*)p)->math;
}

int main(void){
  int n, i;
  struct student s[100];
  scanf("%d", &n);
  for(i = 0; i < n; i++)
    scanf("%s %d %d", s[i].name &s[i].math, &s[i].english);
  qsort(s, n, sizeof(s[0]), cmp_fumc);
  for(i = 0; i < n; i++)
    printf("%d: %s (%d, %d)\n", i+1, s[i].name, s[i].math, s[i].english);
  return 0;
}
n人分の名前、数学の点数、英語の点数を入力して、数学の点数が高い順にソートして表示します。


文字判定ライブラリ

C言語にはctype.hというライブラリがあります。これは、ある文字(intまたはchar型で表される数値)が、アルファベットであるか、数字であるか、などの判定をします。

例えば、isalphaというのがありあす。これは、引数がアルファベットであるかを調べるもので、
#define isalpha(c) ('a'<=(c) && (c)<='z' || 'A'<=(c) && (c)<='Z')
のようなものだと思ってください。

他にもいろいろと。
isdigit 10進の数字 0-9
isxdigit 16進の数字 0-9, A-F, a-f
isupper 英大文字 A-Z
islower 英小文字 a-z
isalpha 英字 A-Z, a-z
isalpnum 英数字 0-9, A-Z, a-z
ispunct 記号 !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~



typedefと構造体

typedefを使うと、変数の型に独自の型名をつけることができます。例えば、
typedef int seisuu;
と書くと、seisuuという型名をint型として使うことができ、
int main(void) {
 seisuu x, y;
 scanf("%d %d", &x, &y);
 ...
のように普通のint型と同じように使うことができます。

イメージとしては、変数宣言の頭にtypedefをつけると、もともと宣言されるはずだった変数名がその変数の型を表す型名になる・・・といった感じでしょうか。え、余計わかりにくいって?
例えば、
struct node *pointer;
とすると、struct node型のポインターpointerが宣言されます。が、
typedef struct node *pointer;
とすると、pointerという型名が「struct node型のポインター」を表す型名になり、
  pointer root;
のように宣言することができるようになります。

これは、構造体と一緒に使ったときにさらに威力を発揮します。
typedef struct {
 ・・・
} student;
と書けば、studentという型名でこの構造体を使うことができるようになります。
  student a[100], x, y;
のように。