0

私は次のコードとその小さな部分が意味をなさなかったので、それは私が現在持っている知識を超えていたので、誰かが私のためにこの小さな問題を解決できるかどうか疑問に思っていました.

stack.h

#ifndef _STACK_H
#define _STACK_H

#include "lis1.h"

typedef List Stack ;
#define stack_init list_init
#define  stack_destroy list_destroy
#define stack_size(stack) (stack)->size
#define stack_is_empty(stack) ((stack)->size==0)?1:0

int stack_push(Stack*stack,void*data);
int stack_pop(Stack*stack,void**data); 

#endif

#define stack_is_empty(stack) ((stack)->size==0)?1:0以下のプログラムをコンパイルする際は注意してください。

#include<stdio.h>
#include"stack.h"

static char ams[5] = { 'h', 'e', 'l', 'l', 'o' };
void* data;
Stack stack;
char*ss;

void debug(int a)
{
    printf(" debug %d \n", a);
}

int main()
{
    stack_init(&stack, NULL);
    debug(1);
    int i;

    for (i = 0; i < 5; i++)
    {
        stack_push(&stack, (void*) (&ams[i]));
    };
    debug(2);
    while (printf("checker\n") && stack_is_empty(&stack) != 1)
    {
        printf("->");
        printf("[%d  ", stack_size(&stack));
        stack_pop(&stack, &data);
        printf("%c]", *((char*) data));
        printf("--\n");
    };
    return 0;
}

私はこれを得る

debug 1   debug 2  checker
->[5  o]-- checker
->[4  l]-- checker
->[3  l]-- checker
->[2  e]-- checker
->[1  h]-- checker
segmentation fault

しかし、に変更#define stack_is_empty(stack) ((stack)->size==0)?1:0 すると#define stack_is_empty(stack) (((stack)->size==0)?1:0)、セグフォルトはありません

マイクエリ

条件が「1」を吐き出すまで、前者のケースでプログラムが完全に正常に機能したのはなぜですか..後者が機能する理由を理解しているようです。

4

3 に答える 3

3

C では、マクロは、期待どおりの式を生成するかどうかに関係なく、単にテキストで置換されます。

括弧がないと、whileループ条件は次のように展開されます。

printf("checker\n")&&((&stack)->size==0)?1:0!=1

次のように解釈されます。

(printf("checker\n") && ((&stack)->size==0)) ? 1 : (0 != 1)

したがってprintf、この三項式の条件の一部になりますが、それは問題を引き起こしません。出力されたバイト数を返します。これは、ゼロでない限り真と解釈されます。次に、実際の状態で、スタック サイズをチェックする部分を実行します。スタック サイズが 0 の場合、これは 1 または true を返します。スタック サイズが 0 でない場合、これは の結果を返します(0 != 1)。これは常に true です。したがって、この条件は常に真の値を返し、while ループは、スタック上の項目がなくなった後でも継続します。

括弧を追加すると、期待どおりに解釈されます。

printf("checker\n") && ((((&stack)->size==0)) ? 1 : 0) != 1)

式に展開されるマクロを作成するときは、結果が単一の式として解釈されるように、演算子の優先順位規則によって式が意図したものとは異なる方法で解釈される可能性があるのではなく、常に結果を括弧で囲む必要があります。

このステートメントには多くの冗長性があることに注意してください。ブール式の値をチェックして、(&stack)->size==0真かどうかを確認し、真の場合は 1 を返し、そうでない場合は 0 を返します。しかし、==それが true の場合は 1 を返し、そうでない場合は 0 を返しました。三項演算子は必要ありません。次に!= 1、それが偽かどうかを確認します。しかし、ブール式からどのように false を取得するのでしょうか? not 演算子を使用するだけ!です。!= 1三項演算子と比較を完全にスキップできます。

#define stack_is_empty(stack) ((stack)->size==0)

while (!stack_is_empty(&stack)) {
    // ...
}
于 2013-10-22T03:17:53.853 に答える
2

C マクロは単なるテキスト置換であり、式の評価ではないことに注意してください。

umbra キー付きの stack_is_empty を使用すると、if 条件は次のようになります。

While (printf("checker\n") && ((&stack)->size)==0)?1:0!=1) {

問題は、!= 演算子の優先度が高いため、事実上次のようになることです。

While (printf("checker\n") && ((&stack)->size)==0)?1:  (0!=1)  ) {

0 != 1 なので、その while ループはスタックのサイズを超えて進み続けます。

于 2013-10-22T03:27:44.843 に答える
1

マクロ置換後

printf("checker\n")&&stack_is_empty(&stack)!=1

になる

printf("checker\n")&&((&stack)->size==0)?1:0!=1

三項演算子?:の優先順位はかなり低いため、これは次と同等です。

(printf("checker\n") && ((&stack)->size==0)) ? 1: (0 != 1)

printf("checker\n")常に真の値を返すことに注意してください(出力する文字数を返すため)。そのため、(&stack)->size==0)ショートカット回路のためにチェックが評価されることはありません。

アドバイス: マクロ定義では常に十分な括弧を使用してください。

于 2013-10-22T03:17:41.357 に答える