(2) Хранить текущую длину в элементе str[0],
а буквы - в str[1] ... итд.
Плохо тем, что в str[0] можно хранить лишь числа от 0 до 255,
и если строка длиннее - то такой подход неприменим.
(3) Не хранить длину НИГДЕ, а ввести символ-признак конца строки.
Теперь в
func(str); /* ОДИН аргумент - сам массив */
передается только сам массив, а его текущая длина может быть
при нужде вычислена при помощи некоей функции, вроде такой:
int strlen(char s[]){ /* функция от массива букв */
int counter = 0; /* счетчик и одновременно индекс */
while(s[counter] != '\0') /* пока не встретился признак конца текста */
counter++; /* посчитать символ */
return counter; /* сколько символов, отличных от '\0' */
}
Тут никаких ограничений нет. Именно этот подход и был избран
в языке Си, хотя в принципе можно самому пользоваться и другими.
На самом деле в языке есть такая СТАНДАРТНАЯ функция strlen(s)
(вам не надо писать ее самому, ее уже написали за вас).
---------------------------------------------------------------------------
ИНИЦИАЛИЗАЦИЯ ГЛОБАЛЬНОГО МАССИВА
=================================
Массив, заданный вне каких-либо функций, можно проинициализировать
константными начальными значениями:
int array[5] = { 12, 23, 34, 45, 56 };
char string[7] = { 'П', 'р', 'и', 'в', 'е', 'т', '\0' };
Если размер массива указан БОЛЬШЕ, чем мы перечислим элементов,
то остальные элементы заполнятся нулями (для int) или '\0' для char.
int array[5] = { 12, 23, 34 };
Если мы перечислим больше элементов, чем позволяет размер массива -
это будет ошибкой.
int a[5] = { 177, 255, 133 };
Операция индексации массива a[] дает:
при n значение выражения a[n] есть
---------------------------------------------------------------------------
-1 не определено (ошибка: "индекс за границей массива")
0 177
1 255
2 133
3 0
4 0
5 не определено (ошибка)
* 13_FUNCS.txt *
КАК ПРОИСХОДИТ ВЫЗОВ ФУНКЦИИ
============================
Пусть у нас описана функция, возвращающая целое значение.
/* ОПРЕДЕЛЕНИЕ ФУНКЦИИ func(). */
/* Где func - ее имя. Назвать мы ее можем как нам угодно. */
int func(int a, int b, int c){
int x, y;
...
x = a + 7;
...
b = b + 4;
...
return(некое_значение);
}
Здесь
a, b, c - аргументы функции (параметры)
x, y - локальные переменные
Точка вызова - находится внутри какой-то другой
функции, например функции main()
main(){
int zz, var;
...
var = 17;
zz = func(33, 77, var + 3) + 44;
...
}
Когда выполнение программы доходит до строки
zz = func(33, 77, var + 3) + 44;
1) Происходит ВЫЗОВ ФУНКЦИИ func()
(a) Этот пункт мы увидим ниже.
(b) Создаются переменные с именами a, b, c, x, y;
(c) Переменным-аргументам присваиваются начальные значения,
которые берутся из точки вызова.
В точке вызова перечислен список (через запятую) выражений (формул):
func(выражение1, выражение2, выражение3)
Вычисленные значения этих выражений соответственно будут присвоены
1-ому, 2-ому и 3-ему аргументам (параметрам) из определения функции:
int func(a, b, c){ /* a = номер 1, b = 2, c = 3 */
Первый параметр:
a = 33;
Второй параметр:
b = 77;
Третий параметр:
c = var + 3;
то есть, вычисляя,
c = 20;
Локальные переменные x и y содержат неопределенные значения,
то есть мусор (мы не можем предсказать их значения,
пока не присвоим им явным образом какое-либо значение сами).
2) Выполняется ТЕЛО функции, то есть вычисления, записанные внутри { ... }
в определении функции. Например:
x = a + 7;
И параметры, и локальные переменные - это ПЕРЕМЕННЫЕ,
то есть их можно изменять.
b = b + 4;
При этом никакие переменные ВНЕ этой функции не изменяются.
(Об этом еще раз позже).
3) Производится ВОЗВРАТ из функции.
...
return(некое_значение);
}
Например, это может быть
...
return(a + 2 * x);
}
Рассмотрим, что при этом происходит в точке вызова:
zz = func(33, 77, var + 3) + 44;
(1) Вычеркиваем func(.....)
zz = XXXXXXX + 44;
(2) Вычисляем значение "некое_значение" в операторе return,
и берем КОПИЮ этого значения.
Пусть при вычислении там получилось 128.
(3) Подставляем это значение на место вычеркнутого func(.....)
У нас получается
zz = 128 + 44;
(4) АВТОМАТИЧЕСКИ УНИЧТОЖАЮТСЯ локальные переменные и аргументы функции:
a - убито
b - убито
c - убито
x - убито
y - убито
Таких переменных (и их значений) больше нет в природе.
(5) Пункт, который мы обсудим позже.
(6) Продолжаем вычисление:
zz = 128 + 44;
Вычисляется в
zz = 172; /* оператор присваивания */
---------------------------------------------------------------------------
int func1(int x){
printf("func1: x=%d\n", x); /* 1 */
x = 77;
printf("func1: x=%d\n", x); /* 2 */
return x;
}
void main(){
int var, y;
var = 111;
y = func1(var); /* @ */
printf("main: var=%d\n", var); /* 3 */
}
В данном случае в точке @ мы передаем в функцию func1()
ЗНАЧЕНИЕ переменной var, равное 111.
Это значит, что при вызове функции будет создана переменная x
и ей будет присвоено начальное значение 111
x = 111;
Поэтому первый оператор printf() напечатает 111.
Затем мы изменяем значение переменной x на 77.
Мы меняем переменную x, но не переменную var !!!
Использовав ЗНАЧЕНИЕ (его копию) из переменной var для x,
мы о переменной var забыли - она нас не касается (а мы - ее).
Поэтому второй оператор printf() напечатает 77.
В переменной же var осталось значение 111,
что и подтвердит нам третий оператор printf,
который напечатает 111.
---------------------------------------------------------------------------
ВРЕМЕННОЕ СОКРЫТИЕ ПЕРЕМЕННЫХ
=============================
int func1(int x){ /* f.1 */
printf("func1: x=%d\n", x); /* f.2 */
x = 77; /* f.3 */
printf("func1: x=%d\n", x); /* f.4 */
return x; /* f.5 */
}
void main(){
int x, y; /* 1 */
x = 111; /* 2 */
y = func1(x); /* 3 */
printf("main: x=%d y=%d\n", x, y); /* 4 */
}
А теперь мы и переменную внутри main(), и аргумент функции
func1() назвали одним и тем же именем. Что будет?
Будет то же самое, что в предыдущем примере.
В момент вызова функции func1() будет создана НОВАЯ переменная
с именем x, а старая (прежняя) переменная и ее значение будут
ВРЕМЕННО СПРЯТАНЫ (скрыты).
Можно было бы уточнить эти переменные именами функций,
в которых они определены:
main::x
и
func1::x
(но это уже конструкции из языка Си++, а не Си).
Выполним программу по операторам:
|/* 1 */ Отводятся переменные main::x и main::y для целых чисел;
|/* 2 */ main::x = 111;
|/* 3 */ Вызывается func1(111);
|
+-------+
. |/* f.1 */ Отводится переменная func1::x со значением 111;
. |/* f.2 */ Печатается 111 из переменной func1::x;
. |
. |/* f.3 */ func1::x = 77; (это не main::x, а другая переменная,
. | ЛОКАЛЬНАЯ для функции func1.
. | Переменную main::x мы сейчас не видим -
. | она "заслонена" именем нашей локальной
. | переменной.
. | Поэтому мы не можем ее изменить).
. |
. |/* f.4 */ Печатает 77 из func1::x;
. |/* f.5 */ Возвращает значение func1::x , то есть 77.
. | Переменная func1::x уничтожается.
. |
. | Теперь мы снова возвращаемся в функцию main(),
. | где имя x обозначает переменную main::x
+-------+
|
|/* 3 */ y = 77;
|/* 4 */ Печатает значения main::x и main::y, то есть
| 111 и 77.
Этот механизм сокрытия имен позволяет писать функции main() и func1()
разным программистам, позволяя им НЕ ЗАБОТИТЬСЯ о том, чтобы имена
локальных переменных в функциях НЕ СОВПАДАЛИ. Пусть совпадают - хуже не
будет, механизм упрятывания имен разрешит конфликт.
Зато программист может использовать любое понравившееся ему имя
в любой функции - хотя бы и x, или i.
---------------------------------------------------------------------------
То же самое происходит с локальными переменными,
а не с аргументами функции.
int func1(int arg){ /* локальная переменная-параметр func1::arg */
int x; /* локальная переменная func1::x */
x = arg;
printf("func1: x=%d\n", x);
x = 77;
printf("func1: x=%d\n", x);
return x;
}
void main(){
int x, y; /* переменные main::x и main::y */
x = 111;
y = func1(x);
printf("main: x=%d y=%d\n", x, y);
}
Действует тот же самый механизм временного сокрытия имени x.
Вообще же, аргументы функции и ее локальные переменные
отличаются только одним:
аргументам автоматически присваиваются
начальные значения, равные значениям соответствующих выражений
в списке
имя_функции(..., ..., ....)
арг1 арг2 арг3
в месте вызова функции.
То есть
ОПИСАНИЕ ФУНКЦИИ:
int f(int арг1, int арг2, int арг3){
int перем1, перем2;
...
/* продолжение */
}
ВЫЗОВ:
.... f(выражение1, выражение2, выражение3) ...
ТО В ТЕЛЕ ФУНКЦИИ ВЫПОЛНИТСЯ (в момент ее вызова):
арг1 = выражение1;
арг2 = выражение2;
арг3 = выражение3;
перем1 = МУСОР;
перем2 = МУСОР;
...
/* продолжение */
---------------------------------------------------------------------------
ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ
=====================
Наконец, существуют переменные, которые объявляются ВНЕ ВСЕХ ФУНКЦИЙ,
и существующие все время выполнения программы
(а не только то время, когда активна функция, в которой они созданы).
Локальные переменные и аргументы УНИЧТОЖАЮТСЯ при выходе
из функции. Глобальные переменные - нет.
int x = 12; /* ::x - ей можно заранее присвоить константу */
int globvar; /* ::globvar */
int f1(){
int x; /* f1::x */
x = 77;
printf("x=%d\n", x); /* 4 */
return x;