Свойства Scanf и strtol при печати целочисленного представления строки

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

char s[5];
s[0] = '1';
s[1] = '2';
s[2] = '3';
s[3] = '4';
s[4] = '5';

и измените массив символов в s[1] на «5», тогда печать такого массива должна вернуть «15345». Теперь мой вопрос касается функций scanf и strtol. Когда я дважды вставляю значения в массив с помощью scanf, используя строки разного размера, почему функция strtol не преобразует ВЕСЬ массив?

Вот мой код в качестве примера:

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

int main(){
    char bytes[5];
    printf("enter size 1: ");
    scanf("%s", bytes);

    printf("the size is: %ld\n", strtol(bytes, NULL, 10));

    printf("enter size 2: ");
    scanf("%s", bytes);

    printf("the size is: %ld\n", strtol(bytes, NULL, 10));

    return 0;

}

Итак, представьте эти пользовательские данные:

10000

затем программа распечатает «размер 10000»

затем пользователь вводит:

100

затем программа печатает "размер равен 100"

почему он снова не печатает «размер 1000»? Я сохранил только 100 байтов, разве остальные элементы массива байтов из первого ввода не должны оставаться неизменными, а strtol должен правильно преобразовать остальную часть массива?

На мой взгляд, когда программа сохраняет первый ввод 10000 байтов в массив, это выглядит в тот момент так

байты = {1,0,0,0,0}

затем, когда пользователь вводит 100, массив выглядит так же, поскольку он изменил только значения первых 3 элементов, а остальная часть массива должна остаться прежней:

байты = {1,0,0,0,0}

с этим strtol преобразует весь массив в 10000, верно?

Действительно ли scanf «очищает» остальную часть массива при сохранении значений в один и тот же адрес памяти?


person john    schedule 04.10.2015    source источник
comment
Вы неправильно завершаете строки нулем, что приводит к неопределенному поведению. Кроме того, вы индексируете за пределами границ. Опять же, не определено.   -  person EOF    schedule 04.10.2015
comment
@WeatherVane исправлено.   -  person john    schedule 04.10.2015


Ответы (3)


Я знаю, что строки - это просто массив символов со смежными адресами памяти.

Не совсем. В C строка также заканчивается нулем. То есть строка заканчивается первым символом, имеющим нулевое значение. Например

char a[6] = { 'h', 'i',  0 , 'h', 'o', 0 }; // print(a) prints "hi"
char b[6] = { 'h', 'e', 'l', 'l', 'o', 0 }; // print(b) prints "hello"
char c[5] = { 'h', 'e', 'l', 'l', 'o' };    // print(c) will attempt to print "hello" followed by whatever characters happen to follow c[4] in memory, until it hits a zero value. But that may be reading outside the memory bounds of your application, or indeed your system, so anything can happen.

Итак, когда у вас есть массив символов: <snip>

Если вы расширите s до char s[6] и установите s[5] = 0, ваши предположения об изменении s[1] и его печати будут правильными.

Теперь мой вопрос касается функций scanf и strtol. Когда я дважды вставляю значения в массив с помощью scanf, используя строки разного размера, почему функция strtol не преобразует ВЕСЬ массив?

Сначала предложение, после каждой scanf("%s", bytes); строки вставьте следующее:

printf("bytes = { %02x, %02x, %02x, %02x, %02x } (%02x)",
        bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5] );

Запустите свой тестовый код с этим изменением и проверьте, что печатает эта строка. Если вы это видите, надеюсь, вы увидите ответ на свой вопрос о scanf и strtol.

Я аннотирую ваш код некоторыми комментариями ниже, указывающими содержимое bytes, используя ? как unknown :

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

int main(){
    char bytes[5];
    printf("enter size 1: ");
    scanf("%s", bytes);  // 10000<return>

    // bytes {  ? ,  ? ,  ? ,  ? ,  ?  } bytes[5] = ?
    printf("the size is: %ld\n", strtol(bytes, NULL, 10));
    // bytes { '1', '0', '0', '0',' 0' } bytes[5] = 0 !!! Note overflow

    printf("enter size 2: ");
    scanf("%s", bytes);  // 100<return>
    // bytes { '1', '0', '0', 0,' 0' } Note bytes[3] changes from '0' to 0

    printf("the size is: %ld\n", strtol(bytes, NULL, 10));

    return 0;

}

Короче говоря,

Действительно ли scanf «очищает» остальную часть массива при сохранении значений в один и тот же адрес памяти?

Он не очищает его, но вы читаете строку (формат = "%s"), поэтому scanf добавит соответствующий завершающий ноль в конец строки, которую вы читаете.

person Sigve Kolbeinson    schedule 04.10.2015

Вы упустили важное свойство строк c. Они должны заканчиваться байтом NUL, также известным как '\0'.

Это означает, что если вы запишете «10000» в 5-байтовый массив, вы нарушите правила.

Функция scanf будет преобразовывать символы в строку для %s до тех пор, пока не встретится пробел. Это небезопасная операция. Вы должны ограничить длину преобразования чем-то вроде scanf("%4s", bytes). Потому что документы scanf говорят:

Преобразования ввода строки сохраняют завершающий нулевой байт ('\0'), чтобы отметить конец ввода; максимальная ширина поля не включает этот терминатор.

Эта строка из документа также объясняет, почему вы получаете «100» для размера 2. Потому что scanf записал {'1', '0', '0', '\0'} в ваш массив bytes.

person Zan Lynx    schedule 04.10.2015

Простой ответ:

scanf() завершит ваш массив символов символом \0. Он не очищает оставшуюся часть массива.

Вот простая программа, которая доказывает это:

#include <stdio.h>

int main(void) {
    char str[100];

    scanf("%s", str); // Inputing 0123456789
    printf("String : %s\n", str);

    scanf("%s", str); // Inputing 01234
    printf("String 2 : %s\n", str); // str should be { '0', '1', '2', '3', '4', '\0', '6', ... }

    printf("Proof : %s", str + 6); // Outputs 6789
    return 0;
}

scanf заменит массив тем, что он нашел, и добавит \0 в конце. Поэтому остальная часть массива остается нетронутой и по-прежнему доступной.

В вашей ситуации вот как выглядит ваш массив в памяти:

  • Перед вторым scanf() : { '1', '0', '0', '0', '\0' } // 1000

  • После второго scanf() : { '1', '0', '0', '\0', '\0' } // 100

person Corb3nik    schedule 04.10.2015