Страницы

Поиск по вопросам

понедельник, 11 февраля 2019 г.

Помощь с указателями

Всем привет, дошел до темы указателей, честно говоря, тема неприятная, поэтому сразу к вам вопросы, товарищи.
1)
char *lo = "12345"; char *plo = lo; // вот так можно, это понятно // но почему можно так? char **plo = lo; или так char **plo = *lo;
2) Что тут происходит и почему так можно делать?
main() { char *lo = "12345"; fun(lo); }
// так void fun(char s[]){ // немного не понимаю, почему так делается printf("%s", s); }
// или так void fun(char *s){ // так понимаю printf("%s", s); }
//можно и так void fun(char **s){ // так тоже не понимаю printf("%s", s); }
//тоже не проблема void fun(char **s[]){ // ну а это вообще факинг щит какой-то printf("%s", s); }
А главное все варианты работают...
3) main() {
char *err[] = { "Cannot Open File
", "Read Error
", "Write Error
", "Media Failure
" }; fun(err); }
void fun(char *s[]){ printf("%s", s[0]); }
Понимаю, если бы написали
void fun(char **s){ printf("%s", s[0]); }
Но почему char *s[] работает, я не понимаю. Главное, если в main так попробую написать
main(){ *perr[] = err; }
то работать не будет...
Спасите... заранее благодарю, хотя бы за то, что прочитаете. Шлю флюиды добра тем, кто ответит


Ответ

Для начала давайте разделим понятие "работает" на "работает и все ок", "компилируется и вроде работает", "компилируется, но происходит что то странное".
Второе, разделим с и с++. Многие считают, что с является подмножеством с++. Но это далеко не так. На данный момент, это языки, которые имеют просто много общего.
Начнем с самого главного, которое справедливо для с/с++
a[b] == b[a] == *(a+b)
То есть, эти все три выражения эквивалентны. Когда в следующий раз увидите 1[a] - не пугайтесь, это нормально, хоть и не очень применимо для продакшн кода.
Отсюдова второе следствие, если закрыть глаза на некоторые особенности, то массив это и есть указатель. И в большинстве случаев *a == a[]
Теперь разберем примеры.
char *lo = "12345";
в си - это почти ок. В с++ это плохо, так как "12345" - это const char*. А присваивание с потерей константности это плохо.
char *plo = lo; // вот так можно, это понятно
конечно можно - char* и char*
// но почему можно так? char **plo = lo;
а так как бы нельзя. И с++ будет ругаться, потому что с одной стороны char** , а с другой char*. Но для с это два указателя и он закрывает на это глаза.
или так char **plo = *lo;
Опять же, для с++ компилятора тут ужас - попытка присвоить типу char** значение типа char Но на низком уровне все ок. Указатель это просто int, char - это просто то, что может быть приведено. Компилятор си доверяет человеку.
Поехали дальше.
main() { char *lo = "12345"; fun(lo); }
по поводу типа я уже писал. там явно не хватает const.
// так void fun(char s[]){ // немного не понимаю, почему так делается printf("%s", s); }
char[] это просто массив. А массив это указатель.
// или так void fun(char *s){ // так понимаю printf("%s", s); }
//можно и так void fun(char **s){ // так тоже не понимаю printf("%s", s); }
тут формально все ок. Потому что printf будет трактовать s как char*. как я сказал выше, char*, char** это все просто int. Вызывающая функция сохранила адрес, а printf его извлек.
//тоже не проблема void fun(char **s[]){ // ну а это вообще факинг щит какой-то printf("%s", s); }
Здесь снова никаких "проблем". char** s[] - это просто char*** и снова тот же int. Пример сводится к предыдущему.
поэтому, и пример ещё ниже - char* s[] это char** s
main(){ *perr[] = err; }
а вот это точно не будет работать - код не валидный, типы переменных не известны (хотя может они где то и спрятались). Но! написать в левой части *perr - это нормально - это разыменования указателя. И эта звездочка не должна обманывать - это звездочка разыменования, а не объявления типа указателя. И квадратные скобки в этом случае лишены смысла. Но если бы там был индекс, то сразу ситуация улучшается.
В целом, си старается быть недалеко от ассемблера. Поэтому подобные конструкции в си и работают. С++ старается быть более строгим и проверяет детальнее.

Комментариев нет:

Отправить комментарий