Inconsistencies

A

Anders Koeln

Could someone comment on this code please.

1. Are the comments in it correct?

2. Why does sizeof(arr) not work consistently in C? In someFunction()
sizeof(arr) means sizeof(&arr[0]) in main. The compiler knows how big arr
is, so why the difference - esp. as it would be v.useful if sizeof(arr)
worked in someFunction as it does in main - so you could use sizeof(arr)
in
the conditional bit of,say, a for loop in someFunction for example?

3. Why doesn't C allow testFunction to be declared char [] testFunction -
seems to me that if it did you could get away with knowing less about
pointers, and it seems as though again that C is incosistent, i.e., you
don't have to declare testFunction's parameter as char *. Surely, the
compiler should know that char [] someFunction actually means char *
someFunction??

char * testFunction(char []);

int main()
{
/* Lie to the compiler. Should be ok though as the compiler knows how
big the array really is.*/
char arr[7] = "0123456789";

/* Ouput the sizeof the array. And the size of a pointer to it. */
printf("main() sizeof arr = %d\n", sizeof(arr));
printf("main() sizeof arr = %d\n", sizeof(&arr[0]));

/* Pass it on to the function where we'll do some other emaningless
things.*/
testFunction(arr);
return(0);

}

/* Can't declare testFunction as char [] testF...() - even though the
compiler knows that array's are passed by address.*/

/* The size is ignored - and can't be checked anyway.*/
char * testFunction(char arr[500])
{
int n;

/* Same sizeof line of code that was in main() - note here that it
output the size of a pointer!*/
printf("sizeof arr = %d\n", sizeof(arr));

return(arr);

}
 
S

Seebs

Could someone comment on this code please.

Okay. Comment is: Read the FAQ's commentary on arrays and pointers.
1. Are the comments in it correct?

Probably not. :)
2. Why does sizeof(arr) not work consistently in C?

It does.

What you're confused by is that a declaration with apparent array type
does not work consistently in C.
In someFunction()
sizeof(arr) means sizeof(&arr[0]) in main. The compiler knows how big arr
is, so why the difference - esp. as it would be v.useful if sizeof(arr)
worked in someFunction as it does in main - so you could use sizeof(arr)
in the conditional bit of,say, a for loop in someFunction for example?

What is passed to the function is &arr[0]. That function could just as
well be called from another module, where it could be passed a dynamically
allocated pointer, or a different array, or anything else.

But sizeof is a compile-time constant. So functions that are declared as
though they took arrays actually take pointers, and sizeof(param) is the
size of the pointer.
3. Why doesn't C allow testFunction to be declared char [] testFunction -

Because there's no such thing as an array of unknown size, really, and thus
you can't return one.

Basically, the size of the thing returned is a compile-time constant,
again. It can't be a thing of inconsistent size.
seems to me that if it did you could get away with knowing less about
pointers, and it seems as though again that C is incosistent, i.e., you
don't have to declare testFunction's parameter as char *. Surely, the
compiler should know that char [] someFunction actually means char *
someFunction??

No. What it really *should* do, probably, is not allow the [] to be used in
function declarations, but:
1. That syntax is too old and too widely used to change.
2. It's actually used for optimizations in some compilers.
int main()
{
/* Lie to the compiler. Should be ok though as the compiler knows how
big the array really is.*/
char arr[7] = "0123456789";

It's not okay, because you're lying to the compiler. You're providing more
initializers for the array than it has. The compiler oughta kick you in
the teeth.
/* Can't declare testFunction as char [] testF...() - even though the
compiler knows that array's are passed by address.*/

"arrays". The plural does not take an apostrophe.

And you alread yhave the explanation here -- "passed by" address. Not
returned by. ;)
/* The size is ignored - and can't be checked anyway.*/
char * testFunction(char arr[500])

It can in C99, with "[static 500]" as a size. Compilers actually
use that.
/* Same sizeof line of code that was in main() - note here that it
output the size of a pointer!*/
printf("sizeof arr = %d\n", sizeof(arr));

Well, yes. You gave it a pointer.

Seriously, this stuff is exactly why the FAQ has a big section on
arrays and pointers.

-s
 
K

Keith Thompson

Anders Koeln said:
Could someone comment on this code please.

1. Are the comments in it correct?

2. Why does sizeof(arr) not work consistently in C? In someFunction()
sizeof(arr) means sizeof(&arr[0]) in main. The compiler knows how big
arr is, so why the difference - esp. as it would be v.useful if
sizeof(arr) worked in someFunction as it does in main - so you could
use sizeof(arr) in the conditional bit of,say, a for loop in
someFunction for example?

In fact, sizeof(arr) does work consistently in C; it gives you the size
in bytes of arr, whatever arr happens to be.
3. Why doesn't C allow testFunction to be declared char [] testFunction -
seems to me that if it did you could get away with knowing less about
pointers, and it seems as though again that C is incosistent, i.e., you
don't have to declare testFunction's parameter as char *. Surely, the
compiler should know that char [] someFunction actually means char *
someFunction??

char * testFunction(char []);

C allows you to declare what looks like an array parameter, but it's
really a pointer parameter. (I personally question whether this was a
good idea, but we're stuck with it for historical reasons.) It doesn't
do the same thing for return types -- possibly because very early C code
typically didn't bother to declare return types. Yeah, I suppose it's
inconsistent, but we have to live it with. (The way I live with it is
to declare pointers as pointers and not take advantage of the ability to
declare pointer parameters using array syntax.)
int main()
{
/* Lie to the compiler. Should be ok though as the compiler knows how
big the array really is.*/
char arr[7] = "0123456789";

What on Earth makes you think this should be ok? It's a constraint
violation; the compiler must complain about it, and may reject your
program. Either use the correct number or leave it out:

char arr[] = "0123456789";
/* Ouput the sizeof the array. And the size of a pointer to it. */
printf("main() sizeof arr = %d\n", sizeof(arr));

"%d" expects an int argument; you're giving it a size_t. You have the
bad luck to be using a system on which your error shows no visible
symptoms (i.e., it happens to "work"). For simplicity, since you're
dealing with small values, you can cast the argument to int:

printf("main() sizeof arr = %d\n", (int)sizeof(arr));
printf("main() sizeof arr = %d\n", sizeof(&arr[0]));

This gives you the size of a pointer to *the first element of* the
array, not of a pointer to to the entire array. They're likely to be
the same size, but it's an important distinction.
/* Pass it on to the function where we'll do some other emaningless
things.*/
testFunction(arr);

Since arr is an array expression, it decays to a pointer to the array
object's first element. (There are a handful of cases where this
doesn't happen; the argument to sizeof is one of them.)
return(0);

}

/* Can't declare testFunction as char [] testF...() - even though the
compiler knows that array's are passed by address.*/

testFunction returns a pointer to char. Why do you object to declaring
it that way?
/* The size is ignored - and can't be checked anyway.*/
char * testFunction(char arr[500])

So leave it out, or declare the parameter as "char *arr".

(You don't use n.)
/* Same sizeof line of code that was in main() - note here that it
output the size of a pointer!*/
printf("sizeof arr = %d\n", sizeof(arr));

Right, because arr is a pointer.
return(arr);

}

Arrays in C are not first-class objects. You cannot pass them as
function arguments or return them as function values (unless you
wrap them in a struct or union, but then they have to be fixed-size).

So if you want to deal with an array in a function, you'll typically
pass a pointer to its first element and then provide some other
way for the function to know how big it is. For strings, this is
the '\0' terminator. For other arrays, you'd typically pass an
additional size_t argument.

Read section 6 of the comp.lang.c FAQ, <http://www.c-faq.com/>. It does
a very good job of explaining the relationship betwen arrays and
pointers in C.

And if this stuff still seems inconsistent, well, it is. In spite of
the common misconception, arrays are not pointers and pointers are not
arrays. And yet there are some features of C that seem designed to
confuse you into thinking that they're the same thing.
 
J

Jens Thoms Toerring

Anders Koeln said:
Could someone comment on this code please.
1. Are the comments in it correct?

See below.
2. Why does sizeof(arr) not work consistently in C? In someFunction()
sizeof(arr) means sizeof(&arr[0]) in main. The compiler knows how big arr
is, so why the difference - esp. as it would be v.useful if sizeof(arr)
worked in someFunction as it does in main - so you could use sizeof(arr)
in the conditional bit of,say, a for loop in someFunction for example?

Because when you pass an array to a function what the function
receives isn't an array but a pointer to the first element of
the array. There's no way in C to pass arrays by value, only
"normal" types and structures can be passed by value, arrays
only by reference. And the compiler typically has no chance
at all to figure out how large the array was. As long as you
have just a single source file program it perhas could be done,
but the moment the function you call on an array is in a dif-
ferent source file things become impossible. Each file gets
compiled on is own, without any knowledge of what is in other
source files. So if you define an array in file A.c and pass
it to a function in file B.c the compiler has not a bit of
chance to find out how large the array is (A.c may not even
have been compiled at all when B.c is compiled).
3. Why doesn't C allow testFunction to be declared char [] testFunction -
seems to me that if it did you could get away with knowing less about
pointers, and it seems as though again that C is incosistent, i.e., you
don't have to declare testFunction's parameter as char *. Surely, the
compiler should know that char [] someFunction actually means char *
someFunction??
char * testFunction(char []);

I guess you mean why you can't write

char [] testFunction(char []);

As you can't pass arrays to function you also can't return arrays
from functions. And there doesn't seem to be any reason to add it
a special notation for something that doesn't work. If you don't
get used to pointers C probably won't become your favourite
language;-)
int main()

int main(void)
{
/* Lie to the compiler. Should be ok though as the compiler knows how
big the array really is.*/
char arr[7] = "0123456789";

No. If you explicitly tell the compiler that the array is supposed
to have 7 elements then it will have 7 elements and the excess ini-
tializers will get discarded. So you will end up with a char array
with 7 elements and no trailing '\0' at the end, thus the array
can't be used as a string.
/* Ouput the sizeof the array. And the size of a pointer to it. */
printf("main() sizeof arr = %d\n", sizeof(arr));

The sizeof() results in a value of type 'size_t' which is a
different type from 'int', thus you shouldn't use "%d". 'size_t'
is an unsigned integer value, and as long as you don't have a C99
compiler the best way is probably to use

printf("main() sizeof arr = %lu\n", (unsigned long) sizeof(arr));

to make sure printf() gets what it expects.
printf("main() sizeof arr = %d\n", sizeof(&arr[0]));
/* Pass it on to the function where we'll do some other emaningless
things.*/
testFunction(arr);
return(0);

return 0;

will do since 'return' is not a function.
/* Can't declare testFunction as char [] testF...() - even though the
compiler knows that array's are passed by address.*/

It's simply not part of the language.
/* The size is ignored - and can't be checked anyway.*/
char * testFunction(char arr[500])

Yes, using 'char arr[500]' doesn't make the compiler handle
'arr' any differently from having used 'char *arr' instead.
Using '[]' with a size for an array parameter actually only
makes sense (and is necessary) when you pass more-dimensional
arrays, otherwise it has no effect at all. (and even then you
still have to pass the dimensions of the array separately.)
/* Same sizeof line of code that was in main() - note here that it
output the size of a pointer!*/
printf("sizeof arr = %d\n", sizeof(arr));
return(arr);
}
Regards, Jens
 
K

Keith Thompson

/* Lie to the compiler. Should be ok though as the compiler knows how
big the array really is.*/
char arr[7] = "0123456789";

No. If you explicitly tell the compiler that the array is supposed
to have 7 elements then it will have 7 elements and the excess ini-
tializers will get discarded. So you will end up with a char array
with 7 elements and no trailing '\0' at the end, thus the array
can't be used as a string.

No, as I mentioned elsethread, this is a constraint violation. C99
6.7.8p2:

Constraints

No initializer shall attempt to provide a value for an object
not contained within the entity being initialized.

As a special case, if you wrote:
char arr[7] = "0123456";
then you'd get an array with no '\0' terminator; that applies only
when the length of the initializing string literal is exactly the
same as the declared size of the array. (I question whether this
rule is a good idea. If you really want to take advantage of it,
you have to count the characters in the literal, something that
should be left to the compiler. A syntax for a string literal that
doesn't specify the trailing '\0' would have been a better solution.)

Now, the fact that it's a constraint violation merely means that
the compiler must issue a diagnostic; it may or may not reject the
translation unit. If it chooses not to reject it, the behavior
of the resulting program is undefined. It might just store 7
characters in arr, or it might try to copy all 10 or 11 characters
into the 7-character array. Or it could even ignore the [7] and
make arr bigger. Or it could make demons fly out of your nose
(at least, if it managed to do so it wouldn't violate the standard).

[...]
 
S

Seebs

Note that my compiler issues a warning:

'arr' : array bounds overflow

and then does that you said -- creates an array of 7 chars. Those chars are
"0123456" _without_ any '\0' terminator. I'm not sure if the Standard
defines what to do here.

I'm not either, off the top of my head. Hmm. 6.7.8 Initialization, paragraph
2 under "Constraints", says:
No initializer shall attempt to provide a value for an object not
contained within the entity being initialized.

I see also, under Semantics, paragraph 14:

An array of character type may be initialized by a character
string literal, optionally enclosed in braces. Successive
characters of the character string literal (including the
terminating null character if there is room or if the array
is of unknown size) initialize the elements of the array.

I'd say that this means that it's a constraint violation. Note that
only the terminating null character gets the "if there is room" qualifier.

-s
 
J

Jens Thoms Toerring

Keith Thompson said:
/* Lie to the compiler. Should be ok though as the compiler knows how
big the array really is.*/
char arr[7] = "0123456789";

No. If you explicitly tell the compiler that the array is supposed
to have 7 elements then it will have 7 elements and the excess ini-
tializers will get discarded. So you will end up with a char array
with 7 elements and no trailing '\0' at the end, thus the array
can't be used as a string.
No, as I mentioned elsethread, this is a constraint violation. C99
6.7.8p2:

No initializer shall attempt to provide a value for an object
not contained within the entity being initialized.

Uuups, thanks for the corrections. Should have looked it up
before posting;-)
Regards, Jens
 
S

Shao Miller

Anders said:
Could someone comment on this code please.

1. Are the comments in it correct?

2. Why does sizeof(arr) not work consistently in C? In someFunction()
sizeof(arr) means sizeof(&arr[0]) in main. The compiler knows how big arr
is, so why the difference - esp. as it would be v.useful if sizeof(arr)
worked in someFunction as it does in main - so you could use sizeof(arr)
in
the conditional bit of,say, a for loop in someFunction for example?

Array decay? What do you say to that, today?

char[W][X][Y][Z] --> char(*)[X][Y][Z]
char[X][Y][Z] --> char(*)[Y][Z]
char[Y][Z] --> char(*)[Z]
char[Z] --> char *

If you declare:

char arr[2][3][5][7];

Then for any scenario other than 'sizeof' and '&', the above-shown type
"decay" occurs for 'arr'. Here's an example:

char (*xyz_arr_ptr)[3][5][7];
xyz_arr_ptr = arr; /* 'arr' "decays" to 'char(*)[3][5][7]'. */

'xyz_arr_ptr' above then points to the first 'char[3][5][7]' element of
the 'arr' array; arrays "decay" to a pointer to their first element.

Now for the unary '&' address operator:

char[W][X][Y][Z] --> char(*)[W][X][Y][Z]
char[X][Y][Z] --> char(*)[X][Y][Z]
char[Y][Z] --> char(*)[Y][Z]
char[Z] --> char(*)[Z]
char --> char *

So if you declare:

char arr[2][3][5][7];

Then if you take the address of 'arr', you can assign it to something
with the above-shown type. Here's an example:

char (*ptr_to_arr)[2][3][5][7];
ptr_to_arr = &arr; /* Take the address of 'arr'. */

And for 'sizeof': 'sizeof' yields the size, in bytes, of its operand.
However many bytes an array consumes, that's how many bytes 'sizeof'
returns. An array doesn't "decay" in such a situation.
3. Why doesn't C allow testFunction to be declared char [] testFunction -
seems to me that if it did you could get away with knowing less about
pointers, and it seems as though again that C is incosistent, i.e., you
don't have to declare testFunction's parameter as char *. Surely, the
compiler should know that char [] someFunction actually means char *
someFunction??

char * testFunction(char []);

The above declaration is legitimate.

If you are asking about a function returning an array, such as in:

char test_a(void)[5];

Or:

typedef char char_arr_5[5];
char_arr_5 test_b(void);

These are not quite the same as your analogy of 'char []' in a prototype.

You don't declare an array with:

char [5]foo;

You declare it:

char foo[5];

So you don't declare a function returning an array:

char [5]testFunction(void);

But as a matter of fact, it's non-Standard, so you "don't" anyway. But!
You can wrap it in a 'struct' and have a function return such a struct:

struct char_arr_5_s {
char arr[5];
};

struct char_arr_5_s test_c(void);

Or for plenty of fun, return a pointer to such an array:

char (*test_d(void))[5];

Yikes. Or using 'typedef':

typedef char(*ptr_to_char_arr_5)[5];
ptr_to_char_arr_5 test_d(void);
int main()

Or you could even use a Standard form, such as:

int main(void)
{
/* Lie to the compiler. Should be ok though as the compiler knows how
big the array really is.*/
char arr[7] = "0123456789";

/* Ouput the sizeof the array. And the size of a pointer to it. */
printf("main() sizeof arr = %d\n", sizeof(arr));
printf("main() sizeof arr = %d\n", sizeof(&arr[0]));

Your second 'printf' is not quite right, I think. 'arr' is the array.
'sizeof arr' is the size of the array. 'sizeof &arr' is the size of a
pointer to the array. 'sizeof &arr[0]' is the size of a pointer to the
first element of the array.

For identifiers of objects like 'arr', your parentheses are optional in
your 'sizeof' expressions.
/* Pass it on to the function where we'll do some other emaningless
things.*/
testFunction(arr);
return(0);

}

/* Can't declare testFunction as char [] testF...() - even though the
compiler knows that array's are passed by address.*/

/* The size is ignored - and can't be checked anyway.*/
char * testFunction(char arr[500])

How about using a pointer to an array, instead? For example:

char (*testFunction(char (*arr)))[500]
{
int n;

/* Same sizeof line of code that was in main() - note here that it
output the size of a pointer!*/
printf("sizeof arr = %d\n", sizeof(arr));

That's because when an array appears in a prototype like that, it's
another situation where it "decays" into a pointer. If you use a
pointer to an array as given a bit above, then you can use 'sizeof *arr'
in your 'printf'.
return(arr);

}

You can pass a pointer to an array and you can return a pointer to an
array. Since 'sizeof' and '&' (and declarations for arrays outside of a
function parameter declaration) are the only situations where arrays
don't "decay", you cannot pass them directly! So why return them? So
why allow for arrays as function parameters?

Of course you can pass pointers to arrays, and pointers to elements of
arrays. And you can pass element counts.

Hope this information above helps (and hope it's fairly accurate).

.... ... ...

#if 0
char test_a(void)[5];
#endif

typedef char char_arr_5[5];
#if 0
char_arr_5 test_b(void);
#endif

struct char_arr_5_s {
char arr[5];
};

struct char_arr_5_s test_c(void);

char (*test_d(void))[5];

typedef char(*ca5_ptr)[5];

ca5_ptr test_d(void);

char (*testFunction(char (*arr)))[500];

int main(void) {
char arr[2][3][5][7];
char (*xyz_arr_ptr)[3][5][7];
char (*ptr_to_arr)[2][3][5][7];

xyz_arr_ptr = arr;
ptr_to_arr = &arr;
return 0;
}
 
S

Shao Miller

Shao said:
Anders said:
/* The size is ignored - and can't be checked anyway.*/
char * testFunction(char arr[500])

How about using a pointer to an array, instead? For example:

char (*testFunction(char (*arr)))[500]

I screwed this up. I missed a piece, sorry:

char (*testFunction(char (*arr)[500]))[500]
 
M

Malcolm McLean

Could someone comment on this code please.
C has a few rough spots, one oddity is that an array label is
equivalent a pointer, except when passed to the sizeof function/
operator (another little oddity).

These are very much the exception. Generally it's a very clean,
intuitive language.
 
K

Keith Thompson

Malcolm McLean said:
C has a few rough spots, one oddity is that an array label is
equivalent a pointer, except when passed to the sizeof function/
operator (another little oddity).

More precisely, an expression of array type is implicitly converted
to a pointer to the first element of the array object unless it
is the operand of a unary "sizeof" or "&" operator or is a string
literal used in an initializer for an array object.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,954
Messages
2,570,114
Members
46,702
Latest member
VernitaGow

Latest Threads

Top