1

Can you explain this:

void foo(const char data[10])
{
   char copy[10];
   const char copy1[10] = {};

   printf("%i", sizeof(copy)); //prints 10, as expected
   printf("%i", sizeof(copy1)); //prints 10, as expected
   printf("%i", sizeof(data)); //prints 4, WTF?

}

Looks like function parameters are treated as simple pointers for sizeof. But WHY does this happen? Is it documented anywhere?

Viesturs
  • 1,691
  • 4
  • 21
  • 25

6 Answers6

5

When you pass an array to a function, what you are actually doing is passing a pointer to the first element of the array (you probably knew that). Even if you have a parameter declared as char[10], that will still mean you only get a pointer since C is designed to be very fast so it avoids making copies of potentially very large data such as arrays. The [10] serves no purpose, except maybe as a reminder to the programmer.

Daniel Fischer
  • 181,706
  • 17
  • 308
  • 431
Paul Manta
  • 30,618
  • 31
  • 128
  • 208
4

I never saw that syntax before in C++ but maybe you meant

void foo(const char data[10])

Anyway, in C++ arrays decay to pointers when passed to a function. So the function has no way of knowing how big the passed arrays are. In that sense, what I wrote above is completely equivalent to:

void foo(const char data[])

There is also a C FAQ about this subject.

cnicutar
  • 178,505
  • 25
  • 365
  • 392
  • That decay isn't because it's being passed to a function (though that's the most common context). Any expression of array type is converted to a pointer in most contexts. See my answer for details. – Keith Thompson Dec 27 '11 at 11:36
  • @KeithThompson You're right, +1 :-) – cnicutar Dec 27 '11 at 12:01
4

There are actually two related but independent rules in play here.

One is that an expression of array type is, in most contexts, implicitly converted (at compile time) into a pointer to (i.e., the address of) the first element of the array. The exceptions are when it's the operand of sizeof, when it's the operand of unary &, and when it's a string literal in an initializer used to initialize an array object.

The other is that a function parameter declared to be of an array type is really of a pointer type -- and if the array type includes a length, that length is silently ignored. This is the rule that causes the behavior you're seeing.

Strongly recommended reading: Section 6 of the comp.lang.c FAQ.

EDIT : Your program has several errors that would prevent it from compiling, which means you couldn't have seen the results you say you're seeing. You apparently re-typed the program when posting it here. Instead, you should copy-and-paste the exact code as you fed it to the compiler.

Here's a corrected version of your code, with a bit of commentary added.

#include <stdio.h> /* required for printf */

void foo(const char data[10]) /* NOTE: The 10 is quietly ignored */
{
    char copy[10];
    /* syntax, [10] follows "copy", not "char" */
    const char copy1[10]; // = {};
    /* as above, and standard C disallows empty initializer */

    printf("%d", (int)sizeof copy);  // prints 10, as expected
    printf("%d", (int)sizeof copy1); // prints 10, as expected
    printf("%d", (int)sizeof data);  // prints 4 (sizeof (char*), as expected

    /*
     * "%i" or "%d" requires an int argument; sizeof yields size_t.
     * You need to convert the result.
     * "%d" is more common and idiomatic than the equivalent "%i"
     * When sizeof is applied to an expression, no parentheses are needed
     * (it's an operator, not a function
     */
}
Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
  • "You apparently re-typed the program when posting it here. Instead, you should copy-and-paste the exact code as you fed it to the compiler." Good advice that neither K&R nor most of my college profs have followed. :-( – The111 Dec 27 '11 at 10:49
  • @The111: How have K&R not followed that advice? – Keith Thompson Dec 27 '11 at 11:37
  • There are a handful of places in the book where the code given does not compile. Common errors that I remember are missing semicolons, misspelled keywords/variables, and missing parentheses. One concrete example off the top of my head is the `readlines` function in Section 5.6. The line `if (nlines >= maxlines || p = alloc(len) == NULL)` is missing a critical set of parentheses (though in this case the code actually would compile, which is not true for some of the other cases). I assume a cut and paste from code known to compile would prevent these errors. – The111 Dec 27 '11 at 11:49
3

You are passing address of 1st element. When we call foo() method the compiler generates a pointer to its first element. Array name is itself a pointer to the first element.

KV Prajapati
  • 93,659
  • 19
  • 148
  • 186
1

Other answers already mention that this is because a function parameter of declared as an array type actually declares a parameter of pointer type; this is specified in C++11, 8.3.5/5:

After determining the type of each parameter, any parameter of type “array of T” or “function returning T” is adjusted to be “pointer to T” or “pointer to function returning T,” respectively.

Therefore, data actually has type const char *, so sizeof data gives the size of a pointer.

You can preserve the size of an array by passing a reference to it:

void foo(const char (&data)[10])
{
   char copy[10];
   const char copy1[10] = {};

   printf("%i", sizeof(copy)); //prints 10, as expected
   printf("%i", sizeof(copy1)); //prints 10, as expected
   printf("%i", sizeof(data)); //prints 10, as expected
}

The reference must be to an array of known size, since 8.3.5/8 says:

If the type of a parameter includes a type of the form “pointer to array of unknown bound of T” or “reference to array of unknown bound of T,” the program is ill-formed.

You can get around this using a function template, parametrised by the size of the array:

template <size_t N>
void foo(const char (&data)[N]);
Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
-1

Try this code below. If your string is initialized with anywhere from 1-9 characters, you'll be able to get the length of it this way. The max is 9 characters because you must have room for the '\0' terminating character.

Incidentally, if your string is not initialized, the strlen in foo() does not report zero as I would expect, but rather some unexpected number. Perhaps someone more experienced than me can comment on this.

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

void foo(const char * data1, const char * data2)
{
   char copy[10];
   const char copy1[10] = {};

   printf("%i\n", sizeof(copy)); //prints 10, as expected
   printf("%i\n", sizeof(copy1)); //prints 10, as expected
   printf("%i\n", (sizeof(*data1) * strlen(data1))); //prints 1
   printf("%i\n", (sizeof(*data2) * strlen(data2))); //prints 9
}

int main(void)
{
    const char bar1[10] = "1";
    const char bar2[10] = "123456789";

    printf("%i\n", sizeof(bar1)); //prints 10, as expected
    printf("%i\n", sizeof(bar2)); //prints 10, as expected
    foo(bar1, bar2);
    return 0;
}
The111
  • 5,757
  • 4
  • 39
  • 55