Код не мой, поэтому удивило такое, можно поправить конечно.
Теперь исключительно любопытно, почему же тогда sizeof при последующих вызовах дает 100, может и правда 100 выделяется, может авторы сырца не зря так написали?
Здравствуйте, Bell, Вы писали:
O>>Каков истинный размер такого массива, 100 или 11 ? B>11
Размер будет 100
B>Иcпользовать strlen.
strlen возвращает длинну строки (т.е. количество символов идущих подряд и ограниченных '\0'), а не размер массива в байтах.
B>Завести рядом с массивом переменную, хранящую размер:
Зачем использовать дополнительную переменную, если при объявлении мы уже резервируем под массив извезстный размер?
Для проверки приведу небольшой код:
extern char A[100];
char A[] = "Hello!";
int _tmain(int argc, _TCHAR* argv[])
{
std::cout<<sizeof A<<std::endl;
A[strlen(A)] = 'A';// здесь должно было бы вывалиться!!!
std::cout<<A<<std::endl;
return 0;
}
Получим:
100
Hello!A
Если бы по массив выделялась память в размере strlen(A), то строка, выделенная жирным, вызвала бы исключительную ситуацию!
Здравствуйте, overt, Вы писали:
O>В одном файле объявлен и инициализирован массив O>
O>char str[] = "0123456789";
O>
O>а в дргом объявлен как O>
O>extern char str[100];
O>
O>Каков истинный размер такого массива, 100 или 11 ?
Обьявление extern есть сообщение компилятору при компиляции текущего файла, что мол где-то существует такой вот обьект, такого вот типа. никакого реального аллокирования этого обьекта компилятор тут не делает. он просто генерит код с символической ссылкой на это имя, и уже линкер должен будет, взяв все файлы сборки, вместо символа str, поставить какой-то адрес. если линкер такого имени не найдет — будет ругаться.
потому реальный массив будет размером определяемым декларацией
char str[] = "0123456789";
Здравствуйте, GGoga, Вы писали:
GG>Здравствуйте, Bell, Вы писали:
O>>>Каков истинный размер такого массива, 100 или 11 ? B>>11 GG>Размер будет 100
Неверно.
B>>Иcпользовать strlen. GG>strlen возвращает длинну строки (т.е. количество символов идущих подряд и ограниченных '\0'), а не размер массива в байтах.
Да, я знаю. Я привел 2 разных варианта — пусть автор выберет подходящий.
B>>Завести рядом с массивом переменную, хранящую размер: GG>Зачем использовать дополнительную переменную, если при объявлении мы уже резервируем под массив извезстный размер?
Неверно. В данном случае при объявлении память не резервируется.
GG>Для проверки приведу небольшой код:
GG>
GG>extern char A[100];
GG>char A[] = "Hello!";
GG>int _tmain(int argc, _TCHAR* argv[])
GG>{
GG> std::cout<<sizeof A<<std::endl;
GG> A[strlen(A)] = 'A';// здесь должно было бы вывалиться!!!
GG> std::cout<<A<<std::endl;
GG> return 0;
GG>}
GG>
GG>Получим: GG>
GG>100
GG>Hello!A
GG>
GG>Если бы по массив выделялась память в размере strlen(A), то строка, выделенная жирным, вызвала бы исключительную ситуацию!
Если бы расстрел памяти отлавливался так легко, жить было бы гораздо веселее.
Так же исключительно с целью проверки приведу пример:
//1.cppint arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int arr2[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
//2.cppextern int arr[20];
extern int arr2[];
int main()
{
int i = sizeof(arr);
int i2 = arr2[9];
arr[19] = 0;
int i3 = arr2[9];
cout << i2 << "\t" << i3 << "\n";
return 0;
}
VC7.1: В Debug-конфигурации имеет место ситуация i3 == 0;
В Release все гораздо интереснее: на консоль выводятся две десятки, (оптимизатор считает себя вправе инициализировать i2 и i3 одним и тем же значением), но если вывести весь массив, то видно, что последний элемент изменен (расстрелян).
А все это — следствие неопределенного поведения, и надеятся на какую-либо исключительную ситуацию ну никак нельзя.
Здравствуйте, Bell, Вы писали:
B>А все это — следствие неопределенного поведения, и надеятся на какую-либо исключительную ситуацию ну никак нельзя.
Я конечно не настаиваю на 100% правоте своей мысли и убеждения, я, как и любой человек, могу ошибаться Но как тогда объяснить поведение оператора sizeof? Разве он показывает неправильный результат?
Здравствуйте, GGoga, Вы писали:
B>>А все это — следствие неопределенного поведения, и надеятся на какую-либо исключительную ситуацию ну никак нельзя.
GG>Я конечно не настаиваю на 100% правоте своей мысли и убеждения, я, как и любой человек, могу ошибаться Но как тогда объяснить поведение оператора sizeof? Разве он показывает неправильный результат?
Естественно, он показывает правильный результат
Итак, вариант 1.
extern int arr[10];
arr имеет полный тип (массив из 10 целых), и естественно к объекту можно применить sizeof, и sizeof вернет значение, равное 10*sizeof(int). Еще раз подчеркну — результат sizeof определяется типом объекта. Но определения этого объекта нет — оно должно быть где-то в другой единице трансляции, соответственно компилятор каким-то образом декорирует имя, и оставляет дальнейшую работу линкеру.
Во второй единице трансляции встречается определение:
int arr[] = {1, 2, 3, 4, 5};
Здесь всем видно, что arr имеет тип int[5]. Но беда в том, что декорированное имя для arr в этой единице трансляции совпадает с именем из первой единицы трансляции, т.е. информация о размере массива не учитывается . Линковка проходит без ошибок, но последсвия могут быть самыми неожиданными.
Кстати интересно было бы узнать, быть может существуют компиляторы, которые позволяют обнаружить подобные несостыковки?
Вариант 2.
extern int arr[];
Здесь все просто: arr имеет неполный тип, sizeof не может быть применен, пользователь должен сам позаботиться о том, чтобы была возможность узнать размер массива.
GG>Спасибо за объяснение
Всегда пожалуйста.
Здравствуйте, overt, Вы писали:
O>а потом пишу за пределами макс. индекса, то дебагер не ловит нарушение границ во время исполнения кода в DEBUG сборке. O>(MS VS 7)
Обычно нормальные программы на С++ пишут так, что объевление
extern const char str[100];
находится в каком-то хедере, а определение
const char str[] = "0123456789";
находится в каком-то cpp.
Это обеспечивает то, что все ссылки на str, сгенерированные в программе будут совместны.
Если делать как-то иначе, например, объявить массив одного размера, а определить другого, то возможно одно из двух.
1) Если из места определения видно объявление, то определение не скомпилируется.
2) Если из места определения объявление не видно, то программа будет некорректной, хотя компилятор и линкер не обяханы сообщать об этом.
Если кто-то не соблюдает такой подход, либо ODR нарушает, либо ещё как-то жжёт, то он сам себе виноват. Таков С++.
По существу вопроса могу посоветовать сгенерировать MAP файл и посмотреть какой реально размер зарезервирован под этот массив...
Если объявление не видно из точки определения, то наверное массив будет маленького размера и вся программа будет некорректна.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Почему не получается объявить extern'ом пользовательские типы данных, типа std::string? Конструкция extern поддерживает только встроенные типы или причина в чем-то другом?
Здравствуйте, overt, Вы писали:
O>Дабы не заводить новую тему..
O>Почему не получается объявить extern'ом пользовательские типы данных, типа std::string? Конструкция extern поддерживает только встроенные типы или причина в чем-то другом?
На консоль выводится "extern_string" как и положено.
Как говорится, что я делаю не так?
Любите книгу — источник знаний (с) М.Горький
Re: extern и массивы
От:
Аноним
Дата:
17.06.08 11:06
Оценка:
Вообще интересно, где в стандарте описано, как должен выводиться размер массива в результате:
extern int arr[100];
int arr[] = {1,2,3};
В 8.3.4/3 сказано, что
The first constant-expression can also be omitted
when the declarator is followed by an initializer (8.5). In this case the bound is calculated from the number
of initial elements (say, N) supplied (8.5.1), and the type of the identifier of D is “array ofN T.”
и ничего не сказано про предыдущие объявления этого массива.
Здравствуйте, Bell, Вы писали:
B>Итак, вариант 1. B>
B>extern int arr[10];
B>
B>arr имеет полный тип (массив из 10 целых), и естественно к объекту можно применить sizeof, и sizeof вернет значение, равное 10*sizeof(int). Еще раз подчеркну — результат sizeof определяется типом объекта. Но определения этого объекта нет — оно должно быть где-то в другой единице трансляции, соответственно компилятор каким-то образом декорирует имя, и оставляет дальнейшую работу линкеру.
B>Во второй единице трансляции встречается определение: B>
B>int arr[] = {1, 2, 3, 4, 5};
B>
B>Здесь всем видно, что arr имеет тип int[5]. Но беда в том, что декорированное имя для arr в этой единице трансляции совпадает с именем из первой единицы трансляции, т.е. информация о размере массива не учитывается . Линковка проходит без ошибок, но последсвия могут быть самыми неожиданными. B>Кстати интересно было бы узнать, быть может существуют компиляторы, которые позволяют обнаружить подобные несостыковки?
B>Вариант 2. B>
B>extern int arr[];
B>
B>Здесь все просто: arr имеет неполный тип, sizeof не может быть применен, пользователь должен сам позаботиться о том, чтобы была возможность узнать размер массива.
GG>>Спасибо за объяснение B>Всегда пожалуйста.
Еще раз СПАСИБО! Теперь понятно поведение оператора sizeof
Здравствуйте, GGoga, Вы писали:
GG>Для проверки приведу небольшой код:
GG>
GG>extern char A[100];
GG>char A[] = "Hello!";
GG>int _tmain(int argc, _TCHAR* argv[])
GG>{
GG> std::cout<<sizeof A<<std::endl;
GG> A[strlen(A)] = 'A';// здесь должно было бы вывалиться!!!
GG> std::cout<<A<<std::endl;
GG> return 0;
GG>}
GG>
ИМХО, не совсем так. В начале будет выделен участок памяти длиной strlen(A) + 1 байт, так как при таком объявлении в конце будет дописан '\0'. Потом вместо этого нуля будет записан символ 'A', пока все нормально, вываливаться не должно. А вот при последующем выводе просто повезло, что дальше шли нули. Если бы там лежали какие-то данные результат был бы другим А если бы массив ещё лежал так, что за ним вообще ничего нет, вот тогда бы вывалилось.
Здравствуйте, Brn, Вы писали:
Brn>ИМХО, не совсем так. В начале будет выделен участок памяти длиной strlen(A) + 1 байт, так как при таком объявлении в конце будет дописан '\0'. Потом вместо этого нуля будет записан символ 'A', пока все нормально, вываливаться не должно. А вот при последующем выводе просто повезло, что дальше шли нули. Если бы там лежали какие-то данные результат был бы другим А если бы массив ещё лежал так, что за ним вообще ничего нет, вот тогда бы вывалилось.
Не заметил extern в начале. Выделено будет 100 байт а не strlen(A) + 1.