Объявление массива структур

Захотелось получить объект структуры, один их элементов которой является массив другой структуры.
После выделения памяти делаю пробное заполнение и вывожу результат, однако похоже весь мой массив засунулся в первую свою строчку :). Помогите найти ошибку.

#include "stdafx.h"

struct ScreenRow {
    char Text[50];
    char Color[50];
};
struct Screen {
    ScreenRow** Rows;  // может надо ScreenRow* Rows[20]
    char X;
    char Y;
};
Screen* g_Screen;

int main()
{
    Screen* pScreen = new Screen;
    ScreenRow* pRows = new ScreenRow[20];
    for (int i = 0; i < 20; i++) {
        memset(pRows[i].Text, '.', 50); //странно что обращение не ->
        memset(pRows[i].Color, 1, 50);
    }
    pScreen->X = 0;
    pScreen->Y = 0;
    pScreen->Rows = &pRows;
    g_Screen = pScreen;
    printf("%s", g_Screen->Rows[0]->Text); //тут выводит все 2*20*50 символов, хотя я ожидаю 50
//  printf("%s", g_Screen->Rows[1]->Text); //тут падает
    return 0;
}

В printf спецификация %s выводит символы начиная с адреса, указанного аргументом и до символа '\0'. У тебя под массив из 20 структур ScreenRow выделяется непрерывный кусок памяти, который ты инициализируешь точками и единицами без единого '\0'. В g_Screen->Rows[0]->Text как раз хранится адрес этого куска памяти. Вот оно тебе всё и выводит на экран, пока не наткнется на случайный '\0' уже где-то за пределами твоей области памяти.

Основная ошибка в лишнем уровне косвенности в описании структуры: ScreenRow** Rows;. Исходя из общего смысла здесь Rows — это массив «строк» типа ScreenRow. А у тебя получился указатель на массив. Поэтому кстати и падает в printf("%s", g_Screen->Rows[1]->Text); тут должно быть printf("%s", (*(g_Screen->Rows))[1]->Text).

Вычищенный и исправленный код:

#include <stdio.h>
#include <string.h>

const size_t STR_LEN = 50;      // длина строки
const size_t NUM_OF_STRS = 20;  // количество строк

struct ScreenRow {
    char Text[STR_LEN + 1];     // резервируем место для символа конца строки
    char Color[STR_LEN + 1];
};
struct Screen {
    ScreenRow* Rows;
    char X;
    char Y;
};
Screen* g_Screen;

int main() {
    g_Screen = new Screen;
    g_Screen->Rows = new ScreenRow[NUM_OF_STRS];

    for (int i = 0; i < NUM_OF_STRS; i++) {
        memset(g_Screen->Rows[i].Text, '.', STR_LEN);
        memset(g_Screen->Rows[i].Color, 1, STR_LEN);
        // отмечаем концы строк
        g_Screen->Rows[i].Text[STR_LEN] = g_Screen->Rows[i].Color[STR_LEN] = '\0'; 
    }
    g_Screen->X = 0;
    g_Screen->Y = 0;
    printf("%s\n", g_Screen->Rows[0].Text);
    printf("%s\n", g_Screen->Rows[1].Text);

    delete[] g_Screen->Rows;    // почистить память!
    delete g_Screen;
    return 0;
}

Как я понял, это всё моделирует экран в режиме консоли? Ты не думал над вариантом что бы каждое знакоместо представлялось структурой

struct CharCell {
    char symbol;
    char color;
};

а экран потом представить тупо двумерным массивом этих структур?

А вообще я несколько удивлен сишным кодом с использованием new из С++.

Спасибо, работает. Получается что new ScreenRow и new ScreenRow[NUM_OF_STRS] — один и тот же тип данных, я не доволен :)
Про printf понял, это я для примера, в проекте смотрел в отладчике.
Двумерный массив не хочу, может мне понадобится добавить еще пару немассивных полей в ScreenRow.
Моих знаний еще не хватает отличать с от с++ :)

Получается что new ScreenRow и new ScreenRow[NUM_OF_STRS] — один и тот же тип данных

Не получается. Первый — структура, второй — массив структур. Это разные типы.

Про printf понял, это я для примера, в проекте смотрел в отладчике.

Если смотрел в отладчике, то должен был заметить, что во втором вызове адресуется неправильная область памяти. Это должно было навести на мысль.

Это разные типы

Почему тогда и то и другое имеет тип ScreenRow*?
Получается нет возможности (или необходимости?) при определении поля Screen.Rows установить что это будет массив структур известной длины, а не структура. Это же плохо.

Почему тогда и то и другое имеет тип ScreenRow*?

Во-первых, ни первое, но второе не имеют тип ScreenRow*. Во-вторых, еще раз почитайте про указатели. В книжке. А потом еще в другой книжке. Это нужно понять обязательно. Это — важно. В-третьих, ответьте на вопрос: чем отличается массив из N структур (N>1) от массива из одной структуры.

Ответить

Вы можете использовать разметку markdown для оформления комментариев и постов. Используйте функцию предпросмотра для проверки корректности разметки.

Пожалуйста, оформляйте исходный код в соответствии с правилами разметки. Для того, чтобы вставить код в комментарий, скопируйте его в текстовое поле ниже, после чего выделите то, что скопировали и нажмите кнопку «код» в панели инструментов. Иначе ваш код может принять нечитаемый вид.

Либо производите оформление кода вручную, следующим образом:

``` #include <iostream> using namespace std; int main() { // ... } ```

Предпросмотр сообщения

Ваше сообщение пусто.