Cの時代にstringはなかったです。たぶん。charを使った文字列だけだったです。stringは作られたけど、C++にsplit関数はないです。
charで文字列 たかが初期化
文字列には終端が必要なのだそうです。"\0"と記述したらエラーになりました。NULLもエラーになりました。'\0'と記述するのが正しいようです。
char data[4];
は、文字を入れる場所を四個確保したということで、入れ物のトップはdata[0]になります。すでにここから違和感がある。。。ゼロ・オリジンという言葉を思い出した。
初期化をすると、
char data[4] = "123" + '\0';
array must be initialized with a brace-enclosed initializer
と怒られました。
どうもC言語では文字列同士の連結に+記号は使えず、C++のstringでは使えるようです。
ともあれ、エラーは、
「ブレースで囲まれた初期化子で配列を初期化する必要があります」
です。意味がわかりません。
検索すると、一番正しい初期化方法は、
char data[4] = "123";
で、終端が4個目に入るようです。
char data[3] = "123";
だと、入りようがないですね。
char data[] = {"123"};
でも、終端を引っ付けて初期化が行われるようです。
char data[4] = {"1", "2", "3"};
では、終端はなしのようです。いや、だめでした。シングルクォテーションでないといけないようです。
次の書き方はセーフでした。
char data1[4] = {'1', '2', '3'};
std::cout << data1 << "\n";
char data[4]="123";
data[3] = '\0';
でも、いいわけで。添え字が気持ち悪い...
誰も読んでいないですね、こんなこと書いても。
文字列リテラルは、
"123"
で、終端記号が付加される。
単一引用符 ( ' ) を使うと、
char data[] = {'123'};
エラーになります。使い分けがまだわからん。
今気が付いたのだけれど、単一引用符 ( ' ) は、名前が示すように、文字1個だけを囲むときに使うんだという使い分けをすればいいのかな。
そして、大原則は、
文字列は二重引用符で囲む必要があります。
だって、Pythonはどっちでもよかったからねー。
つぎ!配列はポインタのアクセスでもOKだけど 混迷を深める結果になりそう
初期化ができたので、その要素にアクセスします。
#include <iostream>
#include <string>
int main(){
char inData[]={"123"};
char * dataPointer = &inData[0];
std::cout << "inData data size " << sizeof(inData) << "\n";
std::cout << "<1>inData is "
<< inData[0] << " " << inData[1] << " " << inData[2] << " " << inData[3]
<< &inData
<< "\n";
std::cout << "<2>inData is "
<< dataPointer << " " << *dataPointer << " " << *(dataPointer + 1) << " " << *(dataPointer + 2) <<
" " << "\n";
std::cout << "<3>inData is "
<< dataPointer << " " << dataPointer[0] << " " << dataPointer[1] << " " << dataPointer[2] <<
" " << "\n";
return 0;
}
実行します。
$ g++ ex102.cpp
$ ./a.out
inData data size 4
<1>inData is 1 2 3 0xffffde367bd0
<2>inData is 123 1 2 3
<3>inData is 123 1 2 3
<1>は、一番普通のアクセス方法ですね。一番最初の値は、[0]でアクセスします。
<2>は、ポインタでアクセスしています。
<3>は検索していたら出ていたのですが、ポインタの配列というのではなく、[]内はポインタのインクリメントする数値を書いているようなのです。
<2>では、まず、
ポインタ変数に配列の先頭アドレスを代入する
が重要です。
で、配列要素inData[X]の値は、 ポインタ*dataPointer+Xで参照できます。X
Xは0から始まります。
<1>の方法だけ知っていたら十分なような気がしますが、現実はどうなんでしょうか。
stringは文字列?
std::stringを使用する際、サイズの制限はありません
charを使用する場合、長さを常に把握しておかないと、はみ出してコピーしたりする事故があるようです。
で、根本的なことを! stringを使えば、文字列の処理はすべて処理できる、んだよ。
であれば、charは不要だ。
なのです。
ところが、書籍には、
C形式からC++形式への変換は、通常は自動で行われます。
C++の文字列をCの文字列に変換するには、メンバ関数c_str()を使用します。
のようなことが書かれています。
変換することが、あるのでしょうか?全部std::stringという文字列型で済ませられないのでしょうか?
検索すると、なぜか、printf文を使いたいときという事例が見つかります。そもそも使わなければいいわけですから論外です。
最近、検索のトップにAIなんちゃらのお言葉が表示されるようになった。
ほぼ普通の検索の上位1点もしくは2点の内容を丸写ししている。
で、C言語に関して、AIさんは、日本語のサイトからしか知識を集めていない。
なんと、なんと! かしこいのだろう
こういうマイナな話題に対応するまで百年はかかりそうな気がする
i2cのアクセスの正常化
前回、ex005.cppの実行をすると、リードのたびにアドレスを送出するという変なアクセスをしていました。読めるからいいものの、全てのデバイスでうまくいくとは限りません。
改善しました。温度だけですが、リード時に2バイトを連続して読み出せました。送っているアドレスも正常に表示されています。
改善したプログラムです。
#include <iostream>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>
#include <unistd.h>
#include <string>
#define lps22hbAdr 0x5d
float press;
float temp;
int init();
float read_pressData();
float read_tempData();
int fd;
int main(){
std::cout << "\nstart LPS22HB data read \n";
init();
// std::cout << "press is " << read_pressData() << "hPa\n";
std::cout << "temp is " << read_tempData() << "`C\n";
return 0;
}
int init(){
fd = open("/dev/i2c-1", O_RDWR);
struct i2c_msg msg[1]; // /usr/include/linux/i2c.h
struct i2c_rdwr_ioctl_data packets; // /usr/include/linux/i2c-dev.h
unsigned char data[2];
// wake up
msg[0].addr = lps22hbAdr; // address
msg[0].flags = 0; // read、writeやアドレス長の指定
msg[0].len = 2; // bufに指定するdataのサイズ
msg[0].buf = data;
data[0] = 0x10; // レジスタアドレス
data[1] = 0x10; // 書き込み値;5Hz
packets.msgs = msg;
packets.nmsgs = 1; // msgのサイズ指定
int ret = ioctl(fd, I2C_RDWR, &packets);
std::cout << "\nsend: 0x" << std::hex << msg[0].addr << " - 0x"
<< std::hex << (int)data[0] << " - 0x"
<< std::hex << (int)data[1] << "\n";
return 0;
}
float read_tempData(){
// read temp data
struct i2c_msg msgT[2];
struct i2c_rdwr_ioctl_data packetsT;
unsigned char data1[1], data2[2];
msgT[0].addr = lps22hbAdr;
msgT[0].flags = 0; // 最初はアドレスを書き込み
msgT[0].len = 1;
msgT[0].buf = data1;
data1[0] = 0xab; // 0x2b | 0x80 MSB data
msgT[1].addr =lps22hbAdr;
msgT[1].flags = I2C_M_RD; // リード時に設定
msgT[1].len = 2; // データを2byte読み出し
msgT[1].buf = &data2[0]; // data2に読み出しデータが入る
packetsT.msgs = msgT;
packetsT.nmsgs = 2; // アドレス書き込みとデータ読み出しでmsgは2
int retT = ioctl(fd, I2C_RDWR, &packetsT);
std::cout << "\ndata2 is 0x" << (int)data2[0] << (int)data2[1] << "\n";
int tempNative = (data2[1] << 8) | data2[0];
int temp2scomp = -(tempNative & 0b1000000000000000) | (tempNative & 0b0111111111111111);
temp = std::stof( std::to_string(temp2scomp)) / 100;
return temp;
}
実行します。
$ g++ ex104.cpp
$ ./a.out
start LPS22HB data read
send: 0x5d - 0x10 - 0x10
temp is
data2 is 0xbea
27.5`C
このプログラム、最初の例題の<?>に相当する形で、読み出したデータにアクセスしています。
msgT[1].buf = &data2[0];
左辺の構造体の要素はchar *なんです。ポインタ・アクセスの基本形ですね。まだなじめない。。。
(※)後で気が付いたのだけれど、三つのどれにも該当していないような気がする。ということは、最初の三つの説明が間違っているのかも。もっと勉強しよう。
気圧を含めて書き直しました。
#include <iostream>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>
#include <unistd.h>
#include <string>
#include <iomanip>
#define lps22hbAdr 0x5d
float press;
float temp;
int init();
float read_pressData();
float read_tempData();
int fd;
int main(){
std::cout << "\nstart LPS22HB data read \n";
init();
std::cout << "press is " << std::fixed << std::setprecision(0) << read_pressData() << "hPa\n";
std::cout << "temp is " << std::fixed << std::setprecision(1) << read_tempData() << "`C\n";
return 0;
}
int init(){
fd = open("/dev/i2c-1", O_RDWR);
struct i2c_msg msg[1]; // /usr/include/linux/i2c.h
struct i2c_rdwr_ioctl_data packets; // /usr/include/linux/i2c-dev.h
unsigned char data[2];
// wake up
msg[0].addr = lps22hbAdr; // address
msg[0].flags = 0; // read、writeやアドレス長の指定
msg[0].len = 2; // bufに指定するdataのサイズ
msg[0].buf = data;
data[0] = 0x10; // レジスタアドレス
data[1] = 0x10; // 書き込み値;5Hz
packets.msgs = msg;
packets.nmsgs = 1; // msgのサイズ指定
int ret = ioctl(fd, I2C_RDWR, &packets);
std::cout << "\nsend: 0x" << std::hex << msg[0].addr << " - 0x"
<< std::hex << (int)data[0] << " - 0x"
<< std::hex << (int)data[1] << "\n";
return 0;
}
float read_pressData(){
// read press data
struct i2c_msg msgP[4];
struct i2c_rdwr_ioctl_data packetsP;
unsigned char data1[1], data2[3];
msgP[0].addr = lps22hbAdr;
msgP[0].flags = 0; // 最初はアドレスを書き込み
msgP[0].len = 1;
msgP[0].buf = data1;
data1[0] = 0xa8; // 0x28 | 0x80 MSB data
msgP[1].addr = lps22hbAdr;
msgP[1].flags = I2C_M_RD; // リード時に設定
msgP[1].len = 3; // データを3byte読み出し
msgP[1].buf = &data2[0]; // data2に読み出しデータが入る
packetsP.msgs = msgP;
packetsP.nmsgs = 2; // アドレス書き込みとデータ読み出しでmsgは2
int retP = ioctl(fd, I2C_RDWR, &packetsP);
press = std::stof( std::to_string(((int)data2[2] << 16) | ((int)data2[1] <<8) | ((int)data2[0]) )) / 4096;
return press;
}
float read_tempData(){
// read temp data
struct i2c_msg msgT[2];
struct i2c_rdwr_ioctl_data packetsT;
unsigned char data1[1], data2[2];
msgT[0].addr = lps22hbAdr;
msgT[0].flags = 0; // 最初はアドレスを書き込み
msgT[0].len = 1;
msgT[0].buf = data1;
data1[0] = 0xab; // 0x2b | 0x80 MSB data
msgT[1].addr =lps22hbAdr;
msgT[1].flags = I2C_M_RD; // リード時に設定
msgT[1].len = 2; // データを2byte読み出し
msgT[1].buf = &data2[0]; // data2に読み出しデータが入る
packetsT.msgs = msgT;
packetsT.nmsgs = 2; // アドレス書き込みとデータ読み出しでmsgは2
int retT = ioctl(fd, I2C_RDWR, &packetsT);
// std::cout << "\ndata2 is 0x" << (int)data2[0] << (int)data2[1] << "\n";
int tempNative = (data2[1] << 8) | data2[0];
int temp2scomp = -(tempNative & 0b1000000000000000) | (tempNative & 0b0111111111111111);
temp = std::stof( std::to_string(temp2scomp)) / 100;
return temp;
}
実行しました。
$ g++ ex105.cpp
$ ./a.out
start LPS22HB data read
send: 0x5d - 0x10 - 0x10
press is 1010hPa
temp is 25.0`C
波形です。
下のリンクがおかしくなっています。C++の勉強①なんですが。。。