Передача массива typedef (фиксированного размера) по значению - PullRequest
4 голосов
/ 17 мая 2011

Мне трудно понять typedef шаблон для массивов.

typedef char Char10[10];
void fun (Char10 a)  // not passing reference (interested in pass by value)
{
  if(typeid(Char10) == typeid(char*))
    throw 0;  // <--- never happens
}

int main ()
{
  char a[10];  fun(a);  // ok
  char b[11];  fun(b);  // why works ?
}

Почему различные размеры массива по значению принимаются fun()? char[10] и char[11] не являются разными типами?

Редактировать : Для тех, кто говорит, что указатель затухает, см. Мой отредактированный код. char[10] и char* не совпадают.

Ответы [ 8 ]

22 голосов
/ 17 мая 2011

В обоих случаях массивы распадаются на тип указателя, а ваша функция на самом деле это:

void fun (char *a); 

Именно поэтому она работает.

Я хотел бы подчеркнуть, что void fun(char*) точно такой же, как void fun(char[10]).10 не имеет никакого значения вообще.На самом деле, 10 настолько не важен и бесполезен, что вы можете даже полностью его опустить, как:

void fun (char a[]); //exactly same as `char*` or `char[10]`.

Это означает, все следующие объявления функций точно same:

void fun(char a[10]);   
void fun(char a[]);  //10 is unimportant in the above declaration
void fun(char *a);   //same as above two declarations!

Надеюсь, что прояснит ваше сомнение.


Однако, если вы напишите это:

void fun (Char10 & a) ; //note &

тогда, это фактически так:

void fun (char (&a)[10]) ; //equivalent!

Тогда fun(b) не будет компилироваться, поскольку теперь fun будет принимать ТОЛЬКО массив ТОЧНОГО размера 10. И массив будет не распадаться на указатель , он будет передан по ссылке .

char a[10], b[11];
char *c=new char[10];
fun(a); //okay
fun(b); //error - type mismatch due to size of the array
fun(c); //error - type mismatch due to c being pointer.
9 голосов
/ 17 мая 2011

Они разных типов, ты прав.

Это вводящая в заблуждение причуда C ++, которая, как вы можете видеть, имеет функцию

void fun(char a[10])

Поскольку вы не можете передавать массивы по значению, а C ++ глуп, это на самом деле функция

void fun(char* a)

И, конечно, оба входа счастливо снижаются до char*.

Было бы неплохо, если бы C ++ не позволял вам даже притворяться, что он принимает массив по значению, но это глупо ... наследоваться от C.

4 голосов
/ 17 мая 2011

Char10 и char* действительно разные типы ..

Попробуйте

if(typeid(a) == typeid(char*))
    throw 0;  // happens

Демонстрационная версия

PS:

Следующие объявления эквивалентны

void fun(Char10 a);
void fun(char a[]);
void fun(char *a);
2 голосов
/ 17 мая 2011

Для вашего редактирования: типы per se различны, но в C ++ вы не можете передать массив по значению в качестве аргумента функции, и тип массива затухает до соответствующего типа указателя при использованииэто как аргумент функции.

#include <iostream>
#include <typeinfo>

using namespace std;

typedef char Char10[10];
void fun (Char10 a) // <-- actually Char10 here is read as char *
{
    Char10 test;
    cout<<(typeid(Char10) == typeid(char*))<<'\n'
        <<(typeid(Char10) == typeid(test))<<'\n'
        <<(typeid(char *) == typeid(a))<<endl;
}

int main ()
{
  char b[11];  fun(b);  // why works ?
}

Это выводит

0
1
1

Поскольку

  • char * и char[10] - это разные типы;
  • test является char[10]
  • a на самом деле char *, потому что в объявлениях функций объявление массива затухает до соответствующего объявления указателя.

Это глупая вещь, которая здесь по историческим причинам, но мы застряли с ней.

Находится в стандарте: в §8.3.5 ¶3:

После определения типа каждого параметра любой параметр типа «массив из T» или «функция, возвращающая * 1031»* »Настроено на« указатель на T »или« указатель на функцию, возвращающую T »соответственно.

2 голосов
/ 17 мая 2011

Когда вы получаете параметр массива, компилятор понимает его как указатель на первый элемент массива.Там нет разницы между массивами разных размеров во время выполнения.Вот почему вам нужно знать размер, с которым вы работаете, чтобы избежать доступа к памяти, которая не принадлежит вашей программе.

1 голос
/ 29 марта 2012
struct char10 {
    char c[10];
};

int foo(char10 param){
...

Вы можете эмулировать передачу массивов фиксированного размера по значению, как это.

1 голос
/ 17 мая 2011

Вы не можете передавать массив по значению ни в C, ни в C ++.Массивы в C / C ++ не копируются, поэтому независимо от того, что вы делаете, вы никогда не достигнете семантики «передача по значению» с типом массива.

Похоже, вы надеялись, что сокрытие типа массива за именем типа-определения позволит вам обойти это ограничение.Не будетВаша декларация

void fun(Char10 a)

эквивалентна декларации

void fun(char a[10])

, которая, в свою очередь, эквивалентна

void fun(char a[])

и

void fun(char *a)

Итак, ваш a параметр имеет тип указателя.Когда вы передаете массив, подобный этому

char a[10]; fun(a); 

, вы просто передаете указатель char * на первый элемент массива.Размер массива не имеет значения, поэтому вы можете передавать массивы разных размеров, используя этот метод.

Внутри функции fun, если вы хотите проанализировать тип параметра a, вы должны применить typeid к a

if(typeid(a) == typeid(char*))
  throw 0;

Это покажет вам, что типы совпадают.Почему вы применяете typeid непосредственно к Char10 и что вы ожидаете получить от него, мне неясно.Типы массивов распадаются на типы указателей в объявлении параметров функции, но не распадаются на типы указателей в операторе typed, что означает, что ваша версия if абсолютно не связана с тем, что происходит в объявлении параметра.

1 голос
/ 17 мая 2011

Однако можно принять ссылку на массив правильного размера, после чего компилятор будет возражать против него.

...