Один человек имеет право смотреть на другого свысока только тогда, когда он помогает ему подняться.
Габриэль Гарсиа Маркес

Меню навигации для мобильных

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

Автор 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

#1
Наверное потому, что
Цитата: 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

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

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