Неопределенная длина символьных массивов - PullRequest
0 голосов
/ 02 июля 2019

Я думал, в чем разница между

    char[] = "hello world"

и

    char[20] = "hello world"

Я пытался написать этот короткий код:


    #include <stdio.h>
    #include <stdlib.h>


    int main(){
        int i;
        char str[20] = "hello world";
        for( i = 0; i<20; i++){
            if(str[i]=='\n')
                printf("\nExit character newline");
            else if(str[i]=='\0')
                printf("\nNull terminated..");
            else
                printf("\nCur: %c", str[i]);
        }
        return 0;

    }

, которыйвывод:


Cur: h
Cur: e
Cur: l
Cur: l
Cur: o
Cur:
Cur: w
Cur: o
Cur: r
Cur: l
Cur: d
Null terminated..
Null terminated..
Null terminated..
Null terminated..
Null terminated..
Null terminated..
Null terminated..
Null terminated..
Null terminated..

С другой стороны, когда я не определяю размер массива и просто использую

    char[] = "hello world"

Это дает мне такой вывод:

Cur: h
Cur: e
Cur: l
Cur: l
Cur: o
Cur:
Cur: w
Cur: o
Cur: r
Cur: l
Cur: d
Null terminated..
Cur: 
Null terminated..
Null terminated..
Null terminated..
Cur: 
Cur:  
Cur: a
Null terminated..

Я запутался в вышеприведенном выводе.Разве char [] = "hello world" просто заканчивается 12 элементами с нулевым терминатором, заполняющим последний элемент?Также, если я напечатаю char с% s, мое предположение будет правильным?

Ответы [ 4 ]

3 голосов
/ 02 июля 2019

Объявление `char str [] =" hello world "резервирует место для 12 символов, последний из которых равен нулю. В отличие от некоторых других языков. тем не менее, реализации C, как правило, не прилагают усилий для захвата доступа к массиву за пределами границ. Как правило, попытки чтения за пределами конца строки позволяют получить доступ к содержимому любого хранилища, которое следует за ними, но если только не используется реализация, позволяющая управлять размещением объектов (например, имея только один объект в блоке перевода, и использование спецификации компоновщика для принудительного размещения данных для этого модуля перевода непосредственно перед другим), чтение после конца строки не будет иметь предсказуемых последствий. Если кто-то использует агрессивный оптимизирующий компилятор, он может решить, что он может опустить любой код, который был бы уместен, только если программа попыталась получить доступ к данным за концом массива.

2 голосов
/ 02 июля 2019

Таким образом, в первом, char [20], у вас есть место, отведенное для максимум 20 символов, поэтому все они заканчиваются после последнего символа. Во втором случае, char [], у вас нет лишних пробелов. Таким образом, кажется, что когда вы пропускаете конец строки, вы становитесь памятью других частей вашего компьютера. Вот почему вы получаете случайные символы там.

Вот еще один поток укладки, который углубляется в это

Как объявить строки в C

0 голосов
/ 02 июля 2019

Я предлагаю вам попробовать эту программу:

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

int main() {
    char str1[] = "hello world";
    char str2[20] = "goodbye world";
    printf("str1: size = %zd, len = %zd\n", sizeof(str1), strlen(str1));
    printf("str2: size = %zd, len = %zd\n", sizeof(str2), strlen(str2));
}

(Если у вас более старый компилятор, который не принимает %zd, вы можете использовать "size = %d, len = %d\n", (int)sizeof(str1), (int)strlen(str1).)

Компилятор выдаст вам массивы, которые вы можете представить так:

      +---+---+---+---+---+---+---+---+---+---+---+---+
str1: | h | e | l | l | o |   | w | o | r | l | d |\0 |
      +---+---+---+---+---+---+---+---+---+---+---+---+

      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
str2: | g | o | o | d | b | y | e |   | w | o | r | l | d |\0 |   |   |   |   |   |   |
      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

(На самом деле, хотя я явно не показал их все, гарантировано, что все «пустые» ячейки к концуиз str2 также будет содержать \0.)

В общем случае, если вы попытаетесь получить доступ к памяти из определенного конца массива: (a) вы не найдете ничего интересного, и(б) это запрещено, хотя (в) компилятор C, как правило, не мешает вам попробовать.

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

for(int i = 0; i < 30; i++)
    printf("str1[%d] = '%c'\n", i, str1[i]);

Вы, вероятно, увидите строку «прощальный мир», скрывающуюся в памяти «с конца» str1.Если вы этого не сделаете, попробуйте поменять местами порядок str1 и str2:

char str2[20] = "goodbye world";
char str1[] = "hello world";

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

Еще одна вещь.Я хочу вернуться к тому, что вы сказали в комментарии.Вы сказали:

Я пытаюсь выяснить и понять, что может быть за нулевым терминатором после 'd' в мире приветствия.Я ожидал, что str[] заполнит все остальные нулевыми терминаторами, как и в случае с str[20].

Теперь, на самом деле, str[20] имеет «остальные, заполненные нулевыми терминаторами»потому что и только , потому что вы явно выделяете массив с большим количеством символов, чем нужно.Когда вы говорите str[] = "...", с другой стороны, вы получаете массив с точно символами, которые ему нужны (включая один , оканчивающийся \0).Когда вы объявляете str[] = "...", даже не имеет смысла говорить «остальное заполнено ...», потому что нет «отдыха» для заполнения.

0 голосов
/ 02 июля 2019

В c вы можете читать и писать за пределами массива. Это, конечно, неопределенное поведение. Но язык это позволяет.

Когда вы читаете память, которая не выделена для объекта, вы можете получить непредсказуемые значения, вы также можете оказаться в segfault

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...