42

Androidのオープンソースqemuコードで、次のコード行に出くわしました。

machine->max_cpus = machine->max_cpus ?: 1; /* Default to UP */

これは紛らわしい言い方ですか?

if (machine->max_cpus) {
   ; //do nothing
} else {
 machine->max_cpus = 1;
}

もしそうなら、それは次のように明確ではないでしょうか:

if (machine->max_cpus == 0) machine->max_cpus = 1;

興味深いことに、これはgccで正常にコンパイルおよび動作しますが、 http://www.comeaucomputing.com/tryitout/ではコンパイルされません。

4

7 に答える 7

53

これは、Cのあいまいな拡張としてGNUで許可されています

5.7オペランドが省略された条件

条件式の中央のオペランドは省略できます。次に、最初のオペランドがゼロ以外の場合、その値は条件式の値です。

したがって、式

 x ? : y

ゼロ以外の場合、xの値があります。それ以外の場合、yの値。

この例は完全に同等です

 x ? x : y

この単純なケースでは、中間オペランドを省略する機能は特に有用ではありません。これが役立つのは、最初のオペランドに副作用が含まれている場合、または含まれている可能性がある場合です(マクロ引数の場合)。次に、中央でオペランドを繰り返すと、副作用が2回実行されます。中央のオペランドを省略すると、再計算による望ましくない影響なしに、すでに計算された値が使用されます。

ご想像のとおり、読みやすさと移植性の理由から、これを避けることをお勧めします。このような文法に互換性のないCの拡張機能を見て、私は正直驚いています。

于 2010-05-10T20:41:07.557 に答える
10

これはGCC拡張であり、「条件が真の場合はそれを使用し、そうでない場合はこの他の値を使用する」ことを意味します。

machine->max_cpus = machine->max_cpus ?: 1;

の省略形です

machine->max_cpus = machine->max_cpus ? machine->max_cpus : 1;

ただし、条件に副作用がある場合は、1回だけ実行されます。

于 2010-05-10T20:41:12.863 に答える
6

gccの-pedanticフラグを使用すると、

foo.c:5:警告:ISO Cは、?:式の中間項を省略することを禁止しています

于 2010-05-10T20:44:20.353 に答える
3

これはGCC拡張機能であり、条件に副作用がある場合は、より面白くて便利になります。

この場合、はい、私はそれが何よりも曖昧であることに同意するでしょう。

于 2010-05-10T20:41:43.483 に答える
1

K&R BNFは、「?」の間に式が必要であることを示しています。と ":"。gccが診断なしでそれをコンパイルするべきではないと思います。

于 2010-05-10T20:37:48.213 に答える
0

これには別の便利なケースがあります。nilを返す可能性のある関数またはメソッドを呼び出すときに中間変数を削除するため、2回の呼び出しは避けたいと考えています。たとえば(Objective-C)、ファイルが存在する場合は配列に解凍し、存在しない場合は空の配列を返します。

- (NSArray*)hydrateBacklogFromFile:(NSString *path)
{
    NSArray *backlog = @[];
    NSData *backlogData = [NSData dataWithContentsOfFile:path];
    if (backlogData)
    {
        backlog = [NSKeyedUnarchiver unarchiveObjectWithData:backlogData] ?: backlog;
    }
    return backlog;
}

選択肢はそれほど簡潔ではありません。

- (NSArray*)hydrateBacklogFromFile:(NSString *path)
{
    NSArray *backlog = @[];
    NSData *backlogData = [NSData dataWithContentsOfFile:path];
    if (backlogData)
    {
        NSArray *tempArray = [NSKeyedUnarchiver unarchiveObjectWithData:backlogData];
        if (tempArray != nil)
        {
            backlog = tempArray;
        }
    }
    return backlog;
}

または、複数の返品などで醜い。

- (NSArray*)hydrateBacklogFromFile:(NSString *path)
{
    NSData *backlogData = [NSData dataWithContentsOfFile:path];
    if (backlogData)
    {
        NSArray *tempArray = [NSKeyedUnarchiver unarchiveObjectWithData:backlogData];
        if (tempArray != nil)
        {
            return tempArray;
        }
    }
    return @[];
}

ですから、私がかなり読みやすいと思うのは有用な糖衣構文です。欠点は

  • ポインタのboolへの暗黙の変換。これは長年のC規則ですが、ほとんどの現代言語では許可されておらず、移植作業が複雑になっています。

  • 他の人が言っているように、これも非標準の拡張機能であるため、移植性を考慮する場合は避ける必要があります。

于 2016-04-22T17:00:31.370 に答える
0

他の答えは、タイトルの質問に答えていないように感じます。また、タグもc考慮に入れています。したがって、私は別の答えを追加します。

この構文を使用して、コードがifステートメントで醜くなるのを防ぎます。

foo(1) == TRUE ?: error_quit("foo(1) failed");
foo(2) == TRUE ?: error_quit("foo(2) failed");
foo(3) == TRUE ?: error_quit("foo(3) failed");
foo(4) == TRUE ?: error_quit("foo(4) failed");

行の先頭に実際の関数呼び出しが表示されます。以下のバージョンと比較してください。先頭のバージョンifが関数呼び出しの直接表示を妨げています。

if (foo(1) == FALSE) error_quit("foo(1)" failed");
if (foo(2) == FALSE) error_quit("foo(2)" failed");
if (foo(3) == FALSE) error_quit("foo(3)" failed");    
if (foo(4) == FALSE) error_quit("foo(4)" failed");

またはさらに読みにくい:

if (foo(1) == FALSE){
  error_quit("foo(1)" failed");
}

if (foo(2) == FALSE){
  error_quit("foo(2)" failed");
}

if (foo(3) == FALSE){
  error_quit("foo(3)" failed");
}

if (foo(4) == FALSE){
  error_quit("foo(4)" failed");
}
于 2020-03-21T14:50:21.530 に答える