Изучаю си (в которых раз :). Вот обучающий код:
#include <stdio.h>
int main(int argc, char *argv[])
{
// create two arrays we care about
int ages[] = {23, 43, 12, 89, 2};
char * names[] = {
"Alan", "Frank",
"Mary", "John", "Lisa"
};
// safely get the size of ages
int count = sizeof(ages) / sizeof(int);
int i = 0;
// first way using indexing
for(i = 0; i < count; i++) {
printf("%s has %d years alive.\n",
names[i], ages[i]);
}
printf("---\n");
// setup the pointers to the start of the arrays
int *cur_age = ages;
char **cur_name = names;
// second way using pointers
for(i = 0; i < count; i++) {
printf("%s is %d years old.\n",
*(cur_name+i), *(cur_age+i));
}
printf("---\n");
// third way, pointers are just arrays
for(i = 0; i < count; i++) {
printf("%s is %d years old again.\n",
cur_name[i], cur_age[i]);
}
printf("---\n");
// fourth way with pointers in a stupid complex way
for(cur_name = names, cur_age = ages;
(cur_age - ages) < count;
cur_name++, cur_age++)
{
printf("%s lived %d years so far.\n",
*cur_name, *cur_age);
}
return 0;
}
http://c.learncodethehardway.org/book/ex15.html
Итак, есть еще пятый способ перечисления данных, который наиболее мне понятен:
// five way using indexing
for(i = 0; i < count; i++) {
printf("%s has %d years alive.\n",
*(names + i), *(ages + i));
}
Таким образом names[i]
это сокращение на *(names + i)
. Объясняется очень просто: массив в си это список, разделенный на блоки указанного размера, при этом обращение напрямую по имени переменной означает на начальный адресс в памяти. Суммирование этого адреса с числом означает, автоматическое умножение числа на размер типа данных массива. Для int ages[]
, ages + i означает адрес ages + i * sizeof(int). Именно поэтому cur_name[i], cur_age[i]
по смыслу это тоже самое что и names[i], ages[i]
, но ведь в одном случае нам вталкивают что это «массивы», а во втором, что это ссылки на эти массивы. Но на деле оба являются ссылками, или адресами на участки памяти.
Поэтому записи указателей int *cur_age; char **cur_name;
на самом деле ничего общего не имеют с инициализацией (определением) собственно массивов, особенно таких char * names[]
(список указателей на статичные литералы), а *(names + i)
на самом деле просто разыменовывает указатель на эту статичную строку и возращает ее значение. В то время как char letters[]
- совсем иная структура данных (список из одного char). Разница между ними в том, что сдвиг адреса будет иметь разное количественно значение.
// print addresses of names
printf("names, sizeof(char *): %lu\n", sizeof(char*));
for (i = 0; i < count; ++i) {
printf("names's element %d has address %lx and value '%s'\n",
i, (size_t)(names + i), *(names + i));
}
// same as above, but for letters
char letters[] = {
'A', 'B', 'C'
};
printf("letters, sizeof(char): %lu\n", sizeof(char));
for (i = 0; i < sizeof(letters); ++i) {
printf("names's element %ud has address %lx and value '%c'\n",
i, (size_t)(letters + i), *(letters + i));
}
Как правильно выводить адреса (сейчас варнинг: cast from pointer to integer of different size) пока не знаю, но скоро узнаю :)
Пруф:
names, sizeof(char *): 8
names's element 0 has address ff0003c0 and value 'Alan'
names's element 1 has address ff0003c8 and value 'Frank'
names's element 2 has address ff0003d0 and value 'Mary'
names's element 3 has address ff0003d8 and value 'John'
names's element 4 has address ff0003e0 and value 'Lisa'
letters, sizeof(char): 1
names's element 0d has address ff0003b0 and value 'A'
names's element 1d has address ff0003b1 and value 'B'
names's element 2d has address ff0003b2 and value 'C'
Все верно? Почему везде об этом не говорится по-человечески? В мануале по ссылке выше наиболее лучшее описание, но всеравно, описано в стиле «ходим, вокруг да около». Хорошо, что у меня был в свое время бэкграунд с изучением асма и я до этого допер сам. Неужели сложно ввести сначала понятие об адресации памяти, ведь это несложно. А потом, опираясь только на этом объяснять про указатели?