1

我读到,在将数组作为参数传递时,我们还必须将其长度作为参数传递(严格 C89)。在这个给定的代码片段中

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>

#define NUM_RANKS 13
#define NUM_SUITS 4
#define NUM_CARDS 5

bool straight, flush, four, three;
int pairs;

void read_cards(int a[], int b[]);      // I did't specify the length in this declaration
void analyze_hand(int a[], int b[]);    // I did't specify the length in this declaration
void print_result(void);

int main()
{
    int num_in_rank[NUM_RANKS];
    int num_in_suit[NUM_SUITS];

    for(;;)
    {
        read_cards(num_in_rank, num_in_suit);
        analyze_hand(num_in_rank, num_in_suit);
        print_result();
    }
}

void read_cards(int num_in_rank[], int num_in_suit[])
{
    bool card_exists[NUM_RANKS][NUM_SUITS];
    char ch, rank_ch, suit_ch;
    int rank, suit;
    bool bad_card;
    int cards_read = 0;

    for(rank = 0; rank < NUM_RANKS; rank++)
    {
        num_in_rank[rank] = 0;
        for(suit = 0; suit < NUM_SUITS; suit++)
            card_exists[rank][suit] = false;        
    }

    for(suit = 0; suit < NUM_SUITS; suit++)
        num_in_suit[suit] = 0;

    while(cards_read < NUM_CARDS)
    {
        bad_card = false;

        printf("Enter a card: ");
        rank_ch = getchar();
        switch(rank_ch)
        {
            case '0':           exit(EXIT_SUCCESS);
                    case '2':           rank = 0; break;
                    case '3':           rank = 1; break;
                    case '4':           rank = 2; break;
                    case '5':           rank = 3; break;
                    case '6':           rank = 4; break;
                    case '7':           rank = 5; break;
                    case '8':           rank = 6; break;
                    case '9':           rank = 7; break;
                    case 't': case 'T': rank = 8; break;
                    case 'j': case 'J': rank = 9; break;
                    case 'q': case 'Q': rank = 10; break;
                    case 'k': case 'K': rank = 11; break;
                    case 'a': case 'A': rank = 12; break;
                    default:            bad_card = true;                        
         }

         suit_ch = getchar();
         switch(suit_ch)
         {
            case 'c': case 'C': suit = 0;  break;
            case 'd': case 'D': suit = 1;  break;
            case 'h': case 'H': suit = 2;  break;
            case 's': case 'S': suit = 3;  break;
            default:            bad_card = true;
         }

        while((ch = getchar()) != '\n')
            if(ch != ' ')
                bad_card = true;

        if(bad_card)
            printf("Bad card; ignored.\n");
        else if(card_exists[rank][suit])
            printf("Duplicate card; ignored.\n");
        else
        {
            num_in_rank[rank]++;
            num_in_suit[suit]++;
            card_exists[rank][suit] = true;
            cards_read++;
        }               
    }   
 }

void analyze_hand(int num_in_rank[], int num_in_suit[])
{
    int num_consec = 0;
    int rank, suit;

    straight = false;
    flush = false;
    four = false;
    three = false;
    pairs = 0;

    for(suit = 0; suit < NUM_SUITS; suit++)
        if(num_in_suit[suit] == NUM_CARDS)
            flush = true;
    rank = 0; 
    while(num_in_rank[rank] == 0)
        rank++;
    for(; rank < NUM_RANKS && num_in_rank[rank] > 0; rank++ )
        num_consec++;

    if(num_consec == NUM_CARDS)
    {
        straight = true;
        return;
    }

    for(rank = 0; rank < NUM_RANKS; rank++)
    {
        if(num_in_rank[rank] == 4)
        four = true;
        if(num_in_rank[rank] == 3)
        three = true;
        if(num_in_rank[rank] == 2)
           pairs++;     
    }       
}

我将两个数组num_in_rank[NUM_RANKS]num_in_suit[NUM_SUITS]一起传递给函数void read_cards(int a[], int b[])并且void analyze_hand(int a[], int b[])没有提供数组长度,我不知道这是如何工作的(没有任何警告/错误)?
任何想法是对还是错?

4

4 回答 4

2

当您在 c 中传递一个数组时,函数所看到的只是指向数组开头的指针,因此在函数内部看不到长度。

为了允许函数使用长度,您必须创建某种也包含长度的结构,在数组的有效输入末尾有某种特殊字符(如字符串的 '\0' ),或者明确地将大小作为函数参数传递。

如果您不传递长度,您将不会出现编译错误,但是您对数组执行的大多数操作都需要您知道它的长度,因此当您尝试时可能会遇到段错误在不知道数组长度的情况下对数组进行操作。

编辑:

发布完整代码后,这是因为您使用宏进行边界检查,这有点像传递大小,但灵活性较差。

于 2013-07-12T19:41:14.247 回答
2

我读到,在将数组作为参数传递时,我们还必须将其长度作为参数传递

这仅适用于函数不预先知道数组大小的情况。当函数知道数组必须有一定数量的元素时,您不需要将大小传递给函数。

这正是您的代码中发生的事情:接收数组的两个函数都知道num_in_rank确切包含NUM_RANKS元素,并且num_in_suit确切包含NUM_SUITS元素。这就是为什么您不需要传递大小,并且不会发生任何事情:该函数已经通过#defined 常量知道大小。

另一方面,如果您需要将一个大小未知的数组传递给您的函数,那么您需要执行以下操作之一:

  • 传递数组中的元素数量,或
  • 将指针传递给数组的最后一个元素(或最后一个元素),或者
  • 同意一个“哨兵”值,例如零或负值,这将指示数组中有效数据范围的结束。
于 2013-07-12T19:41:28.673 回答
1

请记住,C 中的数组本质上是指向程序留出的内存块开头的指针。所以当你说

int num_in_rank[NUM_RANKS];
int num_in_suit[NUM_SUITS];

您可以将它们用作指针。在实践中通常也将大小作为 int 传递,例如

void read_cards(int a[], int b[], int a_size, int b_size);
void analyze_hand(int a[], int b[], int a_size, int b_size);

如果您需要利用数组的大小。但是要回答你的问题,做你正在做的事情没有问题。

于 2013-07-12T19:45:01.233 回答
1

我在 Visual Studio 上尝试了编译器如何对各种函数声明进行名称修改 -

void foo(int a[]);
void foo(int a[10]);
void foo(int *a);

对于上述所有声明,重命名的名称是唯一的 -

?foo@@YAXPAH@Z

我对查看 MS 编译器将函数声明转换为 -

void __cdecl foo(int * const)

因此,至少它让我们了解到,在函数声明中使用负数组索引之前,MS 编译器并不担心。如果我没记错的话,g++ 也应该做这样的事情。

于 2013-07-12T20:07:46.717 回答