としおの読書生活

田舎に住む大学院生であるとしおの読書記録を綴ります。主に小説や新書の内容紹介と感想を書きます。 読書の他にも旅行やパソコン関係などの趣味を詰め込んだブログにしたいです。

タグ:C++

code-1839406_640


C言語でfloat型やdouble型に対してビット演算を行おうとしたところコンパイラに「error: invalid operands to binary & (have ‘float’ and ‘int’)」(要するにそんなことできないぞ)と怒られてしまいました。

そこで解決策を調べたので忘備録として記録しておきます。




float型でbit演算を行うための解決策


まず結論からになりますが、共用体(union)を使用することでこの問題を解決することができました。

typedef union longfloat{
  long lnum;
  float fnum;
}longfloat;

上記のような共用体をしようすることでfloat型であるlongfloat.fnumに値を入れた後、longfloat.lnumに対してビット演算を行うことであたかもfloat型にbit演算を行ったかのように扱うことができます。

以下にサンプルコードを示します。


floatにビット演算を行うサンプルコード



#include typedef union longfloat{ long lnum; float fnum; }longfloat; int main(void){ longfloat lfnum = {0}; lfnum.fnum = 1.2345; printf("fnum:%08lx\n", lfnum.lnum); lfnum.lnum = lfnum.lnum >> 8; printf("fnum:%08lx\n", lfnum.lnum); lfnum.lnum &= 0xFFFF0000; printf("fnum:%08lx\n", lfnum.lnum); return 0; }


出力結果
fnum:3f9e0419
fnum:003f9e04
fnum:003f0000






double型にビット演算を行うサンプルコード


#include 

typedef union longlongdouble{
  long long llnum;
  double dnum;
}longlongdouble;

int main(void){
  longlongdouble lldnum = {0};
  lldnum.dnum = 1.2345;
  printf("fnum:%016llx\n", lldnum.llnum);

  lldnum.llnum = lldnum.llnum >> 8;
  printf("fnum:%016llx\n", lldnum.llnum);

  lldnum.llnum &= 0xFFFFFFFF00000000;
  printf("fnum:%016llx\n", lldnum.llnum);

  return 0;
}


出力結果
fnum:3ff3c083126e978d
fnum:003ff3c083126e97
fnum:003ff3c000000000


code-1839406_640


CやC++でコマンドを実行(サブプロセスを起動)する標準的な関数としてsystemとpopenがあります。

これらの関数を使っているコードを読んでいるとどういった基準で使い分けをしているのか分からなかったため、今回はsystemとpopenの使い分け方について調べてみました。





まずは結論

まずは結論です。

コマンドを実行したいだけなら「system」を使うべき。

コマンドの実行結果を取得したい場合は「popen」を使うのが有効。


以下にsystemとpopenを使用してコマンドを実行するサンプルコードとそれぞれのメリット、デメリットを記していきます。


system関数でコマンドを実行するサンプル


#include 
#include 

int main(void){
    int ret = 0;
    
    ret = system("rm -f test.txt");
    if (ret == -1){
        printf("ERROR\n");
    }

    return 0;
}



system関数のメリットとデメリット


メリット


  • popenと比べてコードが簡易的なため書き間違いを防ぎやすい

デメリット


  • 実行結果を取得するためには実行結果を一度ファイルに出力するなど工夫が必要
  • popen(コマンド, "w")のように標準入力にデータを与えることができない






popen関数でコマンドを実行するサンプル


#include 
#include 

#define BUF 256

int main(void){
    FILE *fp;
    char buf[BUF];

    fp = popen("pwd", "r");
    if (fp == NULL){
        printf("ERROR");
        return -1;
    }
    
    while(fgets(buf, BUF, fp) != NULL){
        fputs(buf, stdout);
    }
    pclose(fp);

    printf("buf = %s\n", buf);

    return 0;
}



popen関数のメリットとデメリット


メリット

  • 実行結果の取得がファイル読み込みの操作と同じ要領でできる
  • system関数と違い標準入力にデータを与えることが可能


デメリット

  • ploseの書き忘れによりメモリリークの恐れがある



最後に


もしこの記事を読んだ人の中でsystemとpopenに他にもメリットやデメリットがあるということを知っている方がいれば、コメントで教えていただければ幸いです。






code-1839406_640


C言語やC++でbit演算を使うときに最低限これだけは覚えておいた方が良いという基本的な使い方をまとめました。





AND演算子でマスクする


マスクは特定のbitを強制的に0にしたい場合に使用します。

以下に01010101に対して下位4bitをマスクするサンプルコードを示します。
#include 

#define EVENT 0b11110000

int main(void){
    unsigned char sample = 0b01010101;

    sample &=  EVENT;

	return 0;
}



OR演算子でセットする


セットはマスクとは逆の操作で特定のbitを強制的に1にしたい場合にしようします。

以下に01010101に対して上位4bitをセットするサンプルコードを示します。
#include 

#define EVENT 0b11110000

int main(void){
    unsigned char sample = 0b01010101;

    sample |=  EVENT;

	return 0;
}



XOR演算子でbitを反転する


bit操作を行っているとbitを反転したい場面によく遭遇します。

そういうときは、XOR演算子を使いましょう。

以下に01010101の全てのbitを反転するサンプルコードを示します。
#include 

#define EVENT 0b11111111

int main(void){
    unsigned char sample = 0b01010101;

    sample ^=  EVENT;

	return 0;
}



特定のbitの値を取り出す


"N"bit目を取り出したいというときは、シフト演算子を使えば楽に実装することができます。

以下に01010101の4bit目を取り出すサンプルコードを示します。
#include 

int main(void){
    unsigned char sample = 0b01010101;
    int N =4;
    int nBitNum;

    nBitNum = (sample >> N) & 1;
    printf("%d\n", nBitNum);

	return 0;
}



最後に


bit演算子の使い方について簡単にまとめましたが、随時便利な使い方を見つけ次第記事を更新していきます。





code-1839406_640


C++でint型やfloat型などの数値を文字列に変換する方法が分からず、いくつか方法を調べたので忘備録として記録しておきます。




sprintfを使用した方法


sprintfはC++のライブラリではなくCのライブラリを使う方法で、数値から文字列変換の一番王道な方法な気がします。
#include 

int main(void){
	char iStr[50] = {0};
	char fStr[50] = {0};
	
	int iNum = 123;
	float fNum = 4.56;
	
	sprintf(iStr, "%d", iNum);
	sprintf(fStr, "%f", fNum);
	
	printf("%s\n", iStr);
	printf("%s\n", fStr);
	
	return 0;

}

ただこの方法は数値の長さがchar配列のサイズより大きい場合、バッファオーバーフローを起こす危険があるので以下のようにsnprintfを使ったほうがいいです。



snprintfを使用した方法


snprintfは上記のsprintfのバッファサイズが指定できる版です。
#include 

int main(void){
	char iStr[50] = {0};
	char fStr[50] = {0};
	
	int iNum = 123;
	float fNum = 4.56;
	
	snprintf(iStr, sizeof(iStr), "%d", iNum);
	snprintf(fStr, sizeof(fStr), "%f", fNum);
	
	printf("%s\n", iStr);
	printf("%s\n", fStr);
	
	return 0;

}







ostringstreamを使用した方法


こちらはC++のライブラリであるsstreamの中のostringstreamを使用した方法です。

char配列に変換するために「ostringstream → string → char配列」という流れがあるので少し分かりにくい方法な気がします。

ただ、fstreamとかを使い慣れている人にとっては読みやすくなるのかもしれません。
#include 
#include 
#include 

int main(void){
	std::ostringstream iOss;
	std::ostringstream fOss;
	
	int iNum = 123;
	float fNum = 4.56;
	
	iOss << iNum;
	fOss << fNum;
	
	printf("%s\n", iOss.str().c_str());
	printf("%s\n", fOss.str().c_str());
	
	return 0;

}



to_stringを使用した方法


stringクラスのto_string関数を使った方法ですが、個人的には今までの方法の中でこの方法が一番分かりやすいと思っています。

ただし、C++11以降でしか使えないという欠点があり、過去のプロジェクトでC++98などの古い言語規格を用いている場合、使用できないという欠点があります。
#include 
#include 

int main(void){
	std::string iStr;
	std::string fStr;
	
	int iNum = 123;
	float fNum = 4.56;
	
	iStr = std::to_string(iNum);
	fStr = std::to_string(fNum);
	
	
	printf("%s\n", iStr.c_str());
	printf("%s\n", fStr.c_str());
	
	return 0;

}



最後に


いくつか数値を文字列に変換する方法を書きました。

このほかにもっと良い方法があればコメントで教えていただければ助かります。





code-1839406_640


業務中にCやC++でプログラムを作成していると、C++11でコンパイルしたい場合やC17++でコンパイルしたい場合など業務内容によって使用したい言語規格が変わってくる場合があります。

今回は、言語規格を切り替えてコンパイルする方法を調べたので忘備録として残しておきます。






コンパイルの方法


最初に結論からになりますが、C++11でコンパイルしたい場合は以下のコマンドでできます。
g++ -o hello hello.cpp -std=c++11

-stdオプションを使用することで言語規格を使い分けることができます。


ちなみにC言語の場合もC++とほぼ同じで、以下のコマンドで言語規格を切り替えることができます。
gcc -o hello hello.c -std=c11


以下にその他の言語規格に切り替える方法を示します。


ANSI Cに変更する場合

gcc -o hello hello.c -std=c90

ISO C99に変更する場合

gcc -o hello hello.c -std=c99

ISO C11に変更する場合

gcc -o hello hello.c -std=c11

ISO C18に変更する場合

gcc -o hello hello.c -std=c18






ISO C++98 に変更する場合

g++ -o hello hello.cpp -std=c++98

今日の天気は晴れです

g++ -o hello hello.cpp -std=c++11

ISO C++14 に変更する場合

g++ -o hello hello.cpp -std=c++14

ISO C++17 に変更する場合

g++ -o hello hello.cpp -std=c++17






↑このページのトップヘ