P.Y. написав:У чому на практиці проявляються розбіжності між float** та float*[] (особливо якщо йдеться про формальний параметр функції)?
Стандарт каже про те, що відбувається, але не каже, чому. Спробую пояснити. Коли у нас є простий тип - int, float, вказівник - ми точно знаємо його розмір у пам'яті. Функція, що приймає параметр типу int, точно знає, де саме буде знаходитися цей int. Наприклад,
int a; //a - це байти пам'яті, наприклад, 1000, 1001, 1002, 1003 (якщо у нас int займає 4 байти)
int *b = &a; //b - це байти 1004-1007, всередині яких у якійсь формі записано посилання на 1000
int *c = new int; //c - це байти 1008-1011, значення яких буде прописано під час виконання
Це дозволяє легко написати, наприклад, функцію, що приймає int чи int * - треба просто домовитися, в які саме 4 байти треба покласти цей int, і надалі той, хто викликає, може вже не думати про того, кого він викликає; точно відомо, що змінні не переплутаються.
А от масив у загальному виді не має одного розміру. int a[10] - це не те саме, що int a[12]; відтак, передати його у функцію просто так не вийде. Вийде лише посилання на нього. Тому в C/C++ масив у більшості виразів автоматично перетворюється на посилання. Посилання куди? На його ж власний початковий елемент: a==&a[0]. Причому саме елемент:
int arr[10]; //байти 11000-11039
int *ptr = arr; //ptr - це байти 11040-11043, що містять в собі посилання на 11000
int *ptr1 = &arr; //в C так можна, а в C++ треба додатково перетворити типи, бо посилання на масив - не те саме, що посилання на елемент
І тільки так працюватиме арифметика вказівників, бо для того, щоб знайти наступний елемент, треба знати розмір поточного:
a[3]==*(a+3) //3 чого саме?
А тепер - до вашого питання. Це зовсім різні речі:
float (*a)[10] - вказівник на масив чи на масив масивів float. Зокрема, a[1] буде зсунутим на 10*sizeof(float), як і a+1. Так можна адресуватися до float x[5][10] чи float x[50][10].
float **a - вказівник на вказівник (чи масив) вказівників (чи масивів). Тобто, щоб скористатися ним, нам потрібні не лише елементи float у пам'яті, а й додатковий масив вказівників на рядки масиву. Запишу статично:
float **pparr;
float *(parr[10]);//масив з 20 float *
pparr = parr;//так можна, parr == &parr[0];
float arr[10][20]; //масив float
for(int i=0;i<10;++i)
parr[i]=arr[i];//arr[i] тут перетворюється на вказівник на свій нульовий елемент, тобто на &float[10]
І от лише тепер pparr[x][y] буде означати те саме, що й arr[x][y].