Мне трудно понять процесс заполнения многомерного массива. Почему мой код печатает одну букву несколько раз?

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

Изначально я не делал указатели на массивы, но это приводило к немедленному падению программы. Именно поэтому они являются указателями на символы. Если бы кто-нибудь также мог объяснить, почему они должны быть указателями и почему они печатаются именно так, это было бы очень полезно.

#include <stdio.h>

int main() {
  char arr[2][2];
  char str[20];
  for(int i = 0; i < 2; i++){
    for (int j = 0; j < 2; j++){
      printf("%s\n", "please put in a string: ");
      scanf("%s\n", str[0]);
      arr[i][j] = str[0];
    }
  }
  for(int i = 0; i < 2; i++){
    for (int j = 0; j < 2; j++){
      printf("arr[%d][%d] == %s\n", i,j,arr[i][j]);
    }
  }
  return 0;
}

вывод, который я получил:

please put in a string:
pleasework
what
please put in a string:
s
please put in a string:
f
please put in a string:
g
arr[0][0] == f
arr[0][1] == f
arr[1][0] == f
arr[1][1] == f

Не правильные выходы


person rashonmyeed    schedule 02.02.2019    source источник
comment
Включите предупреждения компилятора. scanf("%s\n", str[0]); — это ошибка, вы передаете (неинициализированное) значение char функции, которая ожидает тип char *, указатель на char. Это должно быть scanf("%19s", str);, где 19 предотвращает переполнение буфера, и я удалил новую строку.   -  person Weather Vane    schedule 02.02.2019
comment
^^^^^ если это, по крайней мере, не выдает предупреждение компилятора, изучите свои инструменты и выясните, что необходимо, чтобы (а) поднять ваши предупреждения до педантичных уровней и (б) рассматривать все предупреждения как ошибки. Почти во всех случаях предупреждение следует рассматривать как ошибку, а в случае запуска программы на C абсолютно все предупреждения следует рассматривать как ошибки.   -  person WhozCraig    schedule 02.02.2019
comment
Вы используете неправильный спецификатор формата %s в printf("arr[%d][%d] == %s\n",i,j,arr[i][j]);, который должен быть %c.   -  person Weather Vane    schedule 02.02.2019
comment
Спасибо за советы. Я обновил свой вопрос текущими выводами. Когда я вставил %c, он дал мне несколько странных символов, поэтому я изменил его на %s, и он печатает только последний.   -  person rashonmyeed    schedule 02.02.2019
comment
У вас есть char *str[20] — массив из 20 указателей на символы, которые никуда не указывают. Вам нужно, чтобы они указывали на место с достаточным объемом памяти, или, может быть, вы имели в виду char str[20]; — в этом случае вам нужно настроить вызов на scanf() — вам нужно убрать суффикс [0]. Что бы ни случилось, вам также нужно убедиться, что вы читаете в отдельную память для каждой строки или копируете то, что было прочитано в str, во вновь выделенное пространство.   -  person Jonathan Leffler    schedule 02.02.2019
comment
Пожалуйста, не меняйте вопрос, вы спросили то, что спросили. Вы всё равно не правильно скопировали из комментариев: откатились.   -  person Weather Vane    schedule 02.02.2019
comment
@WeatherVane: когда нет ответов, можно изменить вопрос. Комментарии эфемерны; им не придается большого значения. Раз есть ответ, то тормоза должны продолжаться — изменение вопроса требует большей осторожности. До тех пор...   -  person Jonathan Leffler    schedule 02.02.2019
comment
@JonathanLeffler OP изменил исходное char str[20]; на char *str[20], которое представило ваш комментарий. Изменение вопроса затрудняет отслеживание комментариев, если только сообщение не было опубликовано правильно. Извините, я откатывал его до того, как появился ваш комментарий.   -  person Weather Vane    schedule 02.02.2019


Ответы (1)


Если вы хотите просто узнать о:

Мне сложно понять процесс заполнения многомерного массива.

затем забудьте строку и char и сделайте что-нибудь простое, например, используйте int. Следующий пример кода показывает именно это.

Самый простой случай: заполнение многомерного массива любого типа

#include <stdio.h>

int main() {
  int arr[2][2];
  int str;
  for(int i = 0; i < 2; i++){
    for (int j = 0; j < 2; j++){
      printf("please put in a number [%d][%d]: ", i,j);
      scanf("%d", &str);
      arr[i][j] = str;
    }
  }
  for(int i = 0; i < 2; i++){
    for (int j = 0; j < 2; j++){
      printf("arr[%d][%d] == %d\n", i,j,arr[i][j]);
    }
  }
  return 0;
}
$ ./stackoverflow
please put in an number[0][0]: 1
please put in an number[0][1]: 2
please put in an number[1][0]: 34
please put in an number[1][1]: 450

arr[0][0] == 1
arr[0][1] == 2
arr[1][0] == 34
arr[1][1] == 450

Заполнить символы

Если вы хотите использовать одиночные символы, вы также можете использовать следующий код, НО вы должны быть осторожны. scanf работает таинственным образом. Обратите внимание на пробел перед %c. То есть, чтобы потреблять \n, который есть, вы нажимаете ввод. Вы можете прочитать здесь, но именно поэтому вам пришлось сначала дважды введите какой-то ввод и нажмите ввод. Честно говоря, я бы использовал что-то другое, кроме scanf, например, fgets, но, поскольку вы использовали scanf, я показал код, который также использует его.

#include <stdio.h>

int main() {
  char arr[2][2];
  char str;
  for(int i = 0; i < 2; i++){
    for (int j = 0; j < 2; j++){
      printf("%s", "please put in a character: ");
      if (scanf(" %c", &str) == 1)  { arr[i][j] = str; }
    }
  }
  for(int i = 0; i < 2; i++){
    for (int j = 0; j < 2; j++){
      printf("arr[%d][%d] == %c\n", i,j,arr[i][j]);
    }
  }
  return 0;
}
$ ./stackoverflow
please put in a character: a
please put in a character: b
please put in a character: c
please put in a character: d

arr[0][0] == a
arr[0][1] == b
arr[1][0] == c
arr[1][1] == d

Заполнить строки

Если вы хотите читать строки, вам нужно сделать это немного по-другому. Вам не нужен двумерный массив. Вам нужен трехмерный массив, потому что строки сами по себе являются массивами символов. Без 3D-массива вы перезаписали 2D-массив и получили только последнее значение, напечатанное несколько раз. Что еще хуже, так как ваш многомерный массив имеет размер 2x2, а входная строка имеет размер 1xn, а где n больше 2, вы бы получили переполнение буфера. Следующий код исправляет это.

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

int main() {
  char arr[2][2][20];
  char str[20];
  for(int i = 0; i < 2; i++){
    for (int j = 0; j < 2; j++){
      printf("%s", "please put in a string: ");
      if (scanf("%s", &str[0]) == 1)
      {
          // arr[i][j] = &str[0];
          strncpy(arr[i][j], str, 20);
      }
    }
  }
  for(int i = 0; i < 2; i++){
    for (int j = 0; j < 2; j++){
      printf("arr[%d][%d] == %s\n", i,j, arr[i][j]);
    }
  }
  return 0;
}
$ ./stackoverflow
please put in a string: ab
please put in a string: cd
please put in a string: ef
please put in a string: gh

arr[0][0] == ab
arr[0][1] == cd
arr[1][0] == ef
arr[1][1] == gh

И на всякий случай вот 4-й вариант, в котором вместо массива 3D-символов используется char* arr[2][2]. Это не сильно меняет, но, как я уже сказал, просто на всякий случай.

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

int main() {
    char* arr[2][2];
    char str[20];
    for(int i = 0; i < 2; i++){
        for (int j = 0; j < 2; j++){
            printf("%s", "please put in a string: ");
            if (scanf("%s", &str[0]) == 1)
            {
                arr[i][j] = (char*)malloc(20*sizeof(char));
                strncpy(arr[i][j], str, 20);
            }
        }
    }

    for(int i = 0; i < 2; i++){
        for (int j = 0; j < 2; j++){
            printf("arr[%d][%d] == %s\n", i,j, arr[i][j]);
        }
    }
  return 0;
}
person Duck Dodgers    schedule 02.02.2019
comment
ой, этот старый глючный strncpy.. Обратите внимание, что ваша программа может переполниться на scanf(" %s", &str[0]), поэтому использовать %s в scanf не рекомендуется. Всегда ограничивайте количество символов, например. scanf("%19s", &str[0]). Кроме того, пробел перед %s избыточен. - person KamilCuk; 02.02.2019
comment
@KamilCuk, во-первых, большое спасибо за ваш отзыв (так как, по крайней мере, ОП, я думаю, ушел спать, я думал, что здесь никто даже не обращает внимания.) Хорошо, тогда я кое-что узнал. Я не знал, что с помощью scanf("19s",... можно ограничить количество символов. Спасибо за совет. И я согласен, пробел перед %s избыточен (и я соответствующим образом отредактировал свой ответ), но просто для уверенности, и поскольку я также проверил это, пробел перед %c не является лишним. p.s. почему strncpy глючит? Потому что он не завершается нулем? - person Duck Dodgers; 02.02.2019
comment
Ну да, из-за этого. И из-за этого существуют странные функции, такие как lstrcpy и strlcpy. Windows запретила strncpy и разрешила только strncpy_s. В Интернете есть тексты о том, почему strncpy был плохим дизайном. Но в вашем случае я думаю, что это безопасно - вы знаете, что из scanf вы на 100% получите строку с нулевым завершением (или неопределенное поведение), поэтому вы можете безопасно использовать strncpy. - person KamilCuk; 03.02.2019
comment
@KamilCuk, а, хорошо. Большое спасибо. Я узнал что-то новое сегодня. - person Duck Dodgers; 03.02.2019