単語記事: C言語

編集

C言語とは、プログラミング言語の一つである。単にCとも呼ばれる。

概要

AT&T社のベル研究所でB言語の後継として、UNIXの移植性を高めるために作られた。

Bの次はアルファベットでCなのでCと命名されたとも、B言語の元となったBCPLの2番目の文字を採って命名されたとも言われる。

元々UNIXを記述する目的で内輪向けに作られた言語であるため、安全性はあまり考慮されていない。配列の添字チェックが無かったり、ポインタが無効でもアクセスしようとしたり、整数とポインタを自由にキャストできたり。やりたい放題。

しかし、記述の制約がそれまでの言語よりも少なく、より簡易的な記述が可能になったことや、GOTO文を排除した構造化プログラミングが容易に行えることから、1980年代より普及が進んでいった。

その後、C++、Java、C#といったプログラミング言語、Perl、JavaScript、PHPなどのスクリプティング言語の仕様に大きな影響を与えた。

C言語に関する規格

C言語はプログラミング言語の中でも比較的歴史が長く、何度か規格が改定されてきた。

K&R

K&Rとは、カーニハンとリッチーの共著『The C Programming Language』(邦題:プログラミング言語C)のこと。転じて、標準化以前のC言語を指す。K&Rは解説書であり規格書ではないため曖昧な記述も多く、これを元に作られたコンパイラ同士の非互換性を生むこととなった。

プログラミング言語を学ぶ人が一番最初に出会う事の多いプログラム、「Hello, World!」はこの本に由来する。

C89

ANSIが1989年に策定した規格。ANSI Cとも呼ばれる。これにより、初めてC言語の標準化が行われた。

C90

ISOが1990年に策定した規格。内容はC89と同一。

C95

ISOが1995年に策定した規格。C90に国際化のためワイド文字列関連のライブラリが追補として追加された。

C99

ISOが1999年に策定した規格。複素数や可変長配列など、様々な機能が追加された。

現在、一部を除いて一般的に用いられている規格。 

C11

ISOが2011年に策定した規格。スレッドやUnicode対応など、またまた様々な機能が追加された。それと、gets関数が削除された

こんなところで使われているよ

  • ニコニコ大百科の自動リンク機能
  • 2ちゃんねるのread.cgi
  • Unix Kernel (Solaris, FreeBSD, NetBSD, OpenBSD...)
  • Linux Kernel
  • 組み込み開発(Arduino 開発環境など)
  • Rubyインタプリタ
  • その他ありとあらゆる分野

主なコンパイラ

  • cl (Microsoft Visual C++のコンパイラ。Windowsのデファクトスタンダード。C99/C11未対応)
  • gcc (GNU C Compiler。OSSのデファクトスタンダード。 C99を完全にはサポートしていない)
  • clang (llvmバックエンドのコンパイラ。今後Mac OS XやFreeBSDの標準となる予定。)
  • icc(Intel C Compiler。非Windows版は非商用に限り無料。)
  • x86 Open64 Compiler Suite(AMD製コンパイラ。オープンソース。)

サンプルコード

Hello, World!

Cを学ぶ上で避けては通れないコード。

#include <stdio.h>

int main(void) {
printf("Hello, World!");
return 0;
}

よくある勘違い

void main

C言語でのmain関数は

  • int main(void) { /* ... */ }
  • int main(int argc, char *argv[]) { /* ... */ }

と定義するか、

  • int main(int argc, char** argv) { /* ... */ }

のような同等の定義か、処理系依存の定義をしなければならない。(C99 5.1.2.2.1)

よって、

  • void main(void) { /* ... */ }

は、C言語の規格上定義されていない。

また、return せずに main 関数の最後まで到達した場合、main 関数は 0 を返したことになる。int と互換性の無い型の値を返した場合には不定となる。(C99 5.1.2.2.3)

よく気付かれるので、C言語の解説動画などでvoid main(void)にすると荒れる原因となる。

絶対に使ってはいけない関数

諸ヘッダファイルに定義はされているもののその関数自体に重大なバグを抱えている関数がC言語には存在する。C言語プログラミングを学ぶ人のために、簡単にまとめておく(適宜追加してください)。

char *gets(char *s)

この関数は、標準ヘッダファイルstdio.hに宣言されており、 標準入力(stdin,普通はキーボード入力が該当)から改行文字かEOFまでの一行を受け取り、引数となるcharポインタ型の変数が指すバッファに格納する(末尾の改行文字'\n'やEOFは'\0'に置き換えられる)。

問題点は、例えば以下のような状況でバッファオーバーランを起こし、変な動作をする点である。

例えば、最初に変数として「char s[200];」を宣言し、「gets(s);」でキーボードから文字を入力することを考える。ここで、入力が改行記号を除いた199バイトより大きい場合、その余剰分のデータが分け与えられたメモリ領域とは別のところを書き込んでしまう。AAにするとこんな感じである(正確に言うと違うが、簡単に述べている)。

↓あるデータ     ↓s[200]      ↓あるデータ
…0010001000][000000…0000][100010010010…

↓199バイトより大きい文字列をgetsで取得


↓あるデータ     ↓s[200]      ↓あるデータ
…0010001000][101110…1011][011010110010…

この右側の下線をつけたところが変に書き込まれたされたところである。ここに、たまたま別ウィンドウで開いてたお宝画像のデータがあったとしたら…?そのデータは壊れて変な画像になったり、開けなくなってゴミと化すかもしれない。また、ここにOSの重要なプログラムが入っていたとしたら…?PCの動作が不安定になるのは明らかである。

ここではデータ破壊の可能性について論じたが、これがセキュリティホールの原因ではない。
たぶんs[200]の後には何らかの戻りアドレスが入っている確率が高い。そこで、sに「200バイトのダミーデータ」+「ニセの戻りアドレス」+「侵入用プログラム」と書くと、プログラムの送り込みと起動が1回の動作で実行できることになる。

対策は、fgets(s,sizeof(s),stdin)を使うこと。これも指定された数以上に入力があったらプログラムの動作がおかしくなる場合があるが、少なくとも大事なデータを書き換えたり、こんな簡単にシステムに侵入されることはない。

getsはあまりにダメダメすぎる存在だったので、最新の標準規格C11で削除された。

関連動画

  

関連商品

 

 

関連項目


【スポンサーリンク】

携帯版URL:
http://dic.nicomoba.jp/k/a/c%E8%A8%80%E8%AA%9E
ページ番号: 451144 リビジョン番号: 1615774
読み:シーゲンゴ
初版作成日: 08/08/11 01:51 ◆ 最終更新日: 12/08/25 05:20
編集内容についての説明/コメント: バッファオーバーフロー攻撃について
記事編集 / 編集履歴を閲覧

この記事の掲示板に最近描かれたお絵カキコ


getsの危険性

この記事の掲示板に最近投稿されたピコカキコ

ピコカキコがありません

C言語について語るスレ

119 : ななしのよっしん :2016/04/18(月) 10:19:40 ID: LmlCNa3fp8
>>118さんありがとうございます。成る程、基礎は大事ですね。
しかし趣味で実用出来ないのはちょっと寂しいですね……。
なぜか手元にC#の絵本があったのでC#をかじってみようと思います。
ありがとうございました。
120 : ななしのよっしん :2016/04/27(水) 13:59:01 ID: /CCf+S7ued
プログラミングコンテストなるものがあって高校生向けから一般向け、数分で攻略するものから半月くらいかけるもの、簡単な計算からゲーム攻略アルゴリズム開発、といろいろあるらしい
選ぶ言語によっては課題を制限時間内に終わらせるのが物理的に難しくなるものがあるらしいし言語ごとの特性を知る手掛かりになるかも
言語による縛りはないと思うし過去問探して適当に見てみるといいかもね
121 : ななしのよっしん :2016/04/27(水) 14:06:14 ID: S01Z1DFLIi
言語の上っ面をやるより先に
プログラムとはなんぞやを理解すべき
122 : ななしのよっしん :2016/07/17(日) 15:09:14 ID: BfZiHfwtha
よくポインタで詰まる奴がいるがコンピュータアーキテクチャを勉強すればそれなりに理解できるだろ
勉強の仕方的にとりあえず何となくC触ってこんなものなのかーと掴んだ上でハードウェアの方行ってメモリ処理なり割り込みなり見てCに帰ってくるとよく分かるようになる(まあ偶にそっからアセンブラとかバイナリコード行って帰ってこなくなる人がいるけどそんな変態はあんまり居ないから大丈夫だ)
まあ何が言いたいかって言うとPICやれ
Cは実物(ハードウェア)と仮想空間(プログラム)の橋渡しだし、その辺り弄らないならJavaなりC#なりから弄ってオブジェクト指向理解する方がよっぽど建設的
123 : ななしのよっしん :2016/07/17(日) 15:19:28 ID: BfZiHfwtha
まあJavaなりC#なりやって理解を深めようとメモリ関連イジりだすとどうせCに戻ってくるんですがねw
どうりでプログラマの基礎とか呼ばれるわけだよ、ちくせう
どうせ皆Cになる(辿り着ける人は稀)
124 : ななしのよっしん :2016/07/31(日) 23:24:06 ID: Orp85TEfNn
なんかの本の著者が書いてた「Cのポインタがわかりにくいのはアドレスどうの概念どうのじゃない、*++pがどうしたp[i]がこうしたっていう変態記法が単に分かりにくいんだ」ってのがストライクだった
125 : ななしのよっしん :2016/09/14(水) 06:44:55 ID: G3y2KhGZ/x
メモリ管理とかポインタが面倒そうでなんとなく避けてたけど
ポインタとかマクロがある程度使えるようになると気に入ってきて
やがて「Cでオブジェクト指向書くの最強wwほかの言語いらねーwww」とか思うようになってしまった

>>123
Java→C#→SwiftときてCにきた
126 : ななしのよっしん :2016/11/17(木) 23:41:57 ID: 1akJmL6WRW
オブジェクト指向やるならC++だろう。

C/C++でオブジェクト管理してると、他の言語使ってて「この引数はコピーが渡るのかどうか」みたいなことが気になる。
127 : ななしのよっしん :2017/01/17(火) 23:01:06 ID: 7V6NdOBJRb
コピー気にしなくちゃならないのはむしろC++特有というか・・・結局int互換で全部考えるからby valueでいいんだってのがCで、C++はその拡張としてクラスを定義したもんだから代入セマンティクス周りが酷いんだよね。大半のOOPのオブジェクトは黙って参照型なんで勝手にコピーされたりしない。C++的に見ると全部ポインタで持つようなもんだからな。
128 : ななしのよっしん :2017/02/11(土) 11:19:12 ID: B2me8Gv2Th
最近まで知らなかったこと:
c で符号付き算術のオーバーフローが未定義なのは知ってたが、符号付きの型への変換時に起きるオーバーフローは処理系定義
unsigned ui = -1; // 必ず UINT_MAX
int i = UINT_MAX; // ほとんどのコンパイラで -1, 鼻から悪魔が飛び出すことはない
void foo(int a, int b) {
 if (a < 0) return;
 if (b < 0) return;
 int c = a + b; // a, b が正なので、オーバーフローしなければcも正
 if (c < 0) extfunc(); // オーバーフローしたときだけ鼻から悪魔を飛び出させるより、オーバーフローをなかったことにする方が現実的で速いので、extfunc は呼ばれない可能性がある
}
https://godbolt.org/g/MvWxvP
あと本当にどうでもいいけど、関数は自動的にポインタへ変換され
(省略しています。全て読むにはこのリンクをクリック!)
  JASRAC許諾番号: 9011622001Y31015