How Would You Get the Count of an Array in C++?

本文探讨了在C++中准确获取数组元素数量的有效方法。通过比较不同宏定义和技术,如使用模板函数和编译期常量计算,解决了常见问题,并提供了适用于不同类型和上下文的实用解决方案。

The question is simple: given a C++ array (e.g. x as in int x[10]), how would you get the number of elements in it?

 

An obvious solution is the following macro (definition 1):

 

#define countof( array ) ( sizeof( array )/sizeof( array[0] ) )

 

I cannot say this isn’t correct, because it does give the right answer when you give it an array.  However, the same expression gives you something bogus when you supply something that is not an array.  For example, if you have

 

    int * p;

 

then countof( p ) always give you 1 on a machine where an int pointer and an int have the same size (e.g. on a Win32 platform).

 

This macro also wrongfully accepts any object of a class that has a member function operator[].  For example, suppose you write

 

class IntArray

{

private:

    int * p;

    size_t size;

 

public:

    int & operator [] ( size_t i );

} x;

 

then sizeof( x ) will be the size of the x object, not the size of the buffer pointed to by x.p.  Therefore you won’t get a correct answer by countof( x ).

 

So we conclude that definition 1 is not good because the compiler does not prevent you from misusing it.  It fails to enforce that only an array can be passed in.

 

What is a better option?

 

Well, if we want the compiler to ensure that the parameter to countof is always an array, we have to find a context where only an array is allowed.  The same context should reject any non-array expression.

 

Some beginners may try this (definition 2):

 

template <typename T, size_t N>

size_t countof( T array[N] )

{

    return N;

}

 

They figure, this template function will accept an array of N elements and return N.

 

Unfortunately, this doesn’t compile because C++ treats an array parameter the same as a pointer parameter, i.e. the above definition is equivalent to:

 

template <typename T, size_t N>

size_t countof( T * array )

{

    return N;

}

 

It now becomes obvious that the function body has no way of knowing what N is.

 

However, if a function expects an array reference, then the compiler does make sure that the size of the actual parameter matches the declaration.  This means we can make definition 2 work with a minor modification (definition 3):

 

template <typename T, size_t N>

size_t countof( T (&array)[N] )

{

    return N;

}

 

This countof works very well and you cannot fool it by giving it a pointer.  However, it is a function, not a macro.  This means you cannot use it where a compile time constant is expected.  In particular, you cannot write something like:

 

int x[10];

int y[ 2*countof(x) ]; // twice as big as x

 

Can we do anything about it?

 

Someone (I don’t know who it is – I just saw it in a piece of code from an unknown author) came up with a clever idea: moving N from the body of the function to the return type (e.g. make the function return an array of N elements), then we can get the value of N without actually calling the function.

 

To be precise, we have to make the function return an array reference, as C++ does not allow you to return an array directly.

 

The implementation of this is:

 

template <typename T, size_t N>

char ( &_ArraySizeHelper( T (&array)[N] ))[N];

 

#define countof( array ) (sizeof( _ArraySizeHelper( array ) ))

 

Admittedly, the syntax looks awful.  Indeed, some explanation is necessary.

 

First, the top-level stuff

 

char ( &_ArraySizeHelper( ... ))[N];

 

says “_ArraySizeHelper is a function that returns a reference (note the &) to a char array of N elements”.

 

Next, the function parameter is

 

T (&array)[N]

 

which is a reference to a T array of N elements.

 

Finally, countof is defined as the size of the result of the function _ArraySizeHelper.  Note we don’t even need to define _ArraySizeHelper(), -- a declaration is enough.

 

With this new definition,

 

int x[10];

int y[ 2*countof(x) ]; // twice as big as x

 

becomes valid, just as we desire.

 

Am I happy now?  Well, I think this definition is definitely better than the others we have visited, but it is still not quite what I want.  For one thing, it doesn’t work with types defined inside a function.  That’s because the template function _ArraySizeHelper expects a type that is accessible in the global scope.

 

I don’t have a better solution.  If you know one, please let me know.

 

BTW, how does one solve the same problem for C#?  It’s trivial there.  You just say “x.length()” (I forgot the actual syntax, but you should get the idea).

 

One thing to point out is that, in C# the count of an array is not in its type.  You decide how many elements are there when you create the array at run time.  Therefore, don’t even try to derive this information at compile time.

struct JitterBuffer_ { spx_uint32_t pointer_timestamp; /**< Timestamp of what we will *get* next */ spx_uint32_t last_returned_timestamp; /**< Useful for getting the next packet with the same timestamp (for fragmented media) */ spx_uint32_t next_stop; /**< Estimated time the next get() will be called */ spx_int32_t buffered; /**< Amount of data we think is still buffered by the application (timestamp units)*/ spx_uint32_t buffer_size; JitterBufferPacket packets[SPEEX_JITTER_MAX_BUFFER_SIZE]; /**< Packets stored in the buffer */ spx_uint32_t arrival[SPEEX_JITTER_MAX_BUFFER_SIZE]; /**< Packet arrival time (0 means it was late, even though it's a valid timestamp) */ void (*destroy) (void *); /**< Callback for destroying a packet */ spx_int32_t delay_step; /**< Size of the steps when adjusting buffering (timestamp units) */ spx_int32_t concealment_size; /**< Size of the packet loss concealment "units" */ int reset_state; /**< True if state was just reset */ int buffer_margin; /**< How many frames we want to keep in the buffer (lower bound) */ int late_cutoff; /**< How late must a packet be for it not to be considered at all */ int interp_requested; /**< An interpolation is requested by speex_jitter_update_delay() */ int auto_adjust; /**< Whether to automatically adjust the delay at any time */ struct TimingBuffer _tb[MAX_BUFFERS]; /**< Don't use those directly */ struct TimingBuffer *timeBuffers[MAX_BUFFERS]; /**< Storing arrival time of latest frames so we can compute some stats */ int window_size; /**< Total window over which the late frames are counted */ int subwindow_size; /**< Sub-window size for faster computation */ int max_late_rate; /**< Absolute maximum amount of late packets tolerable (in percent) */ int latency_tradeoff; /**< Latency equivalent of losing one percent of packets */ int auto_tradeoff; /**< Latency equivalent of losing one percent of packets (automatic default) */ int lost_count; /**< Number of consecutive lost packets */ }; /** Based on available data, this computes the optimal delay for the jitter buffer. The optimised function is in timestamp units and is: cost = delay + late_factor*[number of frames that would be late if we used that delay] @param tb Array of buffers @param late_factor Equivalent cost of a late frame (in timestamp units) */ static spx_int16_t compute_opt_delay(JitterBuffer *jitter) { int i; spx_int16_t opt=0; spx_int32_t best_cost=0x7fffffff; 为什么 compute_opt_delay(JitterBuffer *jitter)中JitterBuffer 显示变量“JitterBuffer ”不是类型名
09-11
在 `compute_opt_delay(JitterBuffer *jitter)` 函数中出现“变量‘JitterBuffer ’不是类型名”的错误,通常是因为编译器无法识别 `JitterBuffer` 类型。以下是可能的解决办法: ### 1. 检查 `JitterBuffer` 类型定义 确保 `JitterBuffer` 类型已经被正确定义。一般来说,`JitterBuffer` 会被定义为一个结构体类型。例如: ```c typedef struct JitterBuffer { // 结构体成员 int delay_step; int concealment_size; // 其他成员... } JitterBuffer; ``` 要保证这个定义在 `compute_opt_delay` 函数声明或定义之前出现。 ### 2. 检查头文件包含 如果 `JitterBuffer` 类型定义在头文件中,需要确保该头文件被正确包含。例如: ```c #include "jitter_buffer.h" // 假设 JitterBuffer 定义在 jitter_buffer.h 中 void compute_opt_delay(JitterBuffer *jitter) { // 函数实现 } ``` ### 3. 命名空间问题(如果是 C++) 如果是在 C++ 环境中,要确保没有命名冲突。可以使用命名空间来避免冲突。例如: ```cpp namespace speex { typedef struct JitterBuffer { // 结构体成员 int delay_step; int concealment_size; // 其他成员... } JitterBuffer; } void compute_opt_delay(speex::JitterBuffer *jitter) { // 函数实现 } ``` ### 4. 编译顺序问题 确保包含 `JitterBuffer` 类型定义的文件在编译时先被处理。如果使用 Makefile 或其他构建系统,要检查编译规则是否正确。 ### 示例代码 以下是一个完整的示例,展示了如何正确定义和使用 `JitterBuffer` 类型: ```c // jitter_buffer.h #ifndef JITTER_BUFFER_H #define JITTER_BUFFER_H typedef struct JitterBuffer { int delay_step; int concealment_size; } JitterBuffer; void compute_opt_delay(JitterBuffer *jitter); #endif // jitter_buffer.c #include "jitter_buffer.h" void compute_opt_delay(JitterBuffer *jitter) { // 函数实现 // 例如: jitter->delay_step = 10; } // main.c #include "jitter_buffer.h" int main() { JitterBuffer jitter; compute_opt_delay(&jitter); return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值