extern и массивы
От: overt Ниоткуда  
Дата: 16.06.08 15:46
Оценка:
В одном файле объявлен и инициализирован массив
char str[] = "0123456789";

а в дргом объявлен как
extern char str[100];


Каков истинный размер такого массива, 100 или 11 ?
И как убедиться, что рамезр именно таков, т.к. даже если объявляю
char str[] = "0123456789";

а потом пишу за пределами макс. индекса, то дебагер не ловит нарушение границ во время исполнения кода в DEBUG сборке.
(MS VS 7)
(MS VS 7.1/8.0)
Re: extern и массивы
От: Bell Россия  
Дата: 16.06.08 15:52
Оценка:
Здравствуйте, overt, Вы писали:

O>В одном файле объявлен и инициализирован массив

O>
O>char str[] = "0123456789";
O>

O>а в дргом объявлен как
O>
O>extern char str[100];
O>


O>Каков истинный размер такого массива, 100 или 11 ?

11

O>И как убедиться, что рамезр именно таков, т.к. даже если объявляю

O>
O>char str[] = "0123456789";
O>

O>а потом пишу за пределами макс. индекса, то дебагер не ловит нарушение границ во время исполнения кода в DEBUG сборке.
O>(MS VS 7)

Иcпользовать strlen.
Завести рядом с массивом переменную, хранящую размер:
char str[] = "0123456789";
size_t str_sz = sizeof(str)/sizeof(*str);

...

extern char* str;
extern size_t str_sz;
Любите книгу — источник знаний (с) М.Горький
Re[2]: extern и массивы
От: overt Ниоткуда  
Дата: 16.06.08 16:02
Оценка:
Здравствуйте, Bell, Вы писали:


B>Завести рядом с массивом переменную, хранящую размер:


интересно, что если вызвать sizeof(str) в любом другом месте, то получаем 100
(MS VS 7.1/8.0)
Re[3]: extern и массивы
От: Bell Россия  
Дата: 16.06.08 16:07
Оценка:
Здравствуйте, overt, Вы писали:

O>интересно, что если вызвать sizeof(str) в любом другом месте, то получаем 100


Поэтому лучше убрать размер из объявления:
extern char str[];
Любите книгу — источник знаний (с) М.Горький
Re: extern и массивы
От: overt Ниоткуда  
Дата: 16.06.08 16:24
Оценка:
Код не мой, поэтому удивило такое, можно поправить конечно.
Теперь исключительно любопытно, почему же тогда sizeof при последующих вызовах дает 100, может и правда 100 выделяется, может авторы сырца не зря так написали?
(MS VS 7.1/8.0)
Re[2]: extern и массивы
От: overt Ниоткуда  
Дата: 16.06.08 16:41
Оценка:
Проверил, объявив заполненный массив следом за объявлением str.
Вы правы, действительно 11 (точнее 12, видимо для кратности).
(MS VS 7.1/8.0)
Re[2]: extern и массивы
От: GGoga  
Дата: 16.06.08 16:43
Оценка:
Здравствуйте, 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), то строка, выделенная жирным, вызвала бы исключительную ситуацию!
Re: extern и массивы
От: merk Россия  
Дата: 16.06.08 17:04
Оценка:
Здравствуйте, overt, Вы писали:

O>В одном файле объявлен и инициализирован массив

O>
O>char str[] = "0123456789";
O>

O>а в дргом объявлен как
O>
O>extern char str[100];
O>


O>Каков истинный размер такого массива, 100 или 11 ?


Обьявление extern есть сообщение компилятору при компиляции текущего файла, что мол где-то существует такой вот обьект, такого вот типа. никакого реального аллокирования этого обьекта компилятор тут не делает. он просто генерит код с символической ссылкой на это имя, и уже линкер должен будет, взяв все файлы сборки, вместо символа str, поставить какой-то адрес. если линкер такого имени не найдет — будет ругаться.
потому реальный массив будет размером определяемым декларацией
char str[] = "0123456789";
Re[3]: extern и массивы
От: Bell Россия  
Дата: 16.06.08 17:33
Оценка:
Здравствуйте, 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.cpp
int 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.cpp
extern 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 одним и тем же значением), но если вывести весь массив, то видно, что последний элемент изменен (расстрелян).
А все это — следствие неопределенного поведения, и надеятся на какую-либо исключительную ситуацию ну никак нельзя.
Любите книгу — источник знаний (с) М.Горький
Re[4]: extern и массивы
От: GGoga  
Дата: 16.06.08 17:41
Оценка:
Здравствуйте, Bell, Вы писали:

B>А все это — следствие неопределенного поведения, и надеятся на какую-либо исключительную ситуацию ну никак нельзя.


Я конечно не настаиваю на 100% правоте своей мысли и убеждения, я, как и любой человек, могу ошибаться Но как тогда объяснить поведение оператора sizeof? Разве он показывает неправильный результат?

Спасибо за объяснение
Re[5]: extern и массивы
От: Bell Россия  
Дата: 16.06.08 18:20
Оценка:
Здравствуйте, 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>Спасибо за объяснение

Всегда пожалуйста.
Любите книгу — источник знаний (с) М.Горький
Re: С++ суров, но это С++
От: Erop Россия  
Дата: 16.06.08 19:11
Оценка:
Здравствуйте, overt, Вы писали:

O>а потом пишу за пределами макс. индекса, то дебагер не ловит нарушение границ во время исполнения кода в DEBUG сборке.

O>(MS VS 7)

Обычно нормальные программы на С++ пишут так, что объевление
extern const char str[100];
находится в каком-то хедере, а определение
const char str[] = "0123456789";
находится в каком-то cpp.
Это обеспечивает то, что все ссылки на str, сгенерированные в программе будут совместны.
Если делать как-то иначе, например, объявить массив одного размера, а определить другого, то возможно одно из двух.
1) Если из места определения видно объявление, то определение не скомпилируется.
2) Если из места определения объявление не видно, то программа будет некорректной, хотя компилятор и линкер не обяханы сообщать об этом.

Если кто-то не соблюдает такой подход, либо ODR нарушает, либо ещё как-то жжёт, то он сам себе виноват. Таков С++.


По существу вопроса могу посоветовать сгенерировать MAP файл и посмотреть какой реально размер зарезервирован под этот массив...
Если объявление не видно из точки определения, то наверное массив будет маленького размера и вся программа будет некорректна.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re: extern и сложные типы
От: overt Ниоткуда  
Дата: 17.06.08 10:05
Оценка:
Дабы не заводить новую тему..

Почему не получается объявить extern'ом пользовательские типы данных, типа std::string? Конструкция extern поддерживает только встроенные типы или причина в чем-то другом?
(MS VS 7.1/8.0)
Re[2]: extern и сложные типы
От: Bell Россия  
Дата: 17.06.08 10:11
Оценка:
Здравствуйте, overt, Вы писали:

O>Дабы не заводить новую тему..


O>Почему не получается объявить extern'ом пользовательские типы данных, типа std::string? Конструкция extern поддерживает только встроенные типы или причина в чем-то другом?


//1.cpp
#include <string>

extern std::string ext_str;

void test_fn()
{
   std::cout << ext_str << '\n';
}

//2.cpp
#include <string>

std::string ext_str = "extern_string";

void test_fn();

int main( int argc, char *argv[] ) 
{
   test_fn();
   return 0;
}


На консоль выводится "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.”

и ничего не сказано про предыдущие объявления этого массива.
Re[6]: extern и массивы
От: GGoga  
Дата: 17.06.08 12:53
Оценка:
Здравствуйте, 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
Re[3]: extern и сложные типы
От: overt Ниоткуда  
Дата: 17.06.08 13:12
Оценка:
Здравствуйте, Bell, Вы писали:
B>Как говорится, что я делаю не так?

видимо дело в const:
//2.cpp
#include <string>

const std::string ext_str = "extern_string";

void test_fn();

int main( int argc, char *argv[] ) 
{
    test_fn();
    return 0;
}

//1.cpp
#include <string>
#include <iostream>

extern const std::string ext_str;

void test_fn()
{
    std::cout << ext_str << '\n';
}


error LNK2001: unresolved external symbol "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const ext_str" (?ext_str@@3V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@B)


не подскажете почему?
(MS VS 7.1/8.0)
Re[4]: extern и сложные типы
От: Bell Россия  
Дата: 17.06.08 13:16
Оценка: 2 (1)
Здравствуйте, overt, Вы писали:

B>>Как говорится, что я делаю не так?


O>видимо дело в const:

O>const std::string ext_str = "extern_string";

O> не подскажете почему?
Ну отчего же не подсказать — подскажу

По умолчанию константы имеют внутреннее связывание. Чтобы сделать константу с внешним связыванием, нужно явно указать extern при определении:
extern const std::string ext_str = "extern_string";
Любите книгу — источник знаний (с) М.Горький
Re[3]: extern и массивы
От: Brn Россия  
Дата: 17.06.08 13:19
Оценка:
Здравствуйте, 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', пока все нормально, вываливаться не должно. А вот при последующем выводе просто повезло, что дальше шли нули. Если бы там лежали какие-то данные результат был бы другим А если бы массив ещё лежал так, что за ним вообще ничего нет, вот тогда бы вывалилось.
Re[4]: extern и массивы
От: Brn Россия  
Дата: 17.06.08 13:24
Оценка: :)
Здравствуйте, Brn, Вы писали:

Brn>ИМХО, не совсем так. В начале будет выделен участок памяти длиной strlen(A) + 1 байт, так как при таком объявлении в конце будет дописан '\0'. Потом вместо этого нуля будет записан символ 'A', пока все нормально, вываливаться не должно. А вот при последующем выводе просто повезло, что дальше шли нули. Если бы там лежали какие-то данные результат был бы другим А если бы массив ещё лежал так, что за ним вообще ничего нет, вот тогда бы вывалилось.


Не заметил extern в начале. Выделено будет 100 байт а не strlen(A) + 1.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.