#cpp #c
Всем привет, дошел до темы указателей, честно говоря, тема неприятная, поэтому сразу к вам вопросы, товарищи. 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\n", "Read Error\n", "Write Error\n", "Media Failure\n" }; fun(err); } void fun(char *s[]){ printf("%s", s[0]); } Понимаю, если бы написали void fun(char **s){ printf("%s", s[0]); } Но почему char *s[] работает, я не понимаю. Главное, если в main так попробую написать main(){ *perr[] = err; } то работать не будет... Спасите... заранее благодарю, хотя бы за то, что прочитаете. Шлю флюиды добра тем, кто ответит
Ответы
Ответ 1
Для начала давайте разделим понятие "работает" на "работает и все ок", "компилируется и вроде работает", "компилируется, но происходит что то странное". Второе, разделим с и с++. Многие считают, что с является подмножеством с++. Но это далеко не так. На данный момент, это языки, которые имеют просто много общего. Начнем с самого главного, которое справедливо для с/с++ 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 - это нормально - это разыменования указателя. И эта звездочка не должна обманывать - это звездочка разыменования, а не объявления типа указателя. И квадратные скобки в этом случае лишены смысла. Но если бы там был индекс, то сразу ситуация улучшается. В целом, си старается быть недалеко от ассемблера. Поэтому подобные конструкции в си и работают. С++ старается быть более строгим и проверяет детальнее.
Комментариев нет:
Отправить комментарий