0

I am trying to define a function prototype which takes an array of char of different lengths. I understand that I must pass the array by reference to avoid the array decaying to a pointer to its first element. So I've been working on this simple example to get my understanding correct.

 #include <stdio.h> // size_t

 //template to accept different length arrays
 template<size_t len>
 //pass array of char's by reference
 void func(const char (&str)[len])
 {
     //check to see if the array was passed correctly
     printf(str);
     printf("\n");
     //check to see if the length of the array is known
     printf("len: %lu",len);
 }
 int main(){
     //create a test array of chars
     const char str[] = "test12345";
     //pass by reference
     func(&str);
     return 0;
 }

This gives me the compiler errors:

main.cpp: In function ‘int main()’:
main.cpp:19:14: error: no matching function for call to ‘func(const char (*)[10])’
     func(&str);
              ^
main.cpp:6:6: note: candidate: template<long unsigned int len> void func(const char (&)[len])
 void func(const char (&str)[len])
      ^~~~
main.cpp:6:6: note:   template argument deduction/substitution failed:
main.cpp:19:14: note:   mismatched types ‘const char [len]’ and ‘const char (*)[10]’
     func(&str);

I thought that the function signature func(const char (&str)[len]) indicates a pointer to a char array of length len, which is what I am passing by func(&str).

I tried func(str), which I would expect to be wrong, since I am passing the value str, instead of its reference. However, this actually works and I dont understand why.

What is going on here? What does it actually mean to pass by reference?

Mr.Weathers
  • 398
  • 1
  • 5
  • 19
  • I don't understand. Why not use `std::vector`? It's easier to pass (you can simply pass by reference) and it has a `size` method. – Thomas Matthews Mar 31 '22 at 22:44
  • 3
    `func(*str);` is calling `func(char)` (which there is none, including deducible). The call `func(str)` is passing by reference, not value, because that's how the argument is deduced. – WhozCraig Mar 31 '22 at 22:44
  • 2
    It looks like you are showing us an error from a different source in which you do `&str` in main. – Fatih BAKIR Mar 31 '22 at 22:46
  • 1
    I don't quite follow why you are surprised that `func(str)` is the correct way to call the function. If it was an `int i;` instead of a `char[]` and the function took the `int` by reference as `int&`, would you be surprised that `func(i)` is the correct way to call the function? What seems different here to you? Why do you think that `const char (&str)[len]` is a pointer? As you said at the beginning it is a reference, not a pointer. – user17732522 Mar 31 '22 at 22:47
  • Completely unrelated, sending strings as the format string argument to `printf` solo is a *really* bad idea. Use `puts` or at least `printf("%s", str)`, and ideally, just `std::cout << str;` – WhozCraig Mar 31 '22 at 22:48
  • @ThomasMatthews, I'm not using std::vector because I'm modifying code in an existing library. I want to minimize the change and keep compatibility. – Mr.Weathers Mar 31 '22 at 23:07
  • @FatihBAKIR, Yes you are right. I fixed it in the post – Mr.Weathers Mar 31 '22 at 23:08
  • @user17732522 `const char (&str)[len]` seemed like a pointer to me because of the `&` operator. `&str` is the address of `str`. So it seems that `(&str)` is a pointer parameter. – Mr.Weathers Mar 31 '22 at 23:10
  • 1
    @Mr.Weathers My impression is that you are not aware of what a _reference_ in C++ is. `&` in a type has nothing to do with pointers. It indicates a reference. See [What are the differences between a pointer variable and a reference variable in C++?](https://stackoverflow.com/questions/57483/what-are-the-differences-between-a-pointer-variable-and-a-reference-variable-in). – user17732522 Mar 31 '22 at 23:13
  • 1
    @Mr.Weathers `&` in a formal parameter declaration is *never* a pointer-to indicator; it means reference in that context *always*. Ex: `void foo(Type &arg);` You're mistakenly seeing `&` as you would in its functional context where it means address-of, which is a different beast entirely. – WhozCraig Mar 31 '22 at 23:13

1 Answers1

3

Your function is declared correctly, but you are not passing the array to it correctly.

func(*str); first decays the array to a pointer to the 1st element, and then deferences that pointer, thus passing just the 1st character to func(). But there is no func(char) function defined, so this is an error.

func(&str); takes the address of the array, thus passing a pointer to the array, not a reference to it. But there is no func(char(*)[len]) function defined, so this is also an error.

To pass str by reference, you need to simply pass str as-is without * or &:

func(str);

This is no different than passing a reference to a variable of any other type, eg:

void func(int &value);

int i;
func(i);

On a side note: printf(str); is dangerous, since you don't know if str contains any % characters in it. A safer call would be either:

printf("%s", str);

Or:

puts(str);

But those only work if str is null-terminated (which it is in your case). Even safer would be:

printf("%.s", (int)len, str);

Which doesn't require a null terminator.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Out of curiosity, what happens with the precision-specified case for `%.s` when the length provided includes the full string *and* the terminator (as it does in the OPs code)? Does it stop at the encounter of the terminator, though included in `len`, or does it try to shove the terminator into the output stream as well? Just curious (and yes, a bit lazy; could prolly look i up myself; sry). – WhozCraig Mar 31 '22 at 23:01
  • 1
    @WhozCraig The precision element of `%s` specifies a *maximum* length. `printf()` will print UP TO the specified max length, but *should* stop early if `'\0'` is encountered first. – Remy Lebeau Mar 31 '22 at 23:07
  • That's my learned-something-new for today. Thanks, Remy. – WhozCraig Mar 31 '22 at 23:10
  • Ok thanks. That makes sense. my misunderstanding ultimately was due to conflating the words "reference" and "pointer". Could you elaborate a bit on how those terms are different in c++? – Mr.Weathers Mar 31 '22 at 23:13
  • @Mr.Weathers [Already discussed here](https://stackoverflow.com/questions/57483/what-are-the-differences-between-a-pointer-variable-and-a-reference-variable-in). – WhozCraig Mar 31 '22 at 23:14
  • @WhozCraig Thanks for clarifying. The overloading of the `&` symbol seems like a confusing choice – Mr.Weathers Mar 31 '22 at 23:16
  • @Mr.Weathers When adding new features, the C++ committee likes to reuse symbols and keywords whenever possible. This avoids having to reserve new symbols and keywords, which may break compatibility with existing code. – François Andrieux Mar 31 '22 at 23:30