C Переменные и типы

Введение в работу с переменными в C и основные типы

C - это статически типизированный язык.

Это означает, что любая переменная имеет связанный тип, и этот тип известен во время компиляции.

Это сильно отличается от того, как вы работаете с переменными в Python, JavaScript, PHP и других интерпретируемых языках.

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

В этом примере мы инициализируем переменнуюageс типомint:

int age;

Имя переменной может содержать любую заглавную или строчную букву, может содержать цифры и символ подчеркивания, но не может начинаться с цифры.AGEиAge10допустимые имена переменных,1ageне является.

Вы также можете инициализировать переменную при объявлении, указав начальное значение:

int age = 37;

После объявления переменной вы сможете использовать ее в своем программном коде и изменить ее значение в любое время, используя=оператор, например, как вage = 100;при условии, что новое значение имеет тот же тип.

В этом случае:

#include <stdio.h>

int main(void) {
	int age = 0;
	age = 37.2;
	printf("%u", age);
}

компилятор выдаст предупреждение во время компиляции и преобразует десятичное число в целое число.

ВCвстроенные типы данныхint,char,short,long,float,double,long double. Давайте узнаем о них больше.

Целые числа

C предоставляет нам следующие типы для определения целочисленных значений:

  • char
  • int
  • short
  • long

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

Вchartype обычно используется для хранения букв таблицы ASCII, но его можно использовать для хранения небольших целых чисел из-128к127. Требуется минимум 1 байт.

intзанимает не менее 2 байтов.shortзанимает не менее 2 байтов.longзанимает не менее 4 байтов.

Как видите, одинаковые значения для разных сред не гарантируются. У нас есть только указание. Проблема в том, что точные числа, которые могут храниться в каждом типе данных, зависят от реализации и архитектуры.

Нам гарантировано, чтоshortне длиннее, чемint. И нам гарантированоlongне короче чемint.

Стандарт спецификации ANSI C определяет минимальные значения каждого типа, и благодаря ему мы, по крайней мере, можем знать, какое минимальное значение мы можем ожидать в нашем распоряжении.

Если вы программируете C на Arduino, разные платы будут иметь разные ограничения.

На плате Arduino Unointхранит 2-байтовое значение в диапазоне от-32,768к32,767. On a Arduino MKR 1010, intхранит 4-байтовое значение в диапазоне от-2,147,483,648к2,147,483,647. Довольно большая разница.

На всех платах Arduinoshortхранит 2-байтовое значение в диапазоне от-32,768к32,767.longхранить 4 байта, начиная с-2,147,483,648к2,147,483,647.

Беззнаковые целые числа

Для всех вышеуказанных типов данных мы можем добавитьunsignedчтобы начать диапазон с 0 вместо отрицательного числа. Во многих случаях это может иметь смысл.

  • unsigned charбудет варьироваться от0по крайней мере255
  • unsigned intбудет варьироваться от0по крайней мере65,535
  • unsigned shortбудет варьироваться от0по крайней мере65,535
  • unsigned longбудет варьироваться от0по крайней мере4,294,967,295

Проблема с переливом

Учитывая все эти ограничения, может возникнуть вопрос: как мы можем убедиться, что наши числа не превышают лимит? И что произойдет, если мы превысим лимит?

Если у вас естьunsigned intчисло на 255, и вы увеличиваете его, вы получите взамен 256. Как и ожидалось. Если у тебя естьunsigned charчисло на 255, и вы увеличиваете его, вы получите 0 взамен. Он сбрасывается, начиная с начального возможного значения.

Если у тебя естьunsigned charчисло на 255, и вы добавляете к нему 10, вы получите число9:

#include <stdio.h>

int main(void) {
  unsigned char j = 255;
  j = j + 10;
  printf("%u", j); /* 9 */
}

Если у вас есть значение со знаком, поведение не определено. Это в основном даст вам огромное количество, которое может варьироваться, например, в этом случае:

#include <stdio.h>

int main(void) {
  char j = 127;
  j = j + 10;
  printf("%u", j); /* 4294967177 */
}

Другими словами, C не защищает вас от выхода за пределы типа. Вам нужно позаботиться об этом самостоятельно.

Предупреждения при объявлении неправильного типа

Когда вы объявляете переменную и инициализируете ее неправильным значением,gccкомпилятор (тот, который вы, вероятно, используете) должен вас предупредить:

#include <stdio.h>

int main(void) {
  char j = 1000;
}
hello.c:4:11: warning: implicit conversion from 'int' to
      'char' changes value from 1000 to -24
      [-Wconstant-conversion]
        char j = 1000;
             ~   ^~~~
1 warning generated.

And it also warns you in direct assignments:

#include <stdio.h>

int main(void) {
  char j;
  j = 1000;
}

But not if you increase the number using for example +=:

#include <stdio.h>

int main(void) {
  char j = 0;
  j += 1000;
}

Floating point numbers

Floating point types can represent a much larger set of values than integers can, and can also represent fractions, something that integers can’t do.

Using floating point numbers, we represent numbers as decimal numbers times powers of 10.

You might see floating point numbers written as

  • 1.29e-3
  • -2.3e+5

and in other seemingly weird ways.

The following types:

  • float
  • double
  • long double

are used to represent numbers with decimal points (floating point types). All can represent both positive and negative numbers.

The minimum requirements for any C implementation is that float can represent a range between 10^-37 and 10^+37, and is typically implemented using 32 bits. double can represent a bigger set of numbers. long double can hold even more numbers.

The exact figures, as with integer values, depend on the implementation.

On a modern Mac, a float is represented in 32 bits, and has a precision of 24 significant bits, 8 bits are used to encode the exponent. A double number is represented in 64 bits, with a precision of 53 significant bits, 11 bits are used to encode the exponent. The type long double is represented in 80 bits, has a precision of 64 significant bits, 15 bits are used to encode the exponent.

On your specific computer, how can you determine the specific size of the types? You can write a program to do that:

#include <stdio.h>

int main(void) {
  printf("char size: %lu bytes\n", sizeof(char));
  printf("int size: %lu bytes\n", sizeof(int));
  printf("short size: %lu bytes\n", sizeof(short));
  printf("long size: %lu bytes\n", sizeof(long));
  printf("float size: %lu bytes\n", sizeof(float));
  printf("double size: %lu bytes\n", sizeof(double));
  printf("long double size: %lu bytes\n", sizeof(long double));
}

In my system, a modern Mac, it prints:

char size: 1 bytes
int size: 4 bytes
short size: 2 bytes
long size: 8 bytes
float size: 4 bytes
double size: 8 bytes
long double size: 16 bytes  

Download my free C Handbook


More clang tutorials: