簡體   English   中英

C中用於獲取/設置數組條目的宏

[英]Macros in C for getting/setting entries of an array

下面的小代碼輸入一個矩陣,該矩陣具有行優先順序和兩列。 據我了解,我所包含的宏將使我能夠以更自然的方式(從1索引開始)訪問數組的條目。

#define Alocal(i,j) Alocal[ (j-1) + 2*(i-1) ]

void test_function(double* Alocal)
{

    double a;
    double b;

    /* get first and second entries of the array */
    a = Alocal(1,1);
    b = Alocal(1,2);

}

現在我有兩個問題:1)說我將整數Ncols傳遞給函數。 可以在宏中使用它以獲得更籠統的定義嗎? 喜歡

#define Alocal(i,j) Alocal[ (j-1) + Ncols*(i-1) ]

2)有沒有一種方法可以定義將數組的特定條目設置為宏中的某個數字? 喜歡

double d;

d = 3.4579;

Alocal(2,2) = d;

宏提示

  • 將宏參數(可以是表達式)括在括號中,以避免運算符優先級引起的錯誤。
  • 不要“陰影”變量和其他名稱
  • 在您的情況下,應該為數組指定一個額外的參數,以實現這些宏的表達代碼和可重用性
  • 宏名稱應按照慣例全部大寫
  • 要隔離宏表達式(類似於函數“返回” void方式),可以至少將MY_ARRAY_SET宏放入do{ ... }while(0)塊中。
  • 如果可能的話,應該改而執行常規功能(也可以內聯它們)...更少的錯誤發生,更少的麻煩,類型安全


我沒有特別注意您的索引計算,請參閱Ben的答案

#include <stdlib.h>

#define MY_ARRAY_GET(array,i,j) ( (array)[ ((j)-1) + 2*((i)-1) ] )
#define MY_ARRAY_SET(array,i,j,x) ( (array)[ ((j)-1) + 2*((i)-1) ] = (x) )

void test_function(double* Alocal)
{
    double a;
    double b;

    /* get first and second entries of the array */
    a = MY_ARRAY_GET(Alocal, 1, 1);
    b = MY_ARRAY_GET(Alocal, 1, 2);

    MY_ARRAY_SET(Alocal, 1, 2, 42.0);
}

int main(int argc, char** argv)
{
    double array[16] =
    { 1.0, 2.0 }; //Initialize to 1.0,2.0,0.0 ...

    test_function(array);

    return EXIT_SUCCESS;
}


例子2

試圖將想法和其他答案和評論(VLA和斷言)放在一起。 免責聲明:這是一種概念證明,而不是我如何編寫高效的代碼。

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

//********************************************************************************
//Utility macros

/**
 * @brief           Internal macros to retrieve the dynamic size of a VLA dimension 1
 * @param   vla     A pointer to the VLA is need in order to properly deduce its dimensions
 * @warning         for internal use only
 */
#define INTERNAL_VLA_SIZE_D1(vla) (sizeof((*vla))/sizeof((*vla)[0]))

/**
 * @brief           Internal macros to retrieve the dynamic size of a VLA dimension 2
 * @param   vla     A pointer to the VLA is need in order to properly deduce its dimensions
 * @warning         for internal use only
 */
#define INTERNAL_VLA_SIZE_D2(vla) (sizeof((*vla)[0])/sizeof((*vla)[0][0]))

//********************************************************************************
//Debug check helper

int is_valid_index_2D(const size_t nrows, const size_t ncols, const size_t row, const size_t col)
{
    return (nrows) && (ncols) && ((size_t) (row) < (size_t) (nrows)) && ((size_t) (col) < (size_t) (ncols));
}

//********************************************************************************
//1 based index loop macros

#define FOR_RANGE(type,var,start,end) \
        for(type var = (start);var <= (end);++var)

#define FOR(type,var,count) \
        FOR_RANGE(type,var,1,count)

#define MAT_FOREACH_INDEX(rowvar,colvar,mat) \
        FOR(size_t,rowvar,INTERNAL_VLA_SIZE_D1(&(mat))) \
        FOR(size_t,colvar,INTERNAL_VLA_SIZE_D2(&(mat)))

//********************************************************************************
//Matrix functions
//Note: These functions rely upon compiler warnings or errors regarding incompatible pointer types
//      (i.E. for a wrong number of array dimensions)

void mat_set(const size_t nrows, const size_t ncols, double (* const matrix)[nrows][ncols], const size_t row, const size_t col, const double value)
{
    assert(is_valid_index_2D(nrows,ncols,row,col));
    (*matrix)[row][col] = value;
}

double mat_get(const size_t nrows, const size_t ncols, double (* const matrix)[nrows][ncols], const size_t row, const size_t col)
{
    assert(is_valid_index_2D(nrows,ncols,row,col));
    return (*matrix)[row][col];
}

void mat_identity(const size_t nrows, const size_t ncols, double (* const matrix)[nrows][ncols], double value)
{
    for (int row = 0; row < nrows; ++row)
        for (int col = 0; col < ncols; ++col)
            (*matrix)[row][col] = row == col ? value : 0.0;
}

void mat_fprintf(FILE* stream, const char* const field_delim, const char* const field_format, const size_t nrows, const size_t ncols, double (* const matrix)[nrows][ncols])
{
    for (int row = 0; row < nrows; ++row)
    {
        for (int col = 0; col < ncols; ++col)
        {
            if (col)
                fputs(field_delim, stream);
            fprintf(stream, "%.2f", (*matrix)[row][col]);
        }
        fprintf(stream, "\n"); //write a line break, the portable way
    }
}

//Matrix utility macros, using 1 based indices
#define MAT_SET(mat,row,col,value) \
        mat_set(INTERNAL_VLA_SIZE_D1(&(mat)),INTERNAL_VLA_SIZE_D2(&(mat)),(&(mat)),(row)-1,(col)-1,value)

#define MAT_GET(mat,row,col) \
        mat_get(INTERNAL_VLA_SIZE_D1(&(mat)),INTERNAL_VLA_SIZE_D2(&(mat)),(&(mat)),(row)-1,(col)-1)

#define MAT_SET_IDENTIY(mat,value) \
        mat_identity(INTERNAL_VLA_SIZE_D1(&(mat)),INTERNAL_VLA_SIZE_D2(&(mat)),(&(mat)),value)

#define MAT_FPRINTF(stream,field_delim,field_format,mat) \
        mat_fprintf((stream),(field_delim),(field_format),INTERNAL_VLA_SIZE_D1(&(mat)),INTERNAL_VLA_SIZE_D2(&(mat)),(&(mat)))

#define MAT_PRINTF(field_delim,field_format,mat) \
        MAT_FPRINTF(stdout,(field_delim),(field_format),(mat))

int main(int argc, char** argv)
{
#ifdef NDEBUG
    fprintf(stderr, "assertions disabled\n");
#else
    fprintf(stdout, "assertions enabled\n");
#endif

//Note: since below VLA is created on stack, we must be cautious about its size
    for (size_t nrows = 1; nrows <= 5; ++nrows)
    {
        for (size_t ncols = 1; ncols <= 5; ++ncols)
        {

//Allocate the matrix on stack
            double matrix[nrows][ncols];

//Initialize matrix to identity
            MAT_SET_IDENTIY(matrix, 1.0);

            puts("****************************************");
//Print the matrix using a wrapped function
            MAT_PRINTF(", ", "%.2f", matrix);

            puts("****************************************");
//Print the matrix using nested loops with 1 based indices
            FOR(size_t,row,nrows)
                FOR(size_t,col,ncols)
                {
                    printf("%.2f", MAT_GET(matrix, row, col));
                    if (col < ncols)
                        printf(", ");
                    else
                        printf("\n");
                }

            puts("****************************************");
//Print the matrix using a 2D foreach with 1 based indices
            MAT_FOREACH_INDEX(row, col, matrix)
            {
                printf("%.2f", MAT_GET(matrix, row, col));
                if (col < ncols)
                    printf(", ");
                else
                    printf("\n");
            }

        }
    }

    return EXIT_SUCCESS;
}

您所說的所有內容都可以正常運行。 只需確保已將Ncols定義為宏或變量即可。

此方法有效,因為您的宏簡單地創建到原來的數組的元素的引用,並且所述代碼運行時,使用除了像test[1+2]將同樣的事情test[3]

結果,獲取和設置元素的工作方式都與硬編碼數組查找而不是使用宏的方式相同。 因此,再舉一個例子: Alocal(2,2) = d將有效地意味着(一旦您完成了減法和乘法運算,並假設NCol為2): Alocal[3] = d ,這是完全正常的C。

另外,作為一個旁注,如果知道預先需要多少列,也可以使用多維數組,假設是二維數組,則該數組會使您的宏看起來更像這樣:

#define Alocal(i,j) Alocal[ (j-1) ][ (i-1) ]

內置語言功能可解決此問題,因此您不應為此使用宏。

自1999年以來,C具有可變長度數組 VLA,可以完美地完成這項工作。 將原型更改為

void test_function(size_t ncol, double (*A)[ncol]);

然后,您的編譯器將完全知道如何獨自完成諸如A[i][j]類的索引計算。

在上面的原型,重要的是要有ncolA ,使得它在的參數說明點是已知的A

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM