Символ завершения в массиве символов - PullRequest
2 голосов
/ 23 октября 2019

Я пытаюсь напечатать некоторые символы на экране. Я использовал большое количество неправильных реализаций. Например:

Пример 1:

#include <stdio.h>

int main(int argc, char const *argv[]) {
  char hello[6] = {'h', 'e', 'l', 'l', 'o', '\n'};
  char bye[5] = {'b', 'y', 'e', '\n', '\0'};
  char end[] = "end";
  char dot = '.';
  char oops[] = {'o', 'o', 'p', 's'};

  printf("%s\n", hello);
  printf("%s\n", bye);
  printf("%s\n", end);
  printf("%s\n", dot);
  printf("%s\n", oops);
  return 0;
}

Выход 1:

enter image description here

Вопросы:

Почему символ dot был напечатан до bye?

Что это за мусор после dotсимвол?

Пример 2 ( удалено dot объявление / определение, печать ):

#include <stdio.h>

int main(int argc, char const *argv[]) {
  char hello[6] = {'h', 'e', 'l', 'l', 'o', '\n'};
  char bye[5] = {'b', 'y', 'e', '\n', '\0'};
  char end[] = "end";
  char oops[] = {'o', 'o', 'p', 's'};

  printf("%s\n", hello);
  printf("%s\n", bye);
  printf("%s\n", end);
  printf("%s\n", oops);
  return 0;
}

Выход 2:

enter image description here

Вопросы

Мусор все еще там! Я чувствую себя хорошо для них, но почему end был напечатан дважды?

Мне удалось сделать это правильно, используя символ завершения всякий раз, когда он должен использоваться, но почему у меня есть такая несогласованность в печати? Я пришел из Java-фона и уже чувствую себя странно!

Ответы [ 2 ]

1 голос
/ 23 октября 2019

Ваш первый массив строк не заканчивается нулем.

 char hello[6] = {'h', 'e', 'l', 'l', 'o', '\n'};

При передаче строки, которая не заканчивается нулем, в printf (и другие функции, связанные со строками, такие как strlen, strdup, strcat и т. Д.), Поведениене определеноОн может печатать больше байтов после вашего массива, пока не достигнет нулевого (нулевого) байта (как в вашем случае, показанного как мусор), или может вызвать сбой программы.

Чтобы исправить поведение, вам нужны всеваши строковые массивы оканчиваются нулем, то есть в конце имеют символ '\ 0'.

Если вы инициализируете строку, подобную этой

 char end[] = "end";

Она автоматически завершается нулем и получает правильную длинув объявленный массив символов.

1 голос
/ 23 октября 2019

Причина, по которой вы получаете странный вывод, заключается в том, что у вас неопределенное поведение. Функции, обрабатывающие строки (включая печать их с помощью спецификатора формата %s с printf), ожидают, что они будут завершены нулем, в противном случае вы получите неопределенное поведение. И если это произойдет, никакой конкретный результат не гарантируется. Он может работать как положено, может выдавать странные результаты, может вызвать сбой программы или сделать что-то другое. Обычно при печати строк он просто продолжает печатать до тех пор, пока не произойдет нарушение доступа, и он не выйдет из строя или пока не встретит следующий нулевой байт (что, вероятно, произойдет раньше, чем позже, и, пока это не произойдет, он напечатает «мусор»).

Вот почему изменение внешне несвязанных вещей может изменить UB для проявления по-разному. Из-за изменений в Примере 2, стек выглядит по-разному, и когда к нему неправильно обращаются из-за UB, он получит доступ к различным вещам. Возможно, что точка, напечатанная в Примере 1 при печати hello, на самом деле является точкой из char dot = '.';.

char hello[6] = {'h', 'e', 'l', 'l', 'o', '\n'};

. Печать это неопределенное поведение, поскольку она не завершена нулем.

char bye[5] = {'b', 'y', 'e', '\n', '\0'};

Вы можете напечатать это как строку, так как оно завершено нулем.

char end[] = "end";

Поскольку вы сделали это со строковым литералом, терминатор нуля добавляется автоматически;Вы можете распечатать это. char end[] - это массив четырех символов: 'e', 'n', 'd' и '\0'.

char dot = '.';

Это один char, а нестрока. Печать с %s - неопределенное поведение, и ваш компилятор, вероятно, предупредит вас. Вместо этого напечатайте его с помощью %c.

char oops[] = {'o', 'o', 'p', 's'};

То же, что и первый, нулевой терминатор отсутствует, поэтому печать с помощью %s является неопределенным поведением.

мусор все еще там! Я чувствую себя хорошо для них, но почему конец был напечатан дважды?

Компиляторы часто помещают все строки рядом друг с другом в двоичном файле программы. Так что может случиться так, что ваша строка end появится сразу после не завершенного нулем oopsТаким образом, в памяти он выглядит как oopsend\0, поэтому он может печатать "oopsend" (или делать что-то совершенно иное, поскольку оно все еще не определено).

В общем, мало что можно извлечь из удивления, почему UBделает то, что делает, потому что он не согласован (он может делать что-то другое каждый раз, когда вы запускаете его на другом компиляторе / машине, или работать, пока вы не продемонстрируете это кому-то другому, а затем внезапно не завершите программу)Просто посмотрите на это как на что-то неправильное, чего не следует делать.

...