C: странное явление - PullRequest
       38

C: странное явление

0 голосов
/ 19 апреля 2011

кто-нибудь может объяснить это явление.

#include "stdio.h"
#include "stdlib.h"

int main()
{
    char foo[]="foo";
    char bar[3]="bar";
    printf("%s",foo);
    printf("\n");
    printf("%s",bar);
    return 0;
}

Результат:

foo
barfoo

Если я изменю порядок и создаю бар перед foo, я получу правильный вывод.

#include "stdio.h"
#include "stdlib.h"

int main()
{
    char bar[3]="bar";
    char foo[]="foo";
    printf("%s",foo);
    printf("\n");
    printf("%s",bar);
    return 0;
}

Результат:

foo
bar

И еще один.

#include "stdio.h"
#include "stdlib.h"

int main()
{

    char foobar[]="foobar";
    char FOO[3]={'F','O','O','\0'};
    char BAR[3]="BAR";
    printf("%s",foobar);
    printf("\n");
    printf("%s",FOO);
    printf("\n");
    printf("%s",BAR);
    return 0;
}

Результат:

foobar
FOOfoobar
BARFOOfoobar

Ответы [ 11 ]

7 голосов
/ 19 апреля 2011

Строка "bar" состоит из четырех символов: {'b', 'a', 'r', '\0'}.Если вы явно указываете длину массива, вам нужно выделить как минимум четыре символа:

char bar[4]="bar";

При этом:

char bar[3]="bar";
printf("%s",bar);

Вы вызываете неопределенное поведение как barпеременная не имеет нулевого терминатора.Может произойти все, что угодно.В этом случае, в частности, компилятор разместил два массива в памяти:

'b' 'a' 'r' 'f' 'o' 'o' '\0'
 ^           ^
bar[3]      foo[4]

Когда вы печатаете bar, он продолжает читать, пока не найдет нулевой терминатор, любой нулевой терминатор.Так как bar не имеет ни одного, он продолжает идти, пока не найдет тот в конце "foo\0".

2 голосов
/ 19 апреля 2011

Другие авторы уже объяснили вам, что в вашем примере

char bar[3] = "bar";

терминатор строки не помещается в массив, поэтому строка заканчивается без конца.Формально говоря, это даже не строка (так как строки должны заканчиваться по определению).Вы пытаетесь напечатать не строку как строку (используя спецификатор формата %s), что приводит к неопределенному поведению.Неопределенное поведение - это именно то, что вы наблюдаете.

В языке C ++ (например) объявление

char bar[3] = "bar";

было бы недопустимым, поскольку C ++ не позволяет "падать" нулевому терминатору вдекларация, как это.C позволяет это, но только для неявного символа нулевого терминатора.Объявление

char bar[3] = "barr";

недопустимо как в C, так и в C ++.

Опять же, трюк с отсутствующим нулем работает в C только с неявным нулевым символом-ограничителем.Он не работает ни с одним явным инициализатором: вам не разрешено явно указывать больше инициализаторов, чем имеется элементов в массиве.Что подводит нас к вашему третьему примеру.В третьем примере у вас есть объявление

char FOO[3] = { 'F', 'O', 'O', '\0' };

, которое явно указывает 4 инициализатора для массива размера 3. Это недопустимо в C. Ваш третий пример не компилируется.Если ваш компилятор принял его без диагностического сообщения, ваш компилятор должен быть поврежден.Поведение вашей третьей программы не может быть объяснено языком Си, поскольку это не программа Си.

2 голосов
/ 19 апреля 2011

Если вы объявите char bar[3]="bar";, то вы объявите массив char без места для нулевого терминатора. Так что printf() будет просто читать char с из памяти, печатая их на консоли, пока не встретит '\0'.

1 голос
/ 19 апреля 2011

Как вы намекали на знание в своей строке char FOO[3]={'F','O','O','\0'};, это нулевая проблема завершения.Проблема в том, что нулевой терминатор является символом.Если вы выделяете память для 3 символов, вы не можете поместить 4 символа в это место (он просто берет первые 3 и усекает остальные).

0 голосов
/ 19 апреля 2011

Эта строка

char bar[3]="bar";

вызывает неопределенное поведение, так как "bar" - это четыре символа, заботящиеся о '\0'. Таким образом, у вас bar массив должен быть четыре байта.

неопределенное поведение означает, что все может случиться, включая хорошие и плохие вещи

0 голосов
/ 19 апреля 2011

Виновником является эта строка:

char bar[3]="bar";

Это приводит к тому, что в созданном вами массиве длины 3 будут только 'b', 'a' и 'r'.

Теперь, когда это так, строка в foo - это 'f', 'o', 'o' и '\ 0', и ей было присвоено смежное местоположение с баром. Итак, память выглядела так:

b | a | r | f | o | o | \0

Надеюсь, это проясняет.

0 голосов
/ 19 апреля 2011

Пожалуйста, прочитайте этот подробный ответ, который даст понимание этого ...

Причина, по которой вы видите "забавные" вещи, заключается в том, что строки не заканчиваются NUL ...

0 голосов
/ 19 апреля 2011

char bar[3]="bar"; не оставляет достаточно места для добавления завершающего символа '\0'.

Если вы делаете char bar[4]="bar";, вы должны получить ожидаемый результат.

0 голосов
/ 19 апреля 2011
Строка

не заканчивается нулем, поэтому printf продолжает следовать за массивом до тех пор, пока не получит символ «\ 0».Стек устроен так, что bar и foo находятся в памяти рядом друг с другом.Единственный способ узнать размер массива в C - это найти нулевой терминал.Поэтому, если вы разместите свой стек в памяти, он будет выглядеть следующим образом:

  0    1    2    3    4    5    6
 'b'  'a'  'r'  'f'  'o'  'o'  '\0'
  ^bar begins    ^foo begins

Сказав foo [], компилятор устанавливает размер foo на основе строки константы, с которой он инициализирован.Он достаточно умный, чтобы из 4 символов включить нулевой терминатор: '\ 0'.

Чтобы решить эту проблему, размер столбца должен быть равен 4, то есть:

 char bar[4] = "bar"; // extra space for null terminal

илилучше, дайте компилятору разобраться, как вы это сделали с foo:

 char bar[] = "bar"; // compiler adds null term character('\0')
0 голосов
/ 19 апреля 2011

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

В следующем примере FOO имеет размер 3, но выдают ему четыре элемента.При этом многие BAR не имеют нулевого значения.

char FOO[3]={'F','O','O','\0'};
char BAR[3]="BAR";
...