2014-10-20 2 views
1

Я промежуточный программист на Python, но я пытаюсь научиться C. Это очень неприятный опыт.Почему printf производит странный вывод в моем простом скрипте?

Почему этот простой код ...

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

char name[15] = "George Lopez"; /* string representing your name */ 
char birthday[6] = "01-30-92"; /* string representing your bday */ 
int phone_number; /* declair int representing phone number */ 

int main() { 
    phone_number = 2222223333; /* int representing your phone number */ 
    printf("You said your name was: %s\n", name); 
    printf("And your birthday is %s?\n", birthday); 
    printf("And, so I can call you, your number is %d\n", phone_number); 
    return(0); /* exit program normally */ 
} 

производить этот вывод, когда телефонный номер 2222223333:

You said your name was: George Lopez 
And your birthday is 01-30-92? 
And, so I can call you, your number is -2072743963 

Этот выход, когда телефонный номер 9992223333:

error: 4_1_myinfo.c:16:3: warning: overflow in implicit constant conversion [-Woverflow] 
    phonenumber = 9992223333; 

И этот выход, если номер телефона 1112223333:

You said your name was: George Lopez 
And your birthday is 01-30-92? 
And, so I can call you, your number is 1112223333 

Я подозреваю, что это связано с тем, как C имеет дело с целыми числами. Возможно, ints в C имеют меньшую максимальную длину, чем ints в python, и это приводит к wanky output?

+5

Вне границ массива: 'char birthday [6] =" 01-30-92 ";', вызывая неопределенное поведение. – hmjd

+0

'birthday' должен вызывать обязательную диагностику при компиляции. Если вы (можете и делаете) игнорируете это, это не допустимая строка, поэтому передача его в 'printf' в качестве строки - UB. – Deduplicator

+0

Несмотря на свое представление в виде строки цифр, номер телефона не * действительно * номер. Также используйте строку. – chepner

ответ

3

У вас есть пара основных проблем здесь.

Во-первых, при использовании C-строки, избежать определения размера буфера, если вы можете, например, так:

char name[] = "George Lopez"; /* string representing your name */ 
char birthday[] = "01-30-92"; /* string representing your bday */ 

Если вы ДОЛЖНЫ заранее определили максимальную длину строки, используйте общий предел, как так:

#define MAX_STR_LEN 256 
char name[MAX_STR_LEN] = "George Lopez"; /* string representing your name */ 
char birthday[MAX_STR_LEN] = "01-30-92"; /* string representing your bday */ 

Если вы не планируете изменять их в будущем, сделать их const так:

const char* name = "George Lopez"; /* string representing your name */ 
const char* birthday = "01-30-92"; /* string representing your bday */ 

В вашем случае вы определили массив символов, который может содержать только 6 символов, но попытался наполнить 9 символов в нем «01-30-92», плюс завершающий символ \0 включен автоматически. Большая ошибка.

Вторая серьезная проблема заключается в том, что вы пытаетесь установить целочисленный литерал в тип данных, который не может его удерживать. В C максимальное подписанное 32-битное значение, 2,147,483,647, определяется как INT_MAX в <limits.h>. Вы должны сделать свой код следующим:

long long int phone_number = 2222222222L;

Обратите внимание на завершающий суффикс числового литерала, L? Это позволит компилировать ваш код, и использование long long int необходимо для создания переменной, которая может фактически сохранить это значение.

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

Листинг (Modified)


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

char name[] = "George Lopez"; /* string representing your name */ 
char birthday[] = "01-30-92"; /* string representing your bday */ 
long long int phone_number; /* declair int representing phone number */ 

int main() { 
    phone_number = 2222223333L; /* int representing your phone number */ 
    printf("You said your name was: %s\n", name); 
    printf("And your birthday is %s?\n", birthday); 
    printf("And, so I can call you, your number is %lld\n", phone_number); 
    return(0); /* exit program normally */ 
} 

Пример вывода


You said your name was: George Lopez 
And your birthday is 01-30-92? 
And, so I can call you, your number is 2222223333 
+0

Плохая идея переместиться из массива в указатель. Кстати, почему вы не указали постоянную указатель? – Deduplicator

+0

@Deduplicator Если данные предназначены только для чтения, подход указателя идеально подходит. Не нужно обязательно указывать указатель 'const'. Это только данные сохраняются, а не сам указатель. – DevNull

+0

Зачем вам нужна эта ненужная косвенность (и дополнительная переменная)? (Там может быть некоторая польза для него, если он был 'статическими const', хотя это спорно) Кроме того, я уверен, что вы хотите суффикс' LL' для 'длинной long', а не' L'. – Deduplicator

0

В C все типы данных имеют фиксированный размер. Обычно int представляет собой 32-битное значение (но это необязательно), поэтому оно будет иметь максимальное значение 2 147 483 647.

Ваше значение, 9,992,223,333 превышает это значение, и именно поэтому вы получаете предупреждение. Вам нужно использовать больший тип данных, например, 64-битное число. Обычно это будет long long, если вы находитесь в Windows, или long, если вы находитесь в Linux.

+1

или даже лучше использовать uint64_t для платформы вне зависимости. Он определен в stdint.h и совместим с C99 –

1

Я думаю, что лучший способ иметь дело с телефонными номерами - рассматривать их как строку. Вам понадобится строковый тип для «форматирования» ваших выходов, а также для проверки того, что номер телефона соответствует определенному «регулярному выражению» при рассмотрении пользовательских входов.

0

Чтобы понять, почему это происходит, вы должны понять, как эти цифры представленный в памяти. Я постараюсь дать вам представление об основном способе их работы и некоторых дополнительных ссылках, если вы заинтересованы. Поскольку компьютер понимает только 0 и 1, база 2 является очевидным выбором. Но есть проблема с отображением отрицательных чисел. Есть некоторые представления, такие как представление знаковой величины (в основном, бит, который представляет знак - если он установлен, число отрицательно), которые очень просты. Проблема в том, что такие представления имеют 2 способа представления 0: +0 и -0. Итак, используется умное представление, называемое двумя дополнениями. Для двоичного числа abcd (где a, b, c, d представляют свои биты), базовое значение 10 равно * 2^3 + b * 2^2 + c * 2^1 + d * 2^0. Если это число было 4-битным представлением 2-го дополнения, его базовое значение 10 было бы -a * 2^3 + b * 2^2 + c * 2^1 + d * 2^0. Итак, в дополнении два, первый бит имеет отрицательный вес. В представлении дополнения 4-битного двоичного кода, если у вас есть 0111 (десятичный символ 7), и вы пытаетесь добавить 1, вы получите 1000, что составляет -8. Это называется переполнением. Переполнение возникает только при добавлении чисел с одинаковым знаком и получении результата разного знака (добавление 2 положительных чисел приводит к отрицательному числу или добавлению 2 отрицательных чисел приводит к положительному числу).

В вашей программе происходит нечто подобное. Единственное отличие состоит в том, что INT имеет 32 бита, а не 4.

Дальнейшие ссылки: http://en.wikipedia.org/wiki/Binary_number http://en.wikipedia.org/wiki/Signed_number_representations

0

диапазон подписанного 32-битное целое [-2147483648, 2147483647]. Вы пытаетесь представить свой номер телефона как целое число со знаком.

a. When phone num is 1112223333, it's less than the max 2147483647, so you get theright output. 

b. 2222223333 is greater than the max 2147483647, but can still be represented as a 32-bit number, you get the 2's complement number which is -2072743963 

c. 9992223333 cannot be represented by a 32-bit number, i.e. it's even bigger than (2^32 - 1), that's why you such error.