Что произойдет, если я использую «&» со строкой в ​​функции scanf? - PullRequest
0 голосов
/ 16 ноября 2018

Я только что увидел код в блоге. Использовано

scanf("%s",&T);

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

#include <stdio.h>
int main()
{
    double a=0.0, M[12][12];
    char T[2];
    int C,x,y;
    scanf("%d", &C);
    scanf("%s", &T);
    for(x=0;x<=11;x++)
    {
        for(y=0; y<=11; y++)
        {
        scanf("%lf", &M[x][y]);
        if(x==C)
            a+=M[x][y];
        }
    }
    if(T[0]=='S')
        printf("%.1lf\n",a);
    else if(T[0]=='M')
    {
        a=a/12.0;
        printf("%.1lf\n",a);
    }
    return 0;
}

Ответы [ 2 ]

0 голосов
/ 16 ноября 2018

Соответствующая часть фрагмента кода:

char T[2];
scanf("%s", &T);

&T - указатель на массив из двух символов (char (*)[2]). Это не тот тип, который нужен scanf для спецификатора %s: ему нужен указатель на символ (char *). Так что поведение программы не определено.

Правильный способ написать эту программу, как вы знаете, это

char T[2];
scanf("%s", T);

Поскольку T является массивом, когда он используется в большинстве контекстов, он «разлагается» на указатель на первый символ: T эквивалентен &(T[0]), который имеет тип char *. Это затухание не происходит, когда вы берете адрес массива (&T) или его размер (sizeof(T)).

На практике почти все платформы используют одинаковое представление для всех указателей на один и тот же адрес. Таким образом, компилятор генерирует точно такой же код для T и &T. Есть несколько редких платформ, которые могут генерировать другой код (я слышал о них, но не мог назвать одну). Некоторые платформы используют разные кодировки для «байтовых указателей» и «словесных указателей», потому что их процессор изначально адресует слова, а не байты. На таких платформах int * и char *, указывающие на один и тот же адрес, имеют разные кодировки. Преобразование между этими типами преобразует значение, но неправильное использование в чем-то вроде списка аргументов переменной может привести к неверному адресу. Однако я ожидаю, что такие платформы будут использовать байтовые адреса для массива символов. Существуют также редкие платформы, где указатель кодирует не только адрес данных, но также некоторую информацию о типе или размере. Однако на таких платформах информация о типе и размере должна быть эквивалентной: это блок из 2 байтов, начиная с адреса T, и адресуемый байт за байтом. Так что эта конкретная ошибка вряд ли окажет какое-либо практическое влияние.

Обратите внимание, что было бы совсем иначе, если бы у вас был указатель вместо массива во-первых:

char *T; // known to point to an array of two characters
scanf("%s", &T); // bad

Здесь &T - указатель на место в памяти, которое содержит адрес массива символов. Таким образом, scanf записывает символы, которые он читает, в том месте, где указатель T хранится в памяти, а не в том месте, на которое указывает T. Большинство компиляторов анализируют строку формата функций, таких как printf и scanf, и поэтому выдают сообщение об ошибке.

Обратите внимание, что char T[2] имеет место только для двух символов, и это включает нулевой байт в конце строки. Так что scanf("%s", T) имеет место только для чтения одного символа. Если на этом этапе входные данные содержат более одного непробельного символа, программа переполнит буфер. Чтобы прочитать один символ и сделать его односимвольной строкой, используйте

char T[2];
scanf("%c", T);
T[1] = 0;

В отличие от scanf("%s", T), он читает любой символ, даже пробел. Чтобы прочитать строку с ограничением длины, добавьте ограничение к спецификации %s. Никогда не следует использовать неограниченное число %s в scanf, поскольку при этом будет считано столько входных данных, сколько доступно, независимо от того, сколько места есть для хранения этого ввода в памяти.

char T[2];
scanf("%1s", T); // one less than the array size
0 голосов
/ 16 ноября 2018

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

Когда у вас есть массив t типа char[somevalue], когда вы говорите

scanf("%s",t);

t распадается на указатель на первый элемент, так что все в порядке.

С другой стороны, когда вы говорите &t, он имеет тип char (*)[somevalue]- указатель на массив, весь массив, а не указатель на начальный элемент массива.

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

...