Comportamento de printf ao imprimir uma% d sem fornecer nome da variável

StackOverflow https://stackoverflow.com/questions/437816

  •  22-07-2019
  •  | 
  •  

Pergunta

Eu apenas encontrou um problema estranho, eu estou tentando printf uma variável inteira, mas eu esqueci de especificar o nome da variável, i.

printf("%d");

em vez de

printf("%d", integerName);

Surpreendentemente, o compila programa, não há saída e não é aleatória. Na verdade, ele passa a ser o número inteiro que eu queria imprimir em primeiro lugar, que passa a ser m-1.

A declaração printf errorneous vai consistentemente saída de m-1 durante o tempo que o programa continua a funcionar ... Em outras palavras, ele está se comportando exatamente como se diz a declaração

printf("%d", m-1);

Qualquer um sabe a razão por trás deste comportamento? Estou usando g ++ sem nenhuma opção de linha de comando.

#include <iostream>
#define maxN 100
#define ON 1
#define OFF 0

using namespace std;

void clearArray(int* array, int n);
int fillArray(int* array, int m, int n);

int main()
{
    int n = -1, i, m;
    int array[maxN];
    int found;

    scanf("%d", &n);

    while(n!=0)
    {
        found=0;
        m = 1;
        while(found!=1)
        {
            if(m != 2 && m != 3 && m != 4 && m != 6 && m != 12)
            {
                clearArray(array, n);
                if(fillArray(array, m, n) == 0)
                {
                    found = 1;
                }
            }
            m++;
        }

        printf("%d\n");

        scanf("%d", &n);
    }

    return 0;
}

void clearArray(int* array, int n)
{
    for(int i = 1; i <= n; i++)
        array[i] = ON;
}

int fillArray(int* array, int m, int n)
{
    int i = 1, j, offCounter = 0, incrementCounter;

    while(offCounter != n)
    {
        if(*(array+i)==ON) 
        {
            *(array+i) = OFF;
            offCounter++;       
        }
        else 
        {
            j = 0;
            while((*array+i+j)==OFF)
            {
                j++;
            }
            *(array+i+j) = OFF;
            offCounter++;           
        }
        if(*(array+13) == OFF && offCounter != n) return 1;
        if(offCounter ==n) break;

        incrementCounter = 0;       
        while(incrementCounter != m)
        {
            i++;
            if(i > n) i = 1;
            if(*(array+i) == ON) incrementCounter++; 
        }       
    }

    return 0;
}
Foi útil?

Solução

Você diz que "Surpreendentemente as compilações de programa". Na verdade, não é surpreendente em tudo. C & C ++ permitem que funções tenham listas de argumentos variáveis. A definição para printf é algo como isto:

int printf(char*, ...);

O "..." significa que há zero ou mais argumentos opcionais para a função. Na verdade, uma das principais razões C tem argumentos opcionais é apoiar o printf e scanf família de funções.

C não tem conhecimento especial da função printf. No seu exemplo:

printf("%d");

O compilador não analisa a cadeia de formato e determinar que um argumento inteiro está faltando. Este é o código C perfeitamente legal. O fato de que você está faltando um argumento é uma questão semântica que aparece apenas em tempo de execução. A função printf vai supor que você tenha fornecido o argumento e ir procurá-lo na pilha. Ele vai pegar tudo o que acontece para estar lá. Acontece que no seu caso especial que está a imprimir a coisa certa, mas isso é uma exceção. Em geral, você terá dados de lixo. Este comportamento irá variar de compilador para compilador e também irá mudar dependendo do que opções de compilação que você usa; se você ligar otimização do compilador você provavelmente irá obter resultados diferentes.

Como apontado em um dos comentários a minha resposta, alguns compiladores têm "cotão", como recursos que podem realmente detectar chamadas printf / scanf errôneas. Isso envolve o compilador analisar a string de formato e determinar o número de argumentos extras esperados. Esse é um comportamento muito especial compilador e não detectar erros no caso geral. ou seja, se você escrever sua própria função "printf_better", que tem a mesma assinatura como printf, o compilador não irá detectar se os argumentos estão faltando.

Outras dicas

O que acontece se parece com isso.

printf("%d", m);

Na maioria dos sistemas o endereço da corda vai ter empurrado na pilha, e depois 'm' como um inteiro (assumindo que é um int / short / char). Não há nenhum aviso porque printf é basicamente declarado como 'int printf(const char *, ...);' -. A ... significa 'vale tudo'

Então, já que 'vale tudo' algumas coisas estranhas acontecem quando você colocar variáveis ??lá. Qualquer tipo integral menor do que um int vai como um int - coisas assim. Enviando nada é ok também.

Na implementação printf (ou pelo menos uma 'simples' implementação) você vai encontrar o uso de va_list e va_arg (nomes em algum momento diferem ligeiramente baseado em conformidade). Estes são o que um usa a implementação de andar em torno da '...' parte da lista de argumentos. Problema aqui é que não há nenhum tipo de verificação. Como não há verificação de tipo, printf vai puxar dados aleatórios fora da pilha de execução quando se olha para a string ("%d") formato e acha que é suposto ser um 'int' seguinte.

Aleatório tiro no escuro diria que a chamada de função que você fez pouco antes printf possivelmente passou 'm-1' como de segunda parm? Essa é uma das muitas possibilidades - mas seria interessante se este passou a ser o caso. :)

Boa sorte.

A propósito - (? GCC eu acredito) compiladores mais modernos têm avisos que podem ser habilitados para detectar este problema. Lint faz tão bem que eu acredito. Infelizmente eu acho que com VC você precisa usar o / analisar bandeira em vez de ficar de graça.

Você está olhando para a pilha. Alterar os valores otimizador, e isso pode mudar. Alterar a ordem das declarações de suas variáveis ??(particularmente) m. Faça m uma variável registo. Faça m uma variável global.

Você verá algumas variações no que acontece.

Este é semelhante ao famoso hacks de saturação de buffer que você começa quando você faz simplista I / O.

Enquanto eu altamente duvido que isso resultaria em uma violação de memória, o número inteiro que obtém é lixo indefinida.

Você encontrou um comportamento. Poderia ter sido qualquer outro comportamento, incluindo um acesso de memória inválido.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top