Как присвоить значение переменной в c
Перейти к содержимому

Как присвоить значение переменной в c

  • автор:

Переменные в Си. Операции

Переменные в си служат для хранения, изменения информации. У каждой переменной в си есть имя и значения. Значения переменной в ходе программы си можно изменять. Чтобы работать с переменными в си, их нужно сначала объявить. Это значит указать их тип и имя.
Примеры объявления переменных в си
int a; // объявлена целочисленная переменная с именем a
float b, c; //объявлены вещественные переменные b и с
char e; // объявлена символьная переменная с именем e
Чтобы работать со строкой символов в си используется объявление массива символов.
char имя строки[длина строки];

Пример объявления строковой переменной в си
char name[50]; //объявлена строка с именем name длиной 50
Чтобы присвоить или изменить значение переменной используется =
Примеры присваивания значений переменных в си
int a,b,c; // объявлены целочисленные переменные a, b, c
a=10; // переменной a присваивается значение 10
b=20; // переменной b присваивается значение 20
c=a+b; // в переменную с присваивается сумма a и b
char c; // объявлена символьная переменная с
c=’V’; // символьной переменной c присваивается символ V
char name[50]; // объявлена строка name
name=”Вася”; // строке присваивается текст Вася

Знаки арифметических действий с числовыми переменными в си
+ — сложение, вычитание
* / умножение, деление
% остаток от деления
Стандартные функции для числовых переменных
abs(i) модуль целого числа i
fabs(x) модуль вещественного числа x
sqrt(x) квадратный корень из вещественного числа x
pow(x,y) вычисляет x в степени y

Вернуться к содержанию Перейти к следующему уроку Вывод информации на экран в Си.

Переменные

Теги: Си переменные. char, int, unsigned, long, long long, float, double, long double, long float, lexical scoping. Объявление переменных. Область видимости. Инициализация переменных. Имена переменных. Экспоненциальная форма.

Переменные

П еременные используются для хранения значений (sic!). Переменная характеризуется типом и именем. Начнём с имени. В си переменная может начинаться с подчерка или буквы, но не с числа. Переменная может включать в себя символы английского алфавита, цифры и знак подчёркивания. Переменная не должна совпадать с ключевыми словами (это специальные слова, которые используются в качестве управляющих конструкций, для определения типов и т.п.)

А также ряд других слов, специфичных для данной версии компилятора, например far, near, tiny, huge, asm, asm_ и пр.

Например, правильные идентификаторы
a, _, _1_, Sarkasm, a_long_variable, aLongVariable, var19, defaultX, char_type
неверные
1a, $value, a-long-value, short

Си — регистрозависимый язык. Переменные с именами a и A, или end и END, или perfectDark и PerfectDarK – это различные переменные.

Типы переменных

  • 1) Размер переменной в байтах (сколько байт памяти выделит компьютер для хранения значения)
  • 2) Представление переменной в памяти (как в двоичном виде будут расположены биты в выделенной области памяти).

Целые

  • char — размер 1 байт. Всегда! Это нужно запомнить.
  • short — размер 2 байта
  • int — размер 4 байта
  • long — размер 4 байта
  • long long — размер 8 байт.

Указанные выше значения характерны для компилятора VC2012 на 32-разрядной машине. Так что, если ваша программа зависит от размера переменной, не поленитесь узнать её размер.

Теперь давайте определим максимальное и минимальное число, которое может хранить переменная каждого из типов. Числа могут быть как положительными, так и отрицательными. Отрицательные числа используют один бит для хранения знака. Иногда знак необходим (например, храним счёт в банке, температуру, координату и т.д.), а иногда в нём нет необходимости (вес, размер массива, возраст человека и т.д.). Для этого в си используется модификатор типа signed и unsigned. unsigned char — все 8 бит под число, итого имеем набор чисел от 00000000 до 11111111 в двоичном виде, то есть от 0 до 255 signed char от -128 до 128. В си переменные по умолчанию со знаком. Поэтому запись char и signed char эквивалентны.

Таб. 1 Размер целых типов в си.

Тип Размер, байт Минимальное значение Максимальное значение
unsigned char 1 0 255
signed char
( char )
1 -128 127
unsigned short 2 0 65535
signed short
( short )
2 -32768 32767
unsigned int
( unsigned )
4 0 4294967296
signed int
( int )
4 -2147483648 2147483647
unsigned long 4 0 4294967296
signed long
( long )
4 -2147483648 2147483647
unsigned long long 8 0 18446744073709551615
signed long long
( long long )
8 -9223372036854775808 9223372036854775807

sizeof

В си есть оператор, который позволяет получить размер переменной в байтах. sizeof переменная, или sizeof(переменная) или sizeof(тип). Это именно оператор, потому что функция не имеет возможности получить информацию о размере типов во время выполнения приложения. Напишем небольшую программу чтобы удостовериться в размерах переменных.

#include #include int main() < char c; short s; int i; long l; long long L; //Вызов sizeof как "функции" printf("sizeof(char) = %d\n", sizeof(c)); printf("sizeof(short) = %d\n", sizeof(s)); printf("sizeof(int) = %d\n", sizeof(i)); printf("sizeof(long) = %d\n", sizeof(l)); printf("sizeof(long long) = %d\n", sizeof(L)); //Вызов как оператора printf("sizeof(char) = %d\n", sizeof c); printf("sizeof(short) = %d\n", sizeof s); printf("sizeof(int) = %d\n", sizeof i); printf("sizeof(long) = %d\n", sizeof l); printf("sizeof(long long) = %d\n", sizeof L); _getch(); >

(Я думаю ясно, что переменные могут иметь любое валидное имя). Эту программу можно было написать и проще

#include #include int main() < printf("sizeof(char) = %d\n", sizeof(char)); printf("sizeof(short) = %d\n", sizeof(short)); printf("sizeof(int) = %d\n", sizeof(int)); printf("sizeof(long) = %d\n", sizeof(long)); printf("sizeof(long long) = %d\n", sizeof(long long)); //нельзя произвести вызов sizeof как оператора для имени типа //sizeof int - ошибка компиляции _getch(); >

В си один и тот же тип может иметь несколько названий
short === short int
long === long int
long long === long long int
unsigned int === unsigned

Типы с плавающей точкой

  • float — 4 байта,
  • long float — 8 байт
  • double — 8 байт
  • long double — 8 байт.
Таб. 2 Размер типов с плавающей точкой в си.

Тип Размер, байт Количество значащих знаков мантиссы Минимальное значение Максимальное значение
float 4 6-7 1.175494351 E – 38 3.402823466 E + 38
double 8 15-16 2.2250738585072014 E – 308 1.7976931348623158 E + 308

Переполнение переменных

Си не следит за переполнением переменных. Это значит, что постоянно увеличивая значение, скажем, переменной типа int в конце концов мы «сбросим значение»

#include #include void main() < unsigned a = 4294967295; int b = 2147483647; //Переполнение беззнакового типа printf("%u\n", a); a += 1; printf("%u", a); //Переполнение знакового типа printf("%d\n", b); b += 1; printf("%d", b); getch(); >

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

Постфиксное обозначение типа

  • 11 — число типа int
  • 10u — unsigned
  • 22l или 22L — long
  • 3890ll или 3890LL — long long (а также lL или Ll)
  • 80.0f или 80.f или 80.0F — float (обязательно наличие десятичной точки в записи)
  • 3.0 — число типа double

#include #include int main()

Следующий код, однако, не будет приводить к ошибкам, потому что происходит неявное преобразование типа

int a = 10u; double g = 3.f;

Шестнадцатеричный и восьмеричный формат

В о время работы с числами можно использовать шестнадцатеричный и восьмеричный формат представления. Числа в шестнадцатиричной системе счисления начинаются с 0x, в восьмеричной системе с нуля. Соответственно, если число начинается с нуля, то в нём не должно быть цифр выше 7:

#include #include void main()

Экспоненциальная форма представления чисел

Э кспоненциальной формой представления числа называют представление числа в виде M e ± p , где M — мантиса числа, p — степень десяти. При этом у мантисы должен быть один ненулевой знак перед десятичной запятой.
Например 1.25 === 1.25e0, 123.5 === 1.235e2, 0.0002341 === 2.341e-4 и т.д.
Представления 3.2435e7 эквивалентно 3.2435e+7
Существеут и другое представление («инженерное»), в котором степень должна быть кратной тройке. Например 1.25 === 1.25e0, 123.5 === 123.5e0, 0.0002341 === 234.1e-6, 0.25873256 === 258.73256e-3 и т.д.

Объявление переменных

В си переменные объявляются всегда в начале блока (блок — участок кода ,ограниченный фигурными скобками)

При объявлении переменной пишется её тип и имя.

int a; double parameter;

Можно объявить несколько переменных одного типа, разделив имена запятой

long long arg1, arg2, arg3;
#include #include int main() < int a = 10; int b; while (a>0) < int z = a*a; b += z; >>

Здесь объявлены переменные a и b внутри функции main, и переменная z внутри тела цикла. Следующий код вызовет ошибку компиляции

int main()

Это связано с тем, что объявление переменной стоит после оператора присваивания. При объявлении переменных можно их сразу инициализировать.
int i = 0;
При этом инициализация при объявлении переменной не считается за отдельный оператор, поэтому следующий код будет работать

int main()

Начальное значение переменной

О чень важно запомнить, что переменные в си не инициализируются по умолчанию нулями, как во многих других языках программирования. После объявления переменной в ней хранится «мусор» — случайное значение, которое осталось в той области памяти, которая была выделена под переменную. Это связано, в первую очередь, с оптимизацией работы программы: если нет необходимости в инициализации, то незачем тратить ресурсы для записи нулей (замечание: глобальные переменные инициализируются нулями, почему так, читайте в этой статье).

#include #include int main()

Если выполнять эту программу на VC, то во время выполнения вылетит предупреждение
Run-Time Check Failure #3 — The variable ‘i’ is being used without being initialized.
Если нажать «Продолжить», то программа выведет «мусор». В многих других компиляторах при выполнении программы не будет предупреждения.

Область видимости переменной

П еременные бывают локальными (объявленными внутри какой-нибудь функции) и глобальными. Глобальная переменная видна всем функциям, объявленным в данном файле. Локальная переменная ограничена своей областью видимости. Когда я говорю, что переменная «видна в каком-то месте», это означает, что в этом месте она определена и её можно использовать. Например, рассмотрим программу, в которой есть глобальная переменная

#include #include int global = 100; void foo() < printf("foo: %d\n", global); >void bar(int global) < printf("bar: %d\n", global); >int main()

Будет выведено
foo: 100
bar: 333
Здесь глобальная переменная global видна всем функциям. Но аргумент функции затирает глобальную переменную, поэтому при передаче аргумента 333 выводится локальное значение 333.
Вот другой пример

#include #include int global = 100; int main()

Программа выведет 555. Также, как и в прошлом случае, локальная переменная «важнее». Переменная, объявленная в некоторой области видимости не видна вне её, например

#include #include int global = 100; int main() < int x = 10; < int y = 30; printf("%d", x); >printf("%d", y); >

Этот пример не скомпилируется, потому что переменная y существует только внутри своего блока.
Вот ещё пример, когда переменные, объявленные внутри блока перекрывают друг друга

#include #include int global = 100; int main() < int x = 10; < int x = 20; < int x = 30; printf("%d\n", x); >printf("%d\n", x); > printf("%d\n", x); getch(); >

Программа выведет
30
20
10
Глобальных переменных необходимо избегать. Очень часто можно услышать такое. Давайте попытаемся разобраться, почему. В ваших простых проектах глобальные переменные выглядят вполне нормально. Но представьте, что у вас приложение, которое

  • 1) Разрабатывается несколькими людьми и состоит из сотен тысяч строк кода
  • 2) Работает в несколько потоков

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

Безусловно, есть ситуации, когда глобальные переменные упрощают программу, но такие ситуации случаются не часто и не в ваших домашних заданиях, так что НЕ СОЗДАВАЙТЕ ГЛОБАЛЬНЫХ ПЕРЕМЕННЫХ!
Переменные могут быть не только целочисленными и с плавающей точкой. Существует множество других типов, которые мы будем изучать в дальнейшем.

ru-Cyrl 18- tutorial Sypachev S.S. 1989-04-14 sypachev_s_s@mail.ru Stepan Sypachev students

email

Всё ещё не понятно? – пиши вопросы на ящик

Как присвоить значение переменной в c

Операции присваивания позволяют присвоить некоторое значения. Эти операции выполняются над двумя операндами, причем левый операнд может представлять только модифицируемое именованное выражение, например, переменную.

Базовая операция присваивания = позволяет присвоить значение правого операнда левому операнду:

int x <>; x = 2;

То есть в данном случае переменная x (левый операнд) будет иметь значение 2 (правый операнд).

Стоит отметить, что тип значения правого операнда не всегда может совпадать с типом левого операнда. В этом случае компилятор пытается преобразовать значение правого операнда к типу левого операнда.

При этом операции присваивания имеют правосторонний порядок, то есть выполняются справа налево. И, таким образом, можно выполнять множественное присваивание:

int a <>, b<>, c<>; a = b = c = 34;

Здесь сначала вычисляется значение выражения c = 34 . Значение правого операнда — 34 присваивается левому операнду с. Далее вычисляется выражение b = c : значение правого операнда c (34) присваивается левому операнду b . И в конце вычисляется выражение a = b : значение правого операнда b (34) присваивается левому операнду a .

Кроме того, следует отметить, что операции присваивания имеют наименьший приоритет по сравнению с другими типами операций, поэтому выполняются в последнюю очередь:

int x <>; x = 3 + 5;

В соответствии с приоритетом операций вначале выполняется выражение 3 + 5 , и только потом его значение присваивается переменной x.

Все остальные операции присваивания являются сочетанием простой операции присваивания с другими операциями и имеют общую форму op= :

  • += : присваивание после сложения. Присваивает левому операнду сумму левого и правого операндов: A += B эквивалентно A = A + B
  • -= : присваивание после вычитания. Присваивает левому операнду разность левого и правого операндов: A -= B эквивалентно A = A — B
  • *= : присваивание после умножения. Присваивает левому операнду произведение левого и правого операндов: A *= B эквивалентно A = A * B
  • /= : присваивание после деления. Присваивает левому операнду частное левого и правого операндов: A /= B эквивалентно A = A / B
  • %= : присваивание после деления по модулю. Присваивает левому операнду остаток от целочисленного деления левого операнда на правый: A %= B эквивалентно A = A % B
  • >>= : присваивание после сдвига разрядов вправо. Присваивает левому операнду результат сдвига его битового представления вправо на определенное количество разрядов, равное значению правого операнда: A >>= B эквивалентно A = A >> B
  • &= : присваивание после поразрядной конъюнкции. Присваивает левому операнду результат поразрядной конъюнкции его битового представления с битовым представлением правого операнда: A &= B эквивалентно A = A & B
  • |= : присваивание после поразрядной дизъюнкции. Присваивает левому операнду результат поразрядной дизъюнкции его битового представления с битовым представлением правого операнда: A |= B эквивалентно A = A | B
  • ^= : присваивание после операции исключающего ИЛИ. Присваивает левому операнду результат операции исключающего ИЛИ его битового представления с битовым представлением правого операнда: A ^= B эквивалентно A = A ^ B

#include int main() < int a ; a += 10; // 15 a -= 3; // 12 a *= 2; // 24 a /= 6; // 4 a >= 2; // 16 std::cout

Переменные, адреса и указатели в языке программирования C

Переменная — это область памяти, к которой мы обращаемся за находящимися там данными, используя идентификатор (в данном случае, имя переменной). При этом у этой помеченной именем области есть еще и адрес, выраженный числом и понятный компьютерной системе. Этот адрес можно получить и записать в особую переменную. Переменную, содержащую адрес области памяти, называют указателем.

Когда мы меняем значение обычной переменной, то, можно сказать, просто удаляем из конкретной области памяти данные и записываем туда новые. Когда мы меняем значение переменной-указателя, то начинаем работать с совершенно иным участком памяти, т.к. меняем содержащийся в ней адрес.

Тема указателей тесно связана с темой динамических типов данных. Когда программа компилируется, то под объявленные переменные так или иначе (в зависимости от того, где они были объявлены) выделяются участки памяти. Потом размер этих участков не меняется, может меняться только их содержимое (значения или данные). Однако именно с помощью указателей можно захватывать и освобождать новые участки памяти уже в процессе выполнения программы. Динамические типы данных будут рассмотрены позже.

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

int i = 0; printf ("i=%d, &i=%p \n", i, &i);

В результате выполнения данного программного кода на экране появляется примерно следующее (шестнадцатеричное число у вас будет другим):

i=0, &i=0x7fffa40c5fac

Знак амперсанда & перед переменной позволяет получить ее адрес в памяти. Для вывода адреса переменной на экран используется специальный формат %p . Адреса обычных переменных (не указателей) в процессе выполнения программы никогда не меняются. В этом можно убедиться:

int a = 6; float b = 10.11; char c = 'k'; printf("%5d - %p\n", a, &a); printf("%5.2f - %p\n", b, &b); printf("%5c - %p\n", c, &c); a = 2; b = 50.99; c = 'z'; printf("%5d - %p\n", a, &a); printf("%5.2f - %p\n", b, &b); printf("%5c - %p\n", c, &c);
6 - 0x7fff653532e0 10.11 - 0x7fff653532e4 k - 0x7fff653532df 2 - 0x7fff653532e0 50.99 - 0x7fff653532e4 z - 0x7fff653532df

Как мы видим, несмотря на то, что значения переменных поменялись, ячейки памяти остались прежними.

Зная адрес, можно получить значение, которое находится по этому адресу, поставив знак * перед адресом:

int a = 8; printf("%d \n", *&a);

На экране будет выведено число 8.

Однако запись типа &a не всегда возможна или удобна. Поэтому существует специальный тип данных — указатели, которым и присваивается адрес на область памяти.

Указатели в языке C , как и другие переменные, являются типизированными, т.е. при объявлении указателя надо указывать его тип. Как мы узнаем позже, с указателями можно выполнять некоторые арифметические операции, и именно точное определение их типа позволяет протекать им корректно. Чтобы объявить указатель, надо перед его именем поставить знак * . Например:

int *pi; float *pf;

Обратите внимание на то, что в данном случае * говорит о том, что объявляется переменная-указатель. Когда * используется перед указателем не при его объявлении, а в выражениях, то обозначает совсем иное — «получить значение (данные) по адресу, который присвоен указателю». Посмотрите на код ниже:

int x = 1, y, z = 3; int *p, *q; p = &x; printf("%d\n", *p); // 1 y = *p; printf("%d\n", y); // 1 *p = 0; printf("%d %d\n", x, y); // 0 1 q = &z; printf("%d\n", *q); // 3 p = q; *p = 4; printf("%d\n", z); // 4 printf("%p %p\n", p, q); // p == q

С помощью комментариев указаны текущие значения ячеек памяти. Подробно опишем, что происходит:

  1. Выделяется память под пять переменных: три типа int и два указателя на int . В ячейки x и z записываются числа 1 и 3 соответственно.
  2. Указателю p присваивается адрес ячейки x . Извлекая оттуда значение ( *p ), получаем 1.
  3. В область памяти, которая названа именем у , помещают значение равное содержимому ячейки, на которую ссылается указатель p . В результате имеем две области памяти ( x и y ), в которые записаны единицы.
  4. В качестве значения по адресу p записываем 0. Поскольку p указывает на x , то значение xменяется. Переменная p не указывает на y , поэтому там остается прежнее значение.
  5. Указателю q присваивается адрес переменной z . Извлекая оттуда значение ( *q ), получаем 3.
  6. Переменной p присваивается значение, хранимое в q . Это значит, что p начинает ссылаться на тот же участок памяти, что и q . Поскольку q ссылается на z , то и p начинает ссылаться туда же.
  7. В качестве значения по адресу p записываем 4. Т.к. p является указателем на z , следовательно, меняется значение z .
  8. Проверяем, p и q являются указателями на одну и туже ячейку памяти.

Под сам указатель (там, где хранится адрес) также должна быть выделена память. Объем этой памяти можно узнать с помощью функции sizeof() :

int *pi; float *pf; printf("%lu\n", sizeof(pi)); printf("%lu\n", sizeof(pf));

Под указатели всех типов выделяется одинаковый объем памяти, т.к. размер адреса не зависит от типа, а зависит от вычислительной системы. В таком случае, зачем при объявлении указателя следует указывать тип данных, на который он может ссылаться? Дело в том, что по типу данных определяется, сколько ячеек памяти занимает значение, на которое ссылается указатель, и через сколько ячеек начнется следующее значение.

Если указатель объявлен, но не определен, то он ссылается на произвольный участок памяти с неизвестно каким значением:

int *pa, *pb; float *pc; printf(" %p %p %p\n", pa, pc, pb); // может возникнуть ошибка printf(" %d %f\n", *pa, *pc); 

Результат (в Ubuntu):

0x64 0x1000 0x55ac036d1060 Ошибка сегментирования (образ памяти сброшен на диск)

Использование неопределенных указателей в программе при вычислениях чревато возникновением серьезных ошибок. Чтобы избежать этого, указателю можно присвоить значение, говорящее, что указатель никуда не ссылается ( NULL ). Использовать такой указатель в выражениях не получится, пока ему не будет присвоено конкретное значение:

int a = 5; float c = 6.98; int *pa; float *pc; pa = NULL; pc = NULL; printf(" %15p %15p\n", pa, pc); // Error // printf(" %15d %15f\n", *pa, *pc); pa = &a; pc = &c; printf(" %15p %15p\n", pa, pc); printf(" %15d %15f\n", *pa, *pc);

Результат (в Ubuntu):

(nil) (nil) 0x7ffd8e77e550 0x7ffd8e77e554 5 6.980000

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

На этом уроке вы должны понять, что такое адрес переменной и как его получить ( &var ), что такое переменная-указатель ( type *p_var; p_var = &var ) и как получить значение, хранимое в памяти, зная адрес ячейки ( *p_var ). Однако у вас может остаться неприятный осадок из-за непонимания, зачем все это надо? Это нормально. Понимание практической значимости указателей придет позже по мере знакомства с новым материалом.

Курс с решением задач:
pdf-версия

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *