Morris Keesan said:
@gmail.com> wrote:
struct object {
char pattern[MAX_PATTERN_SIZE];
char value[MAX_VALUE_SIZE];
};
int main(void) {
struct object (*all)[2500];
all = malloc(sizeof *all);
This is a very strange construct. I've never yet encountered code where
declaring something as a "pointer to array" is actually a good
idea.
It's certainly odd in this context, but it is surprisingly common in
some programming domains.
... ... ...
I agree to disagree, since "odd" is subjective.
If you have:
and you have N_OBJECTS throughout your code, you need to search-and-
replace if you ever decide to change the name of 'N_OBJECTS'. Not so
with 'sizeof'. If the value doesn't fit within a 'size_t', it's
trouble anyway because the C Standard Library memory management
functions take a 'size_t' argument.
The tradition of using preprocessor macros for constant expressions is
perhaps well-established and does serve to document a programmer's
choices, but if we see:
for (index = 0; index < N_OBJECTS; index++) {
all[index].pattern = 'c';
/* ... */
is it _immediately_ apparent that this loop will iterate through every
valid element that 'all' points to? Compare with 'all' as a pointer
to an array:
for (index = 0; index < (sizeof *all / sizeof **all); index++) {
(*all)[index].pattern = 'c';
/* ... */
Or use '/ sizeof (*all)[0]' if you like.
Now speaking of macros, perhaps you have some macros which expand to
common iteration logic that you enjoy:
#define FOR_EACH_ELEMENT(index_, total_) \
for ((index_) = 0; (index_) < (total_); (index_)++)
Compare to:
#define FOR_EACH_ELEMENT(index_, array_) \
for ((index_) = 0; (index_) < (sizeof (array_) / sizeof *(array_));
(index_)++)
Why should we need to repeatedly insist on the total number of
elements when that should be a property of an array itself?
FOR_EACH_ELEMENT(i, N_OBJECTS) {
or for a VLA:
FOR_EACH_ELEMENT(i, num_of_elements) {
Versus with arrays, it's the same in both cases:
FOR_EACH_ELEMENT(i, my_array) {
Or for an allocated array via pointer-to-array:
FOR_EACH_ELEMENT(i, *my_array) {
Now please consider:
typedef int ten_ints[10];
/* Called using 'fill(some_array, 5);' */
void fill(ten_ints my_array, int pattern) {
size_t i;
/* Uh oh, my_array is a pointer, not an array! */
for (i = 0; i < (sizeof my_array / sizeof my_array[0]); i++) {
my_array
= pattern;
}
return;
}
Compare to:
/* Called using 'fill(&some_array, 5);' */
void fill(ten_ints *my_array, int pattern) {
size_t i;
for (i = 0; i < (sizeof *my_array / sizeof **my_array); i++) {
/* *my_array is an array */
(*my_array) = pattern;
}
return;
}
Please ponder:
#define NUM_OF_ELEMENTS(array_) \
(sizeof (array_) / sizeof *(array_))
Well that's kind of handy.
int foo[20];
int (*bar)[30];
/* ... */
.... NUM_OF_ELEMENTS(foo) ...
/* ... */
.... NUM_OF_ELEMENTS(*bar) ...
Anyway, it's most likely a matter of preference, but not really
odd. My suggestion simply keeps the number of elements tagging
along with the type. Because arrays "decay" to pointers outside of
'sizeof' and '&', we have to keep typing the number of elements.
My preference is to keep things in one place.
If you really want to use 'N_OBJECTS' to document a choice, it's still
possible to use it along with 'sizeof' logic and pointers-to-arrays:
int main(void) {
struct object (*all)[N_OBJECTS];
all = malloc(sizeof *all);