C言語なら俺に聞け 158
■ このスレッドは過去ログ倉庫に格納されています
というか、右辺値と左辺値の違いってアドレスを持つか持たないかの違いだと思っていたのですが
違うのでしょうか? え、右辺値と左辺値の違いってイコールの左辺になりえるかどうかじゃねーの? >>164
左辺値です。「アドレスを持つか」という分類でだいたい間違いないと思います。
>>166
「イコールの左辺になりえるかどうか」は語源の話としては正しいですが、現状の語義は違ったものになっています。
規格の "lvalue" に対する脚注でも「オブジェクトに対する "locator value" を表すものと考えたほうがよいだろう」とされています。 >>167
すみませぬ。
アドレスを持たないってどういうのを言うんですか。 >>164
配列は lvalue だが、ポインタに変換する規則が適用された後は lvalue ではない。 >>168
たとえば数値リテラルや算術演算子の結果など。これらに対して "&" でアドレスを取ることはできない。 >>168
仕様上の理屈ではアドレスをもたないものが右辺値 (rvalue) なんじゃなくて右辺値がアドレスを持たないので逆なんだが……。
たとえば
int a=1;
a=2;
というように変数に代入できるが、
1=2;
という式がおかしいことは感覚的にわかるだろう。
a には 1 が入っているんだから a を評価すれば 1 になるはずなのに 1=2; と同じではない。
このとき a は a に入っている値のことであると同時に場所としての性質を持つ。
アドレスを持たないのが右辺値であるのとは対照的にアドレスを持つ (場所としての性質を持つ) のが左辺値だ。
左辺値は現れる文脈によって「値 (右辺値)」に変換される。
左辺値が値に変換される場合とされない場合が上手く使い分けられているから代入が成立するわけ。
一例をあげると以下のような式の途中経過として現れる一時的な値は右辺値であるので、
単項 & を適用できずにエラーになる。
#include <stdio.h>
int main(void) {
int a=1, b=2;
printf("%p\n", &(a+b)); // a+b の結果は rvalue なので & を適用してアドレスを知ることは出来ない
} >>168
int a;
&a //OK.これがlvalue
&1 //NG. これがrvalue
const int b = 1;
&b //OK.これもlvalue
b = 1; //NG. lvalueには書き込み可能でないものもある 164です、ありがとうございます
以下の内容で理解しました
場所としての性質を持つのが左辺値、持たないものが右辺値
左辺値は場所としての性質を持つためアドレスを持つ
参考書には文字列リテラルと配列はアドレスを持つ右辺値だと書いてあります
誤植かもしれませんね
ちなみにですが関数も左辺値でしょうか?これもアドレスを持つ右辺値と書いてあります >>174
関数については関数指示子という概念があらたに登場するのでちょっとややこしくなる。
「関数そのもの」は C では値として扱うことができないので左辺値でも右辺値でもない。
式中に関数名を書くとそれは関数ポインタに型変換されて、その関数ポインタは右辺値。 ついでの補足しておくと、
普通の関数呼出しも (言語仕様の理屈の上では) 関数ポインタに変換されてから解釈されている。
有名なネタとして
(**********printf)("hello,world\n");
というように * をいくつ付けても問題なく通る。
関数指示子が関数ポインタに (暗黙に) 変換されるという規則のせいで結局は関数ポインタとして解釈されるので。 C++だと関数も左辺値とされていて、そのことで互換性の問題が起こったりしていないので
性質の面で関数指示子は左辺値と大差無かったりする。 宣言された関数(のポインタ)はconst左辺値の認識でいたけどC言語だと違うのか?
typedef double(*functype)(double);
functype f;
f=sin; // 可能
sin=f; //不可能 関数は左辺値
ただしsizeofと代入を除きポインタに変換され
そのポインタは右辺値 関数についてまとめると、
関数そのものは右辺値でも左辺値でもない、式中の関数名は関数ポインタに変換されて右辺値になる
(C++では関数は左辺値である)
初心者なので誤植があるとパニックになりますね…
どうもありがとうございました C言語の範囲内なら用語に惑わされず振る舞いだけを考えれば
そこまで混乱することはないんじゃなかろうか (>>176 の例のような変態的なのはおいといて)
C++ の ムーブセマンティクスがなかなか なかなか https://www.toyokawa.jp/
初心者すぎる質問なんだが、C言語系のループは目的に応じて1始まりにする
必要があるとブログ記事で読んだんだが、実際はどちらが良いの?
このブログにように九九を計算して配列に入れる場合に九九だから1始まりで計算して、配列へのアクセスは−1する。
今までやっていたようにゼロからループして、計算するときに+1して、配列にはそのままアクセス。
現場だと何も考えずにゼロからループし、目的に応じてオフセットさせていたけど特にコードレビューで突っ込まれた経験も無いので。 プログラミング初心者なんやが、まずC言語の何からすればいいんや。
初歩的な質問ですまない。 プログラミング初心者なんやが、まずC言語の何からすればいいんや。
初歩的な質問ですまない。 >>182
あと、比較するときも無意味だから<=や>=は使わずに<、>を使えば良いって指示は受けたことがある。
for(i=10;i<=20; じゃなく
for(i=10;i<21; とすれば文字が少なくなり間違えが減ると言ってた。
ただ、ブログをみると、21と言う数字は唐突なので10から20までループさせるのなら <=20を使うべきと書かれている。
どちらが正しいのか? それC言語の仕様じゃなくて現場の慣例に従うべきだろ 変数を定数と比較するなら変数を右辺にもってこいとかの
文法外のコーディングルールは郷に従え としか >>184
とりあえずHello world
これがなにか分からない場合は
カーニハン&リッチー著のプログラミング言語C
という本を何とかして手に入れろ
文庫本サイズの小さな本だが
Cの発明者が書いている本で
基本はこれでOK C言語の構造体ってメモリーの割当って全てで保証されているのだろうか?
ビッグエンディアン、リトリエンディアンで異なるのは分かるのだが
全てリトル、Windowsで動くとして同じなのだろうか?
昔、アプリケーションの保存データを一つの構造体に収納し、それをそのままバイナリーとして
ディスクに出力していたのだが当時から気持ち悪く思っていた。
公式ドキュメントで保証でもされていれば良いのだが。 実行ファイルがそのままで、実行環境のOSのを行き来するならまず変わらんだろう
ソースをコンパイルしなおした結果として同じ配置になるかどうかは使ってるコンパイラ次第
ソースを別のコンパイラでコンパイルした時も同じ配置になるかどうかは使ってるコンパイラ次第
どんな場合においてもなんとかポータビリティを維持しようとするとテキスト化する羽目になる まあ
{
char ch0;
int int0;
}
みたいになるときは
{
char ch0;
char dummy[3];
int int0;
}
って入れてるよ俺は [3]というマジックナンバーがイヤ
詰め物の不定がイヤならmemsetでゼロクリアすれば済むこと エンディアンを合わせて読み書きなんて朝飯前だし
構造体を渡された関数は中でfread/fwriteを1回じゃなきゃいけないなんて縛りはナンセンス
構造体の中に構造体があるときは、その構造体の読み書きも関数化しておけば何も問題はない パディングの距離、何バイトでアライメントされるかを制御したいとなると
コンパイラ依存の #pragma に頼るわ >>191
10人規模のプロジェクトだったが、構造体のデータをそのままファイル出力してた。
特にパフォーマンスを求められる部分でも無かったので、自分でマッピングして出力したかったのだが、現状できているからと言うことで却下。
ただ、気持ち悪さは残る。案の定といってもよいが、Sun Sparkが混在したときは当然問題が出たらしい。
関係ないが、そのソフトハウスって、OracleにInsertした順序でselect出来るって信じているところもあった。
実際、そうなるが気持ち悪い。 C言語で冗長化と称して同じ内容を複数の変数に書き込みバックアップをするってプロジェクトが有ったが、意味有ったのだろうか?
今でも、変数ってプログラマが意図して変更しない限り値が変わることは無いし、仮に変わったとしたらもうプログラマレベルでは対処できない問題だと思う。
あと、そんな可能性があったらループ変数だってループ中に意図せずに変わると思うが。
(ハイレベルな一般的なアプリで、OS保護下で動くもの、DMAとか他ハードウェアとメモリを共有するような低レベルな話ではない) 昔、組込みやってた時は全部ではないけど特に動作中に変化すると致命的な箇所は同内容を複数の変数に保存していたことはある
製品評価でノイズ試験やサージ試験おこなっていると意外にマイコン内のメモリのデータはよく化ける >>197,198
素朴な疑問なんたが、それ不一致起こしてた場合どっちが正しいと判断してたんだ? >>190
言語仕様としての保証はあんまりないよ。
ABI (Application Binary Interface) と呼ばれる規格を作って同じアーキテクチャでは同じレイアウトにする通例はあるし、
既存のライブラリと組み合わせられないと不都合だからだいたいのコンパイラはそれに従ってる。
x86 (64ビットも含む) については歴史的事情で Windows はマイクロソフトが決めた ABI があって、
Windows 意外 (Linux とか BSD とか) はおおよそ System V ABI を採用してる。
で、自分が作るプログラムがターゲットにする範囲 (開発環境・実行環境) で ABI が共通ならそれでいいし、
違うなら適当に切り分けて対処するとかでも問題ない。
まあ結果的に問題なかったとしても検証せずにやってたんだとしたら良くはないけどな。 >>200
不一致が起きた場合はとにかく最優先でシステムを安全に停止させる
制御不能で暴走させることだけは何としても回避する
その後はソフトリセット処理を実行して再起動させていた PC でもデータが化けることは思ったよりも頻繁に起きているらしい。
データ破損を検出して補正する ECC と呼ばれる機構を持ったメモリもあるんだが、
コンピュータの値下げの圧力と信頼性の高い現代のメモリでは ECC は必要ないという論から ECC を付けてない機種が増えてきてる。
でもリーナスは ECC は要るにきまってんだろという感じの発言をしてる。
https://linux.srad.jp/story/21/01/05/0240202/
実際問題として化けるんだよ、化けたことを検出したり補正したりする機構がいたるところに入ってるから
アプリケーションからは気にせずに済んでるだけで……。 >>192
よく見かける手法だな。ただ、後からメンバー追加したのか以下のようなアホな事になっている件もたまにあるw
{
char ch0;
char ch1;
char dummy[3];
int int0;
} 「メルキオール (MELCHIOR)」「バルタザール (BALTHASAR)」
「カスパー (CASPER)」 という3つの独立したシステムによる
合議制をとっていた。 >>202
チェッサムやパリティビット的な用途なんか
なぜチェックサムを使わないのか……って聞くのは野暮?
>>192>>204
パックや共用体にしとこうぜ >>204
コンパイラによるけど警告レベル上げれば検出できるぞ >>200
3箇所の変数で多数決だった。3つ違う場合はアプリ終了。
ちなみにOSのメモリ保護された普通なプログラムです。
組み込みマイコンでも、DMAアクセスされるメモリーでも有りません。 >>203
>>アプリケーションからは気にせずに済んでるだけで……
なのでOSで保護された上で動くアプリケーションはデータの冗長化は必要ないのでは?
というか、データを冗長化するという方針で作られたシステムもいわゆる神経質という
設計思想があり。 データ認定?された物は3重化されて変数に保管されるが
ループのi,j,kや一時的にデータを取得するaと言った変数は多重化されていなかった。
仮に変数の冗長化が重要だと言うことになった場合。なぜデータ認定されたものだけ冗長化され
ループ変数は冗長化せずに単体で使うのかって部分に気持ち悪さを感じる。
どの変数も「平等に」食らう可能性はあると思うから。 >>201
「変数をバイナリーでファイルに出力する」と聞いてリーダーに求めたのは
メモリーマップ。当然、準備しているものだと思っていた。
ただ、メモリーマップなど知らないし意味も分からない、サンプルを見てくれ?
という指示だった。
バイナリーでのデータ交換をするのにメモリーマップを定めないことに関しては今でも気持ち悪さを感じている。 >>209
> OSで保護された上で動くアプリケーションは
OS では解決できない。 だからリーナスはインテルにマジムカという話。
変数を何か所にも増やすのが意味ないというのは、それはそう。
どうやったってどうにもならんので考えるだけ無駄。
職人技でどうにかできるような規模の小さな組み込み分野ならともかく、
複雑高度なコンピュータで低レイヤが信頼できないなら信頼できない箇所にコストをかけて改善すべき。 >>208
それなら気持ちは分かる
意味は解せぬが >>209
ハードウェアエラーが原因ならプログラムの実行バイナリも平等に化ける可能性あるよね >>204
そんなんテスト通るんか?
テストもせずに放置? アポロ計画だったか、3台のコンピュータに計算させて多数決で決めてたな それとCのようにロジックが追え、時間さえあれば
人でも答えが出せる場合ではなく、
AIシステムのようにブラックボックス化していると、
複数台による合議制もあるかも知れない 深層学習ってニューラルネットワークを多層構造にしたモデルなんだよね。
AI の仕組みの中に (重みづけされた) 多数決が既に組み込まれていると言えなくもない。 >>215
それなら分かる。計算結果を多数決専用のワイヤロジックで決め
また、3台の入力に戻して計算していれば。 >>213
そう思う。本当に冗長化するなら、ストアされているプログラムも
カウンタ変数も信頼性は平等だと思う。
レジスタに収納されてればより信頼性が上がると言われそうだが、
ループ変数だってメモリに退避されていることが多いから信頼性は同じだと思う。 >>220
この場合は出来るハードウェアもあるってのは反論にならないんだよ。
出来ないハードウェアが世の中に溢れてるってのが問題なんだから。 >>221
溢れてて何が問題なんだ?
リーナスは金持ってるかもしれんが世の中ECCマシンを買える奴ばかりじゃないんだし >>222
安ければデメリットもあるのもしょうがないといえばしょうがない話ではあるけどね。
デメリットはない (あるいは十分に小さい) と言い張ってインテルは ECC なし製品を推し進めたけど、
実際にはそうじゃないことがわかったとなれば文句を言いたくもなるだろう。 >>223
> 実際にはそうじゃないことがわかったとなれば文句を言いたくもなるだろう。
そうじゃないと言ってるけど実際どれくらいあるんだ?
仕事で上に書いたようなサーバーでシステム構築してたけどメモリーエラーなんて見たことないし >>210
WindowsでのBITMAPFILEHEADERとか主要なファイルヘッダの構造は全部構造体で定義されてるから気にしたことないなあ
ダミーパディング込みで
気になるなら定義部分を見てみるといい
余計なパディング入れないプラグマとかも使われているはず >>226
> それは、Microsoftは今調べているドライバの問題から、Windows XPのシステム・アプリケーションのクラッシュの主な原因がメモリの欠陥にあることを知ったからだ。
えっ?w リーナスもMSもECCメモリーがーって騒ぐ前にやることがあるだろw >>224
20年間プログラマをやってたが、勝手に値が変わっていたことは一度もない。
(全てOSで保護されたアプリケーションレベルの経験) 昔宇宙線だかなんだかがメモリデータを破壊するとかそんな記事を読んだ気がそこはかとなく(ry >>229
万が一書き換わってても気付かない事の方が多いだろ >>229
勝手に書き換わっているかどうかどうやって検証したんだ?
ICE とかでわかるもんなの? Windowsならメモリーエラーでブルースクリーンになるよな ならねえよ
「何となく調子悪い」PCがmemtest86で問題発覚はあるけどな 再現性がまったくないから
端子の接触不良か熱暴走で片付けられると思う Cで変数を分散するって話は人生で一回だけ。
それ以降は、どの言語でも意図して変更していなければ絶対に変数は変わらないという事を前提にコーディングしている。
でも、問題は無い。 >>231
ケースをシールドしたり、信号ラインを絶縁して対応していると思われる。 while(1){
a(&x);
if(x==1) break;
b();
}
ってコーディングを禁止している案件もあった。break文じゃなく、しっかりと条件式がfalseになるように書けとのことだった。
ただ、このやり方って入門書にも書いてある普通な方法だと思うけど。ループの中間くらいで抜けたいときはその後の処理をifで実行できないようにしていたので可読性は悪かった。
もちろん continue も封印されていた。
while(x!=1){
a(&x);
if(x!=1) b();
}
みたいに inline関数ならオーバーヘッドもないはずだしな
それで何が聞きたいんじゃ? 俺なら
a(&x);
while(x == 1){
b();
a(&x);
}
って書くかな 理不尽だと思われるコーディングルールも歴史があってそうなってたりするから
長いものに巻かれるしかない 俺なら嫌味のように
{
bool need_break=false;
while(... && !need_break){
//前半処理
if(!(need_break=...)){
//後半処理
}
}
}
とでも書くかな あと制御文の括弧前にスペース入れるのは少数派なんだろうか… 個々の場面で不適切でもチーム全員が適切に判断できないようなら
比較的マシそうな側に統一するルールにも合理性はある。
それとか「わからんかったらこうしとけ」みたいな口伝が
拡大解釈されていったりするのもありそうな話だ。 >>249
Google のガイドでは if や for の後に空白を入れる方針になってる。
私は入れないけど。 >>247
それが無限ループになるというのが読み取り難いからではないかな。
初期化やインクリメントの部分を省略したら単に何もしないのだろうと類推できるけど、
省略された条件式が真に判定されるのか偽に判定されるのかというのは例外規則として知ってなきゃわからない。
慣用句 (イディオム) と化してるので慣れてしまうと普通に読めるけど、
while(1) のほうが明示的な分だけわかりやすいのかもしれない。 関数っぽさをなくすためにスペース空ける方がいいと思う >>252
while文で無限ループを作れるにも関わらず
Rustではloop式を導入してる点が興味深いよね
loop文ではなくloop式なのでbreak 値; で値を返せて凄く便利 >>254
ループとしては無限ループにしておいて break で抜けるような構造は良くないといえばあんまり良くないんだけど、
そうは言っても存在する以上はその場合を明示的に表せるような構文があると便利ってのも確かで、
そのあたりは設計思想の違いとしか言いようがないよな……。 >>255
その構造が良くないというのは自分も昔はそうだったが単なる思い込みだと気付いたw
さらにwhileは先頭でif break、do whileは最後でif breakしているだけであるし
ある程度計算を進めてから途中でようやくif breakできないとwhileだと困ることもloop式で解決された
さらにループを抜けてbreakしたい時は値を返したい時が多いからloop文ではなくloop式が非常に便利 >>251,249
K&R2 では if や for, switch の後にはスペースを入れますね
私は基本的に K&R2 に沿って書きますが、関数の始まりを示す { は例外的に宣言に続けて書きます
int main() {
return 0;
} >>247
俺は使うよ
while(1) だと警告出るから >>260
こう書くとbreakやgotoが嘘つきに見えるな #define DEATH_MARCH for(;;) #define DISALLOW_BREAK for(;;)
#define FOREVER for(;;)
DISALLOW_BREAK FOREVER{
...
if(...)break;
} >>261
正規表現ライブラリとか使って、複雑な制御を見えなくするのも一つの手かも?
自分のところのbreak continueは嫌がられるが、他所なら見えないので。
結局機械語になれば、中でGoto(jmp)しまくっているわけなので。 ■ このスレッドは過去ログ倉庫に格納されています