У меня есть следующий код. Вызов f1 вызывает segfault, но не f2.
#include <stdlib.h>
#include <stdio.h>
void f1(unsigned char** arr)
{
unsigned char* p = *arr;
*p = 'h';
p++;
*p = '\0';
}
void f2(unsigned char* arr)
{
unsigned char* p = arr;
*p = 'h';
p++;
*p = '\0';
}
int main()
{
unsigned char a[31];
f1((unsigned char**)&a);
//f2(a);
printf("%s\n", a);
}
&a не является unsigned char**. Настаивать на том, что это так, еще не значит, что это так.





unsigned char** — это указатель на указатель на unsigned char.
&a возвращает адрес массива a[]. Тип, который возвращает &a, — это unsigned char (*)[31], а не unsigned char**, поэтому вы не можете просто вызвать f1(a), что является несоответствием типов. Обратите внимание на эту ошибку, это важно.
&a не возвращает адрес указателя, но вы используете приведение типов, чтобы обмануть компилятор, заставив его думать, что это так, таким образом, вы заставляете f1() получить доступ к недопустимому адресу памяти через недопустимый указатель, отсюда и ошибка сегмента.
IOW, вы берете указатель на массив и преобразуете его в указатель на указатель, а это не так. Внутри f1() ожидается, что разыменование *arr даст указатель unsigned char* на действительный unsigned char, но на самом деле это не так. arr указывает на сам a[], и, таким образом, чтение *arr будет читать и неправильно интерпретировать первые 4 байта (в 32-битной сборке) или 8 байтов (в 64-битной сборке) a[] как значение указателя, а это не так. Тогда разыменование этого указателя не удается.
Не используйте слепо приведение типов только для того, чтобы заглушить ошибки компилятора. Это ошибки по какой-то причине. Приведение типов полезно в определенных случаях, но это не один из них. Правильный способ вызова f1() с помощью a[] в вашем примере выглядит следующим образом:
unsigned char a[31];
unsigned char *ptr = a; // aka: = &a[0]
f1(&ptr);
Альтернативно, измените f1(), чтобы принять указатель на массив вместо указателя на указатель:
#include <stdlib.h>
#include <stdio.h>
void f1(unsigned char (*arr)[31])
{
unsigned char* p = *arr;
*p = 'h';
p++;
*p = '\0';
}
int main()
{
unsigned char a[31];
f1(&a);
printf("%s\n", a);
}
Что касается того, почему вызов f2(a) работает как есть, то это потому, что массив распадается на указатель на свой 1-й элемент, и f2() правильно использует этот указатель. Это не тот случай, когда вы звоните f1(&a).
a — адрес массива, &a — адрес a, в f1, *arr указывает на массив, я что-нибудь пропустил?
@user180574 user180574 да, это так. a — это не адрес массива, а сам массив. &a — адрес массива. Я обновил свой ответ.
Я думал, что указатель и массив в C — это одно и то же, нет?
@RemyLebeau, не могли бы вы опубликовать код, который будет работать?
@JosephDoggie, я уже сделал это
@user180574 user180574 «Я думал, что указатель и массив в C — это одно и то же» - нет, и на самом деле вам уже сказали именно это в вашем предыдущем вопросе.
@RemyLebeau Отлично! Отличная работа....
Проблема в том, что приведение (unsigned char**)&a. Итак, первая функция пытается использовать неопределенное значение выражения указателя *arr.