投稿日:

CXXFLAGSとCPPFLAGSの違い

MakefileのCXXFLAGSとCPPFLAGSの違いについて。
名前が似ているだけあって混同してしまっていることがあるので念の為に書いとく。

実は2つの変数の違いは公式ドキュメントを見ると一瞬で分かるので読む。

CXXFLAGS

Extra flags to give to the C++ compiler.

 

CPPFLAGS

Extra flags to give to the C preprocessor and programs that use it (the C and Fortran compilers).

CXXFLAGSはC++コンパイラ(CXX)のオプション、CPPFLAGSはCプリプロセッサのオプションを意味している。
MakefileにCPPFLAGS = -std=c++11tと書くのは誤りで、CXXFLAGSを使わなければいけない。

試しにCXXFLAGSとCPPFLAGSで挙動が異なるMakefileを書いてみる。

実際に以下のコマンドでmakeする。

それぞれ実行した時の出力を見るとCXXFLAGSとCPPFLAGSの違いが出てくる。

CプリプロセッサはCとC++の両方で使われるため、CPPFLAGSはどちらのソースコードをコンパイルする時でも使われる。
CPPFLAGSをC++コンパイラのオプションと勘違いしてMakefileを書くと、CコンパイラにもC++コンパイラのオプションまで渡されてしまう。

ではCPPFLAGSの正しい使い方はと言うとインクルードパスを指定するのに使うそうだ。

投稿日:

なんか人気のfopen_s関数について

前に書いた記事のPV伸びすぎなのと、最近fopen_s関数の存在を思い出したので改めてfopen_s関数について記事を書きます。
ちなみに前のfopen_s関数の記事は大学(.ac.jpドメイン)からのアクセスが結構あるので、fopen関数の代わりにfopen_s関数を教えている大学はそれなりに存在することが推測できます。

fopen_s関数について

fopen_s関数はC11で標準Cライブラリに取り入れられた関数です。
基本的にはfopen関数と同じで、ファイルを開くという役割も変わっていません。
ただし、戻り値と引数は変わっているので単純に置換することはできません。

実はfopen_s関数自体はC11以前からMicrosoft Visual C++(MSVC)の独自拡張で存在していました。
MSVCの独自拡張が標準化されたという感じです。

C11で標準化され、MSVC以外でも使えそうなfopen_s関数ですが、fopen_s関数の実装は任意です。
処理系によってはfopen_s関数は存在しません
例えばglibcはfopens_s関数を実装していません。

fopen_s関数の使い方

まずは引数と戻り値から。

fopen_s関数はファイルポインタのアドレスを引数に取り、戻り値がerrno_tになっています。
ファイルオープンに成功した場合の戻り値は0です。
ファイルオープンに失敗すればfopen関数同様にファイルポインタは空ポインタ(NULL)になり、errnoが戻り値になります。

fopen_s関数もそうですが、C11から追加された末尾に_sが付いている関数の実装は任意で、実装されている処理系では__STDC_LIB_EXT1__マクロが定義されます。
使うには該当する関数のヘッダファイルをincludeする前に__STDC_WANT_LIB_EXT1__マクロを1にする必要があります。

これはすでに_s系の関数があるMSVCで関数名が衝突しないようにするためだそうです。
__STDC_WANT_LIB_EXT1__マクロは0だと_s系の関数が宣言・定義されません。

fopen_sとfopenの違い

引数と戻り値以外にも違いがあります。
しばしば「セキュリティが強化されている」とだけ説明されますが、あまりにも抽象的すぎる説明です。

戻り値でエラーの種類が分かるからセキュリティが〜という説明も見るんですけど多分違います。
fopen関数でもerrnoで取得できますからね。

fopen_sとfopenの最大の違いはファイルを排他モードで開くかどうかです。
例えば以下の2つのコードはfopen_sとfopenの違いしかありませんが、異なる結果になります。

動作結果
http://rextester.com/JMGR52902
http://rextester.com/KOZ39339

C11のfopen_sはファイルモードのwの前にuを加えると共有モードでファイルを開けますが、MSVCの実装はC11に準拠していないので使えません。
まあxも使えないんですけど。

fopen_s関数が使われる理由

ここでとても単純なソースコードを載せます。

ファイルを開いて、閉じるだけで何もしません。

これをVisual Studio 2015とそれに付いているVisual C++でコンパイルします。
gccやclangだとコンパイルが通るにも関わらず、コンパイルに失敗します。

つまり、fopen_s関数が使われる理由はVisual Studioでエラーになるからです。

ちなみに直接clコマンドを使ってコンパイルするとエラーにはなりません。

Visual Studioでfopen_s関数を使わなくてもいい方法

_CRT_SECURE_NO_WARNINGSマクロを毎回定義する

エラーメッセージにあるように_CRT_SECURE_NO_WARNINGSマクロを定義するとC4996が無効化されます。

SDLチェックを無効化する

MSDNを参照してください。

/sdl (追加のセキュリティ チェックの有効化)
https://msdn.microsoft.com/ja-jp/library/jj161081.aspx#Anchor_4

SDLチェックは規定で無効になっているそうですが、Visual Studioでプロジェクトを作ると有効にされています。

SDLチェックを無効化すると警告C4996がエラーにならなくなります。

プリプロセッサの設定を変更する

  1. メニューの「プロジェクト(P)」→プロパティ(P)でプロジェクトの設定を開く
  2. 構成プロパティ→C/C++→プリプロセッサ
  3. プリプロセッサの定義に「_CRT_SECURE_NO_WARNINGS」を追加する
    セミコロン区切りなので注意

これで勝手にマクロが定義されます。

個人的な意見

インターネットではfopenを使うべきという人もいれば、fopen_sを使うべきという人もいます。
書籍だとほぼfopenの方を使っているのに、入門者によく勧められるVisual Studioだとfopenはエラーになります。
入門者なら間違いなく混乱するでしょう。

その混乱ぶりはYahoo!知恵袋を見れば明らかです。
https://chiebukuro.yahoo.co.jp/search/?p=fopen_s&flg=3&class=1&ei=UTF-8&fr=common-navi&dnum=2078297622

適当に「fopen_sの方が正しい」と教えるとgccやclangなどでコンパイルするときにエラーになって余計に混乱します。
絶対にやめてください
現に僕の周りの人間が混乱してます。
C11でもfopen_sはoptionalだからな。

あと、ろくに調べもせず安易な理由でにVisual StudioでC言語を教えないでください。
fopen関数使っただけでエラーになる訳あり統合開発環境であるということを理解して使ってください。
本当に頼みますよ

投稿日:

C言語でテキストファイルの行数を数える方法

1. fgets関数を使う方法

fgets関数は1行ごとに読み込むので、読みこんだ回数 == 行数としてカウントしていくもの。
読み込みに失敗するとfgets関数はNULLを返すので、それを利用してEOFまで読み込みます。

インターネットではこの方法がよく載っていますが、必ず正しい行数がカウントできるとは限りません。
fgets関数はバッファのサイズを超えて読み込まないので、1行がバッファのサイズを超えると行数を正しくカウントできなくなってしまいます。

fgets関数を使う方法は実行速度が速いので、1行の長さがはっきりと分かる場合は使ってもいいと思います。

2. fgetc関数を使う方法

fgetc関数で改行文字を1バイトづつカウントしていく方法です。
行数を正しくカウントできますが、実行速度が遅いです。

3. fread関数を使う方法

fread関数で一度バッファに読み込み、そこから改行文字をカウントしていく方法です。
バッファの終端が\0ではない点に注意しなければいけません。
fread関数は読み込んだデータの数を返すので、そこからバッファの文字数が分かります。

実行結果と実行速度

テストデータとしてexample.txt(4906788バイト、30172行)を用意して計測。
gcc 6.2.0でコンパイラオプションは-std=c99のみ指定してコンパイル。

実行結果

fgets fgetc fread
41952 30172 30172

1行の長さがバッファを超えたのでfgets関数を使ったプログラムは余計に行数をカウントしてしまいました。

実行速度

単位:秒

fgets fgetc fread
0.004 0.064 0.024

fgets関数を使ったプログラムが1番速いです。
実行結果は間違っていましたが。

投稿日:

Windowsでコンソールの文字色を変える方法

大学のシステム開発の演習をどう進めていくか決めたんですけども

  • OSはWindows
    理由: Windowsしかないから
  • プログラミング言語はC
    理由: 他の学生がCしか知らないから
  • インターフェースはCUI
    理由: 時間ないから

……かなり消極的な方法で決定してしまったので、大人しくC言語の学習をすることに。

過去の完成したプログラムを見ると、単純にprintf()で文字を出力しているだけという大変お粗末なものなので、せめて色くらいつかないものかと。
そこで、Windows上で文字色を変える方法を調べておくことにしました。

文字色を変える

これで文字の色が赤になります。
FOREGROUND_REDを変えると赤以外の色も使えます。

FOREGROUND_BLUE 0x0001
FOREGROUND_GREEN 0x0002
シアン FOREGROUND_BLUE | FOREGROUND_GREEN 0x0003
FOREGROUND_RED 0x0004
マゼンダ FOREGROUND_BLUE | FOREGROUND_RED 0x0005
黄色 FOREGROUND_GREEN | FOREGROUND_RED 0x0006
FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_GREEN 0x0007
強調 FOREGROUND_INTENSITY 0x0008

FOREGROUND_INTENSITYはオンにすると文字色が強調されるフラグ。
FOREGROUND_BLUE | FOREGROUND_INTENSITYにするとより鮮やかな青になります。

背景色を変える

これで背景の色が赤になります。
BACKGROUND_REDを変えると赤以外の色も使えます。

BACKGROUND_BLUE 0x0010
BACKGROUND_GREEN 0x0020
シアン BACKGROUND_BLUE | BACKGROUND_GREEN 0x0030
BACKGROUND_RED 0x0040
マゼンダ BACKGROUND_BLUE | BACKGROUND_RED 0x0050
黄色 BACKGROUND_GREEN | BACKGROUND_RED 0x0060
BACKGROUND_BLUE | BACKGROUND_RED | BACKGROUND_GREEN 0x0070
強調 BACKGROUND_INTENSITY 0x0080

BACKGROUND_INTENSITYはオンにすると文字色が強調されるフラグ。
BACKGROUND_BLUE | BACKGROUND_INTENSITYにするとより鮮やかな青になります。

投稿日:

| ^o^ |  なんというクソコード・・・

今までいろんなクソコード見たけど今度はもう無理だ。
クソコードすぎて降参だ。
でもこれを解読しなければいけない課題が出た。

謎の一文字変数連打

どの変数が何を意味しているのか全く分からない。
一部は一時変数として使われていて、途中で意味が変わったりする。

謎のfor文

これ意味あるの?

謎の配列

配列なのにarray[1]しか使われていない。
配列である必要が無い。

謎の即値と定数

前者のnと後者の100って何が違うんだろう?
しかもこれがいくつもあるから意味不明。

謎のgoto

とするべきところが

になってる。

なんで?(殺意)

コードを呼んだ感想

gotoと謎の即値でもう気が狂う。
死にそう。

投稿日:

srand()は1回だけでいいダルォォン?

C言語のrand()srand()でシード値を与えないと、同じ乱数が出てくる。
そこでsrand((unsigned)time(NULL));で現在時刻をシード値として与える。

 

しかしっ!

 

このように何度もsrand()するとかやりだしたぞこいつ・・・。
一体何がしたいんだ!

こうすることで

  • シード値毎回セット
  • シード値が1秒に1回しか変わらない
  • よってrand()の出力も1秒に1回しか変わらない

という事態が発生し、「あれ〜?なぜか値があまり変わらないな〜」などと嘆くようになったのだった。
—完—

というか乱数発生の仕組みを軽く触れたことがあったのにどうしてこうなった。

投稿日:

その”static”に意味はあるのか

staticという単語をつけて変数を宣言すると、関数を抜けても値が保持される。
よくこういう説明を初心者は受けることになる。
実際にそういう説明だった。

 

しかし、偶然にもこのような使い方をしてしまっているのを発見してしまった。
お前、毎回100を代入してるから意味無いじゃん。
何故こんなことをやってしまったのかさっぱり分からないが、もしかしたら意味があるかもしれないので、考える前に手を動かすことにした。

この2つのコードを

でコンパイルして、出力された実行時間を計測した。

static non-static
1 338 367
2 346 357
3 335 371
4 332 369
5 349 390
6 359 349
7 354 356
8 315 339
9 353 335
10 353 329
平均 343.4 356.2

あまり変わらないので、とりあえず何にでもstaticをつけるのはクソということだ。
まーた間違ったことを学生に教えてるのかけしからんな。

 

また、なんかこんなことがプリントに書いてあった。
文字列を変えないのでconst char*でやれという感じだが、こちらも計測。

char[] static char[] const char* static const char*
1 1513 270 278 287
2 1573 286 279 292
3 1561 274 263 278
4 1547 268 269 277
5 1634 279 271 280
6 1551 269 273 275
7 1566 263 284 270
8 1583 265 285 272
9 1586 263 271 277
10 1583 258 160 272
平均 1569.7 269.5 263.3 278

毎回バーボンハウスのコピーが行われるchar[]は激遅だが、ポインタを使うコードとでは速度は変わらない。

そもそもstaticつけた時点で、文字列を変えると2回目以降の実行結果に影響する。
文字列の内容を変更しないのであれば、const char*で文字列リテラルへのポインタを取るのが一般的だし、なぜこんな変なことを学生に教えてるのかやっぱり分からない。

投稿日:

【C言語】よく分からないクソコード

最近演習中に発掘されたクソコードだ。
xyが定数だが、実際にはWin32 APIを使ったプログラム中で、ウィンドウ中に描画する画像の座標が入る。
また、この座標はユーザの操作で変化する。

まず、どのへんがクソコードなのかと言うと、1番目につくのはsprintf()の使い方だ。
実際にコメントにはintを文字列に変換すると書いてあるし、演習中でも各種変数を文字列に変換する関数という扱いだった。
確かにそのような使い方もできるが、明らかに1回のsprintf()で済ませられるのだからもっと簡潔にするべきだ。
ちなみに、char []型変数は1つで十分だが、なぜか2つ必要らしい。なぜだか知らないが!

sprintf()を1つだけにするとこうなる。
むしろこういう使い方のほうが自然な気がするが一体どういう意図でこうしなかったのか本当に分からない。
使い方もprintf()と似ているし、違うのは出力先が文字列か標準出力かということくらいだというのに。

それともう1つ。
x座標とy座標はウィンドウ上の画像の位置で、ウィンドウの大きさなんて4桁×4桁までなのでchar []は10バイトで十分らしい。
確かに今のディスプレイの解像度を考えると十分だと思う。
しかし、座標には負の値が入ることがある。

2回目のstrcat()でバッファオーバーフローを起こしている。
このクソコードはC言語のメモリ管理の大変さを思い知らせるためだったのか?

このクソコードに対し、どのような魔改造改良をしていくべきか。
注目すべきはC言語やってるつもりなのにC++としてソースコードをコンパイルするというのが当たり前になっている大学のやり方だ。
つまり、C++が使えるということだ。

こんなことstd::stringstreamクラスで一瞬で終わる。
std::string::c_str()std::string::length()があればC言語の関数に文字列渡すのも全く問題ない。

投稿日:

C++でExifデータの読み書き

最近Exifデータを弄る機会があるので、自分用にソフトウェアを作ろうとしている。
C++でExifデータを読み書きできるExiv2というライブラリがあるので、それを使うことにした。
Exiv2の簡単な使い方を書いていこうと思う。

サンプル画像

単9363レ EF65 1132

ここでは画像を扱う。
とりあえず、昨日撮ったEF65 1132の画像があるのでそれを使うことにする。

簡単な読み書き

"Exif.Image.ImageDescription"はExiv2での各Exifデータのkeyとなっている。
標準的なExifデータのkeyはここにある。ちなみに、メーカ独自のタグにも対応している。

Exifデータを全部出力