0

In the following example, is it possible to declare test() with such a signature so it would print 6? I would like this for style and readability purposes, so that it's clear that the function takes an array of exactly 6 chars, and it actually can get this information with sizeof.

#include <stdio.h>

void test( char foo[ 6 ] )
{
  printf( "%zu\n", sizeof( foo ) ); // Prints 8 as it's a pointer now, but I want 6 as in an array
}

int main()
{
  char foo[ 6 ];
  printf( "%zu\n", sizeof( foo ) ); // Prints 6, which is what I want
  test( foo );
  return 0;
}

So far, the best I could come up with is this:

typedef char Foo[ 6 ];
void test( Foo foo )
{
  printf( "%zu\n", sizeof( Foo ) ); // Works, but it doesn't even use foo, and I want to get the size information from foo! What if I change the signature of the function later? I would have to update this line too, which is something I'd like to avoid
}
dragonroot
  • 5,653
  • 3
  • 38
  • 63
  • Does this code actually compile? Anyway, just pass the size you want as an additional parameter to the method, or scan for a null value, or something like that. – Robert Harvey Dec 09 '14 at 23:57
  • You cannot pass an array to a function in C. Like, at all. All you can do is passing pointers, like a pointer to an array of 6 `char`. Though @RobertHarvey mentioned how any sane person deals with it in C. – Deduplicator Dec 09 '14 at 23:58
  • I want `6` to just appear once, so if it needs to be changed, there's only a single place to do that. I could of course define it, but that would be a bit over-verbose. – dragonroot Dec 10 '14 at 00:07
  • OP, could it be that you actually want to use C++ instead of C? – Kijewski Dec 10 '14 at 00:17
  • I just want to pass a plain pointer. With the compiler keeping track that it points to 6 elements, not an arbitrary amount. What's wrong with that? If it's impossible, fine, I could happily accept that as an answer. – dragonroot Dec 10 '14 at 00:19
  • You could pass a pointer to a struct, and have the struct contain a pointer to an array, plus the length of the array. I think any solution is going to involve some acrobatics, using C. – yellowantphil Dec 10 '14 at 00:22
  • Well, that was my question exactly - whether it's possible without acrobatics or not. – dragonroot Dec 10 '14 at 00:24
  • @dragonroot In trying to reconcile wanting to pass a pointer, the called function then _must_ receive a pointer. The size of that pointer will then naturally be `sizeof(type*)`. The only avenue left is having the size of the type pointed _to_ as 6 or `sizeof *arg`. Another choice is to pass by value, maybe via a structure. Lastly, we _simply_ evolve the language to have true pass-by-reference. – chux - Reinstate Monica Dec 10 '14 at 01:40

2 Answers2

4

Yes, it is possible:

#include <stdio.h>

void test(char (*foo)[6]) // "declare foo as pointer to array 6 of char" 
{
  printf("%zu\n", sizeof(*foo)); // mind the asterisk
}

int main(void)
{
  char foo[6];
  test(&foo); // mind the ampersand
  return 0;
}

Compare cdecl.org ("C gibberish ↔ English") for


Another option is wrapping the array. But if you don't like referencing/dereferencing, then this solution with an addition member access probably is not what you are looking for, either:

#include <stdio.h>

typedef struct
{
  char data[6];
} char_6_array_t;

void test(char_6_array_t foo)
{
  // prints "6: abcdef"
  printf("%zu: %.*s\n", sizeof(foo.data), (int) sizeof(foo.data), foo.data);
}

int main(void)
{
  char_6_array_t foo = { {"abcdef"} };

  // Depending on the ABI, this prints 6, 8, or possibly 16:
  printf("%zu\n", sizeof(foo));

  test(foo);
  return 0;
}
Kijewski
  • 25,517
  • 12
  • 101
  • 143
  • There is no point of doing this, if you hardcode the size, then just use that size. – 2501 Dec 10 '14 at 00:00
  • Yes, it doesn't look that useful, but it's a neat trick. I didn't know this was possible. – yellowantphil Dec 10 '14 at 00:03
  • @Deduplicator, what do you mean by "passing an array"? You mean passing by value? – Kijewski Dec 10 '14 at 00:05
  • This solution introduces an additional reference/dereference operation, which is not desirable. – dragonroot Dec 10 '14 at 00:06
  • 2
    he wants the function signature to include 6 for style and readability. This is a fine answer – pm100 Dec 10 '14 at 00:08
  • @yellowantphil, using `some_type (*var)[N]` is of little use in C, but comes in handy in C++ where `some_type` and `N` can automatically be deduced as template arguments. – Kijewski Dec 10 '14 at 00:16
  • 1
    @dragonroot You asked for for a solution that supported "this for style and readability purposes". Now you have another condition to avoid a "solution introduces an additional reference/dereference operation". Suggest updating your post to include that, rather than only in a local comment, or rate answers without that addition. – chux - Reinstate Monica Dec 10 '14 at 01:14
  • 1
    Interesting example of how to print an array with `printf("%.*s\n", (int) sizeof(array), array);` – chux - Reinstate Monica Dec 10 '14 at 01:26
2

Not without some acrobatics.

There are ways to make it work, either with global variables as in your question, or the solutions proposed by Kay. But the C language does not pass entire arrays by value as function arguments. Your test function only receives a pointer to a char, and the compiler will not tell you what size of array the pointer might point to.

Some other languages, like C++, handle this sort of thing for you. But in C, you just have to track the size yourself, one way or another. A common way is to add another argument to your function, where you pass in the size of your array.

In the function prototype you gave:

void test( char foo[ 6 ] )

I think that the compiler unfortunately just ignores the 6, although you could argue that that doesn't make much sense. For a multi-dimensional array, the compiler does pay attention to the sizes you give, except for the first one, so foo[][6] works, and is necessary to tell the compiler where each row of your array ends. (See this question.)

Yet another workaround would to use a structure like this:

struct array {
    char *a;   // pointer to your real array
    size_t s;  // how many elements your array has
};

You could pass one of those structures to your function, and then read the size out of it.

Community
  • 1
  • 1
yellowantphil
  • 1,483
  • 5
  • 21
  • 30
  • Just to be clear, I don't want to pass an array by value. I want to pass a pointer to an array of 6 chars, and I want the signature of the function to contain that information. Furthermore, I want this size information to be retrievable within the function from the signature. While I can write `void fn( char foo[ 6 ] )`, I can't retrieve that `6` from within the body of the function. Btw, I'm writing this comment to you just to have at least one person in this whole thread who understands me. The rest of the crowd doesn't seem to, which makes me sad. Thanks for your participation :) – dragonroot Dec 10 '14 at 00:36
  • @dragonroot It is strange that the C language lets you specify `foo[6]`, when it ignores the 6 entirely. I added something in my answer about that. Maybe some language lawyer knows why `foo[6]` is allowed, when it apparently doesn't mean anything different from `foo[]`. – yellowantphil Dec 10 '14 at 00:45
  • Exactly. As for the workaround you've mentioned, it adds both additional indirection and additional memory use, while I just want the compiler to give me that value of 6 at compile time :) – dragonroot Dec 10 '14 at 00:50
  • "the compiler does pay attention to the sizes you give, except for the first one". That's the issue, for an array, the one and only size is the first one. – rcgldr Dec 10 '14 at 02:40