06 Авг., 2020, 18:10

Некоторые ошибки слишком хороши, чтобы совершать их только один раз!


Изучаю Си (Массимы указателей)

Автор Nikopol, 17 Июль, 2020, 13:27

« предыдущая - следующая »

Nikopol

Помогите пожалуйста.

Вот пример программы
// массивы указателей
#include <stdio.h>
#include <string.h>
#define SIZE_NUMBER 6

int main()
{
   static char *p_mass_string[SIZE_NUMBER] = {"Я ", "изучаю ", "програмирование ", "на ", "языке ", "Си!"};
for(int i = 0; i < SIZE_NUMBER; i++)
    {
       fprintf(stdout, "%s", p_mass_string[i]);
    }
    fprintf(stdout, "\n");

    int num[SIZE_NUMBER] = {1, 2, 3, 4, 5, 6};
        int *p1 = &num[0];
        int *p2 = &num[1];
        int *p3 = &num[2];
        int *p4 = &num[3];
        int *p5 = &num[4];
        int *p6 = &num[5];
    int *p_mass_num[SIZE_NUMBER] = {p1, p2, p3, p4, p5, p6};
for(int i = 0; i < SIZE_NUMBER; i++)
    {
       fprintf(stdout, "%d", *p_mass_num[i]);
       
    }
    fprintf(stdout, "\n");
   
}

вот эти места кода
Код (первый) Выделить
for(int i = 0; i < SIZE_NUMBER; i++)
    {
       fprintf(stdout, "%s", p_mass_string[i]);
    }
   
Код (второй) Выделить
for(int i = 0; i < SIZE_NUMBER; i++)
    {
       fprintf(stdout, "%d", *p_mass_num[i]);
    }

отличаются только типом выводимых данных и * перед "р_..."
Почему если тип int то нужно ставить *, а если char то нет?
Если поставить * в char, т.е. написать "fprintf(stdout, "%s", *p_mass_string);" то компилятор выдаёт ошибку.
Код (Вывод компилятора) Выделить
arr_points.c: In function 'main':
arr_points.c:11:26: warning: format '%s' expects argument of type 'char *', but argument 3 has type 'int' [-Wformat=]
        fprintf(stdout, "%s", *p_mass_string[i]);
                         ~^   ~~~~~~~~~~~~~~~~~

Вот здесь ещё пример.

Slabovik

21 Июль, 2020, 09:15 #1 Последнее редактирование: 21 Июль, 2020, 16:27 от Slabovik
Наверное потому, что
Цитата: Nikopol от 17 Июль, 2020, 13:27static char *p_mass_string[SIZE_NUMBER] = {"Я ", "изучаю ", "програмирование ", "на ", "языке ", "Си!"};
является массивом указателей, а не данных. "Данные" - это p_mass_string[SIZE_NUMBER], а *p_mass_string[SIZE_NUMBER] - это указатели.

Поэтому попытка вывести fprintf(stdout, "%s", *p_mass_string); закономерно приводит к ошибке типов.

Подробнее можно почитать, например здесь: https://metanit.com/cpp/tutorial/4.1.php
но рекомендую начинать с малого, т.к. играясь с указателями на указатели, можно такого наворотить...

p.s. кстати, вот ещё чуток пояснений: https://ravesli.com/urok-82-ukazateli-i-massivy/
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

Slabovik

Вчера в 16:24 #2 Последнее редактирование: Вчера в 16:45 от Slabovik
Смотрю-смотрю и не пойму, либо я фигню здесь сморозил, либо что-то не так.
Попробую своими словами, вне справочника.

Операция '*' - разыменование (название видится неудачным, но уж раз так прижилось, им и пользуемся).
Разыменование имеет смысл тогда, когда есть некий адрес, по которому располагается некая переменная, а функции необходимо "скормить" саму переменную (данные). Собственно, это логика ЯВУ, когда вы прямо пишете формулы типа Z=X*Y

В примере выше имеем

static char *p_mass_string[SIZE_NUMBER] = {"Я ", "изучаю ", "програмирование ", "на ", "языке ", "Си!"};

Здесь получается, что p_mass_string - это массив адресов (шесть позиций), показывающих на начала соответствующих строк.
А *p_mass_string - это сами строки. Выходит, что выше как раз определяются строки, т.е. происходит их заполнение.

Пруф: http://all-ht.ru/inf/prog/c/func/printf.html
Цитировать's' - Вывод строки, на которую ссылается указатель в аргументе функции printf (fprint). Строка выводится, пока не будет встречен признак конца строки (/0). По умолчанию строка должна обозначаться как char*.
Таким образом, звёздочка здесь не нужна - аргументом fprint и должен быть указатель.

Дальше немного чёрт ногу сломит (откуда вы берёте такие примеры?)

int num[SIZE_NUMBER] = {1, 2, 3, 4, 5, 6}; - тут вроде ясно. Если мы пишем num - фактически пишем само значение.


int *p1 = &num
  • ;
  • - вот тут уже интересно. &num - указатель на num
Получается, в разыменованное (!) p прописываем в качестве значения указатель на num
А само p1 - это указатель на место, где записан указатель на num

int *p_mass_num[SIZE_NUMBER] = {p1, p2, p3, p4, p5, p6}; - здесь получается массив *p_mass_num из указателей на
указатели.

Посему получается, что в обоих случаях функции fprint скармливаются указатели на данные. Ну, а тип данных предваряется директивами (или как их там называют) форматирования.

Где я ошибся? Дело в том, что я сам перестал понимать, зачем fprint указатели, а не сами данные (со строкой-то понятно - там сама структура строк такая, хотя...)

ps: в любом случае, эти зубодробительные "преобразователи преобразователей" использовать в качестве учебных примеров настоятельно не советую. Потому что из-за того, "кто на ком стоял" сути не видно.
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

Nikopol

Здорово, понятно и доходчиво, а то я уже весь мозг сломал. И почему то об этом нигде не написано.  ;D