[image]

Споры про С (вынесено из топика Nonconformist-а)

 
1 2 3
+
-
edit
 

umbriel

опытный

Serge77> Интересно, зачем это сделано? Почему компилятор по умолчанию не может обнулять переменную при объявлении?

Компилятор обнуляет статические и глобальные переменные, а в дебаг режиме заполняет неинициализированные спец. значениями типа 0xcdcdcdcd 0xfeeefeee и пр
   
US Mishka #18.10.2008 22:10  @Oxandrolone#18.10.2008 17:11
+
-
edit
 

Mishka

модератор
★★★
Serge77>> Интересно, зачем это сделано? Почему компилятор по умолчанию не может обнулять переменную при объявлении?
umbriel> Компилятор обнуляет статические и глобальные переменные, а в дебаг режиме заполняет неинициализированные спец. значениями типа 0xcdcdcdcd 0xfeeefeee и пр

Это очень спорное утверждение. Местами даже просто неверное.
   3.0.33.0.3
+
-
edit
 

umbriel

опытный

umbriel>> Компилятор обнуляет статические и глобальные переменные, а в дебаг режиме заполняет неинициализированные спец. значениями типа 0xcdcdcdcd 0xfeeefeee и пр
Mishka> Это очень спорное утверждение. Местами даже просто неверное.

Что значит "спорное", это так как есть и точка.
Это по соображениям производительности - глобальные и стат. инициализируются 1 раз, а локальные кол-во инициализаций не ограничено.
   
Это сообщение редактировалось 19.10.2008 в 13:47
US Mishka #19.10.2008 17:40  @Oxandrolone#19.10.2008 12:39
+
-
edit
 

Mishka

модератор
★★★
umbriel> Что значит "спорное", это так как есть и точка.

Потому, что это не так.

umbriel> Это по соображениям производительности - глобальные и стат. инициализируются 1 раз, а локальные кол-во инициализаций не ограничено.

Нет. Например, в Unix-е, глобальная память может инициализироваться 0xdeadbeaf. И делается это обычно всегда в debug режиме. Кроме того, память при выделении, инициализируется очень часто OS, а не компилятором.
   3.0.33.0.3
+
-
edit
 

umbriel

опытный

umbriel>> Что значит "спорное", это так как есть и точка.
Mishka> Потому, что это не так.
Вот надо же, а я студентов учу что именно так
Книжек полно, вот например http://lib.ru/CTOTOR/kernigan.txt можно посмотреть,
Или просто проверить, ставлю 100$ что переменные будут нулевые хоть на турбо ц хоть на гцц :)
Фактически, эти переменные грузятся непосредственно вместе с кодом из ехе, по дефолту по стандарту си там нули.


Mishka> Нет. Например, в Unix-е, глобальная память может инициализироваться 0xdeadbeaf. И делается это обычно всегда в debug режиме.
Mishka, признайтесь что просто хотите поспорить. Это нормально, ну? ;)
Я же написал выше что в дебаг режиме локальные переменные инитяца спец значениями - 0xcdcdcdcd, 0xfeeefeee, 0x0badf00d и прочее. Что же значит это "нет"?)


Mishka> Кроме того, память при выделении, инициализируется очень часто OS, а не компилятором.
При выделении память не инициализируется ни чем. К локальным переменным это не относится, потому что они находятся собсно в стеке.
   
EE Татарин #20.10.2008 02:04  @Oxandrolone#20.10.2008 01:41
+
-
edit
 

Татарин

координатор
★★★★★
umbriel>>> Что значит "спорное", это так как есть и точка.
Mishka>> Потому, что это не так.
umbriel> Вот надо же, а я студентов учу что именно так
umbriel> Книжек полно, вот например http://lib.ru/CTOTOR/kernigan.txt можно посмотреть,
umbriel> Или просто проверить, ставлю 100$ что переменные будут нулевые хоть на турбо ц хоть на гцц :)
В случае Non-conformist'a это была именно автоматическая переменная. :)

umbriel> При выделении память не инициализируется ни чем. К локальным переменным это не относится, потому что они находятся собсно в стеке.
При выделении память может очищаться системой из соображений безопасности.
   3.0.33.0.3
RU umbriel #20.10.2008 02:13  @Татарин#20.10.2008 02:04
+
-
edit
 

umbriel

опытный

Татарин> В случае Non-conformist'a это была именно автоматическая переменная. :)
Авто.. какая переменная?)

Татарин> При выделении память может очищаться системой из соображений безопасности.
Да, может конечно, но такой системы у Non-conformist'a и никогда не будет
   
EE Татарин #20.10.2008 02:19  @Oxandrolone#20.10.2008 02:13
+
-
edit
 

Татарин

координатор
★★★★★
Татарин>> В случае Non-conformist'a это была именно автоматическая переменная. :)
umbriel> Авто.. какая переменная?)
Автоматическая.

Стековая.
   3.0.33.0.3
RU umbriel #20.10.2008 02:31  @Татарин#20.10.2008 02:19
+
-
edit
 

umbriel

опытный

Татарин> Стековая.
Ok

Вообще сортировка - это очень хорошая задача. Обязательно сделай все возможные
   
US Mishka #20.10.2008 08:58  @Oxandrolone#20.10.2008 01:41
+
-
edit
 

Mishka

модератор
★★★
umbriel> Вот надо же, а я студентов учу что именно так

А кто виноват, что ты их бреду учишь.

umbriel> Книжек полно, вот например http://lib.ru/CTOTOR/kernigan.txt можно посмотреть,

И?

umbriel> Или просто проверить, ставлю 100$ что переменные будут нулевые хоть на турбо ц хоть на гцц :)

Не надо проверять, потому что пролетишь со свистом. :)

umbriel> Фактически, эти переменные грузятся непосредственно вместе с кодом из ехе, по дефолту по стандарту си там нули.

А можно ссылку в стандарт?

umbriel> Mishka, признайтесь что просто хотите поспорить. Это нормально, ну? ;)
Не, просто знаю.

umbriel> Я же написал выше что в дебаг режиме локальные переменные инитяца спец значениями - 0xcdcdcdcd, 0xfeeefeee, 0x0badf00d и прочее. Что же значит это "нет"?)
Странно, я писал про локальные переменные?

umbriel> При выделении память не инициализируется ни чем. К локальным переменным это не относится, потому что они находятся собсно в стеке.

Ага, я уже понял, что ты ни фига не знаешь. Выделение памяти бывает не только под локальные переменные. А так немного почитай про разные ОСи.
   3.0.33.0.3
US Mishka #20.10.2008 09:37  @Oxandrolone#20.10.2008 01:55
+
-
edit
 

Mishka

модератор
★★★
umbriel> Лично мое мнение, что программирование надо изучать не со всяких там бейсиков, а наоборот, с асма. Чем меньше абстракта - тем прощще понять и не будет глупых ошибок/вопросов.

ИМХО, наихудший подход.

По поводу инициализации — вот выдержка из последнего С++ стандарта (С нет под рукой дома):
5 To zero-initialize an object of type T means:
— if T is a scalar type (3.9), the object is set to the value of 0 (zero) converted to T;
— if T is a non-union class type, each nonstatic data member and each base-class subobject is zeroinitialized;
— if T is a union type, the object’s first named data member89) is zero-initialized;
— if T is an array type, each element is zero-initialized;
— if T is a reference type, no initialization is performed.
To default-initialize an object of type T means:
— if T is a non-POD class type (clause 9), the default constructor for T is called (and the initialization is
ill-formed if T has no accessible default constructor);
— if T is an array type, each element is default-initialized;
— otherwise, the object is zero-initialized.
To value-initialize an object of type T means:
— if T is a class type (clause 9) with a user-declared constructor (12.1), then the default constructor for T is
called (and the initialization is ill-formed if T has no accessible default constructor);
— if T is a non-union class type without a user-declared constructor, then every non-static data member
and base-class component of T is value-initialized;
— if T is an array type, then each element is value-initialized;
— otherwise, the object is zero-initialized
A program that calls for default-initialization or value-initialization of an entity of reference type is illformed.
If T is a cv-qualified type, the cv-unqualified version of T is used for these definitions of zeroinitialization,
default-initialization, and value-initialization.
6 Every object of static storage duration shall be zero-initialized at program startup before any other initialization
takes place. [Note: in some cases, additional initialization is done later. ]
7 An object whose initializer is an empty set of parentheses, i.e., (), shall be value-initialized.
8 [Note: since () is not permitted by the syntax for initializer,
X a();
is not the declaration of an object of class X, but the declaration of a function taking no argument and
returning an X. The form () is permitted in certain other initialization contexts (5.3.4, 5.2.3, 12.6.2). ]
9 If no initializer is specified for an object, and the object is of (possibly cv-qualified) non-POD class type (or
array thereof), the object shall be default-initialized; if the object is of const-qualified type, the underlying
class type shall have a user-declared default constructor. Otherwise, if no initializer is specified for a nonstatic
object, the object and its subobjects, if any, have an indeterminate initial value90); if the object or any
of its subobjects are of const-qualified type, the program is ill-formed.
 


Внимательно смотрим на пункт — if T is a union type, the object’s first named data member89) is zero-initialized;
И долго думаем, когда может быть оставлен мусор в глобальной памяти.
   3.0.33.0.3
RU HolyBoy #20.10.2008 10:31  @Oxandrolone#20.10.2008 01:41
+
-
edit
 

HolyBoy

старожил

umbriel> Вот надо же, а я студентов учу что именно так

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

umbriel> Книжек полно, вот например http://lib.ru/CTOTOR/kernigan.txt можно посмотреть,
umbriel> Или просто проверить, ставлю 100$ что переменные будут нулевые хоть на турбо ц хоть на гцц :)
umbriel> Фактически, эти переменные грузятся непосредственно вместе с кодом из ехе, по дефолту по стандарту си там нули.

Никто никуда не грузится. У ОС запрашивается кусок памяти нужного размера и ОС выдает адрес этого куска. Все. Больше ничего не происходит. Всякие инициализации нулями, единичками и прочими вещами, если не указано явно в программе — на совести авторов компиляторов. И, как уже отметили, оно только в дебаг-режиме работает.
   
+
-
edit
 

umbriel

опытный

Ну о чем можно говорить, если человек видит белое - говорит черное.

#include <stdio.h>
#include <conio.h>

int A;

int main()
{
static int B;
int C;

printf("A = %d, B = %d, C = %d", A, B, C);

getch();
return 0;
}


Нон-конформист, запусти это, пожалуйста, чтобы не было больше споров. A и B будут нулевые.
Не обращай внимание на ворнинг что локальная переменная C используется без инициализации.
   
Это сообщение редактировалось 20.10.2008 в 11:45
+
-
edit
 

umbriel

опытный

umbriel>> Вот надо же, а я студентов учу что именно так
HolyBoy> Странно, а вот меня учили и я сам до того, как меня учили, уже знал что это не так.
umbriel>> Книжек полно, вот например http://lib.ru/CTOTOR/kernigan.txt можно посмотреть,
umbriel>> Или просто проверить, ставлю 100$ что переменные будут нулевые хоть на турбо ц хоть на гцц :)
umbriel>> Фактически, эти переменные грузятся непосредственно вместе с кодом из ехе, по дефолту по стандарту си там нули.
HolyBoy> Никто никуда не грузится. У ОС запрашивается кусок памяти нужного размера и ОС выдает адрес этого куска. Все. Больше ничего не происходит. Всякие инициализации нулями, единичками и прочими вещами, если не указано явно в программе — на совести авторов компиляторов. И, как уже отметили, оно только в дебаг-режиме работает.

Вот у тебя же есть gcc, тоже можешь проверить ;)
   

umbriel

опытный

yacc> На этом народ горит, когда прога у них входит в бесконечный цикл ;)

Результат операции присваивания равен тому, что было присвоено, в данном случае 0/ложь - не будет ни одного цикла.
На низком уровне (кстати к изучения сначала асма) это связано с установлением флага нуля ZF после последней операции, например cmp, dec, mov
   
Это сообщение редактировалось 20.10.2008 в 12:16
+
-
edit
 

yacc

старожил
★★★
yacc>> На этом народ горит, когда прога у них входит в бесконечный цикл ;)
umbriel> Результат операции присваивания равен тому, что было присвоено, в данном случае 0/ложь - не будет ни одного цикла.
umbriel> На низком уровне (кстати к изучения сначала асма) это связано с установлением флага нуля ZF после последней операции, например cmp, dec, mov
И это типичное бажное место.
   6.06.0
US Mishka #20.10.2008 15:06  @Oxandrolone#20.10.2008 12:10
+
-
edit
 

Mishka

модератор
★★★
umbriel> На низком уровне (кстати к изучения сначала асма) это связано с установлением флага нуля ZF после последней операции, например cmp, dec, mov

Чего-то покапавшись в голове я и не припомню общедоступную архитектуру, которая устанавливает флажки при команде mov. Можно пример?
   3.0.33.0.3

Mishka

модератор
★★★
yacc> И это типичное бажное место.
И бажное оно в С и С++ из-за синтаксиса. Скажем, в том же Алголе 68, Аде — ни каких проблем.

Non-conformist, кстати, вот тебе совет по стилю, который позволяет избежать таких ошибок — только ты сам оцени, если он тебе понравится.

Вместо писания i == 0, f !=5 можно написать 0 == i, 5 != f. Выглядит необычно, но, если забыл указать дополнительный = или !, то компилятор поймает за руку, т.к. 0 = i, 5 = f будет присвоением в констатнту, что запрещено по языку.
   3.0.33.0.3
RU HolyBoy #21.10.2008 02:10  @Oxandrolone#20.10.2008 11:54
+
-
edit
 

HolyBoy

старожил

umbriel> Вот у тебя же есть gcc, тоже можешь проверить ;)

Проверяем. ;)
Компиляция без флагов оптимизации и прочего:

$ gcc calcul.c -o result

Исходный код calcul.c

code text
  1. #include <stdio.h>
  2.  
  3. int a;
  4. double b;
  5.  
  6. int main(void){
  7.  
  8.  
  9. printf ("address of a is %d a=%d\\taddress of b is %d b=%f\\n",&a,a,&b,b );
  10.  
  11. return 0;
  12.  
  13. }


Многократно запускаем result:
code text
  1. $ ./result
  2. address of a is 134520864 a=0   address of b is 134520872 b=0.000000
  3. @homepc ~/programming $ ./result
  4. address of a is 134520864 a=0   address of b is 134520872 b=0.000000
  5. @homepc ~/programming $ ./result
  6. address of a is 134520864 a=0   address of b is 134520872 b=0.000000
  7. @homepc ~/programming $ ./result
  8. address of a is 134520864 a=0   address of b is 134520872 b=0.000000
  9. @homepc ~/programming $ ./result
  10. address of a is 134520864 a=0   address of b is 134520872 b=0.000000


И с разочарованием убеждаемся, что каждый раз одни и те же участки оперативки выделяются. А поскольку её много:
code text
  1. $ free
  2.              total       used       free     shared    buffers     cached
  3. Mem:       2074472    1921844     152628          0      75412    1312424
  4. -/+ buffers/cache:     534008    1540464
  5. Swap:      2939884          0    2939884


то надо придумать что-то другое. Попробуем выделить массив. Новая версия тестовой программы:
code text
  1. #include <stdio.h>
  2.  
  3. int a[10], i, *j;
  4.  
  5. int main(void){
  6. i=0;
  7. j=a;
  8. while ( (i<10) && !(*j) ){
  9.   printf ("a[%d]=%d\\n",i,*j);
  10.   j+=i++;
  11. }
  12.  
  13. printf("\\n");
  14. printf ("a[%d]=%d\\n",i,*j);
  15.  
  16. return 0;
  17.  
  18. }


Результат:
code text
  1. $ gcc calcul2.c -o result2 ; ./result2
  2. a[0]=0
  3. a[1]=0
  4. a[2]=0
  5. a[3]=0
  6. a[4]=0
  7.  
  8. a[5]=5


Изменим размер массива, скажем, на 20 элементов:
code text
  1. #include <stdio.h>
  2.  
  3. int a[20], i, *j;
  4.  
  5. int main(void){
  6. i=0;
  7. j=a;
  8. while ( (i<20) && !(*j) ){
  9.   printf ("a[%d]=%d\\n",i,*j);
  10.   j+=i++;
  11. }
  12.  
  13. printf("\n");
  14. printf ("a[%d]=%d\\n",i,*j);
  15.  
  16. return 0;
  17.  
  18. }


Результат:
code text
  1. $ gcc calcul2.c -o result2 ; ./result2
  2. a[0]=0
  3. a[1]=0
  4. a[2]=0
  5. a[3]=0
  6. a[4]=0
  7. a[5]=0
  8. a[6]=0
  9.  
  10. a[7]=134520980


30 элементов:
code text
  1. $ gcc calcul2.c -o result2 ; ./result2
  2. a[0]=0
  3. a[1]=0
  4. a[2]=0
  5. a[3]=0
  6. a[4]=0
  7. a[5]=0
  8. a[6]=0
  9. a[7]=0
  10. a[8]=0
  11. a[9]=0
  12. a[10]=0
  13. a[11]=0
  14. a[12]=0
  15. a[13]=0
  16. a[14]=0
  17. a[15]=0
  18. a[16]=0
  19. a[17]=0
  20. a[18]=0
  21. a[19]=0
  22. a[20]=0
  23. a[21]=0
  24. a[22]=0
  25. a[23]=0
  26. a[24]=0
  27. a[25]=0
  28. a[26]=0
  29. a[27]=0
  30. a[28]=0
  31. a[29]=0
  32.  
  33. a[30]=0
А тут все 30 элементов оказались в куске памяти, инициализированном нулями.

Ну и на закуску, динамический массив (из C99):
code text
  1. #include <stdio.h>
  2.  
  3. int i,*j,k;
  4.  
  5. int main(void){
  6. printf ("enter dimension of array: ");
  7. scanf ("%d", &k);
  8.  
  9.  
  10. int a[k];
  11.  
  12. i=0;
  13. j=a;
  14. while ( (i<k) && !(*j) ){
  15.   printf ("a[%d]=%d\n",i,*j);
  16.   j+=i++;
  17. }
  18.  
  19. printf("\n");
  20. printf ("a[%d]=%d\n",i,*j);
  21.  
  22. return 0;
  23.  
  24. }


Компилируется так: gcc -std=c99 calcul3.c -o result3
Результаты:
code text
  1. $ ./result3
  2. enter dimension of array: 16
  3. a[0]=0
  4. a[1]=0
  5.  
  6. a[2]=-1080289182
  7.  
  8. $ ./result3
  9. enter dimension of array: 20
  10.  
  11. a[0]=-1208969208
  12.  
  13. $ ./result3
  14. enter dimension of array: 30
  15.  
  16. a[0]=-1208229888
  17.  
  18. $ ./result3
  19. enter dimension of array: 1000
  20. a[0]=0
  21. a[1]=0
  22. a[2]=0
  23. a[3]=0
  24. a[4]=0
  25. a[5]=0
  26. a[6]=0
  27. a[7]=0
  28. a[8]=0
  29. a[9]=0
  30. a[10]=0
  31. a[11]=0
  32. a[12]=0
  33. a[13]=0
  34. a[14]=0
  35. a[15]=0
  36. a[16]=0
  37. a[17]=0
  38. a[18]=0
  39. a[19]=0
  40. a[20]=0
  41. a[21]=0
  42. a[22]=0
  43. a[23]=0
  44. a[24]=0
  45. a[25]=0
  46. a[26]=0
  47. a[27]=0
  48. a[28]=0
  49.  
  50. a[29]=-1208724539
  51.  
  52. $ ./result3
  53. enter dimension of array: 50
  54. a[0]=0
  55. a[1]=0
  56. a[2]=0
  57.  
  58. a[3]=-1208978780


Результаты говорят сами за себя. :P

Кстати, можно вынести мой и не только флуд не по теме отдельно куда-нить?
   
Это сообщение редактировалось 21.10.2008 в 02:17
EE Татарин #21.10.2008 03:49  @Mishka#20.10.2008 15:06
+
-
edit
 

Татарин

координатор
★★★★★
umbriel>> На низком уровне (кстати к изучения сначала асма) это связано с установлением флага нуля ZF после последней операции, например cmp, dec, mov
Mishka> Чего-то покапавшись в голове я и не припомню общедоступную архитектуру, которая устанавливает флажки при команде mov. Можно пример?
Что-то вроде mov flags, 0 - возможно во многих архитектурах :D
Но вообще говоря - да, umbriel с mov сильно тут погорячился. :)
   3.0.33.0.3
US Mishka #21.10.2008 07:46  @Татарин#21.10.2008 03:49
+
-
edit
 

Mishka

модератор
★★★
Татарин> Что-то вроде mov flags, 0 - возможно во многих архитектурах :D

Ну, это может быть и load flag и set flag. :) Но это прямая операция, а не косвенная.

Татарин> Но вообще говоря - да, umbriel с mov сильно тут погорячился. :)

Ага, а ещё с обнулением, тем более, что по IEEE стандарту 0 для плавающего представляется вовсе не 0-м. :) Там характеристика сдвинута кое-где. Ну и NULL вовсе не 0 часто. Хотя по стандарту, NULL константа определена как нуль, которая неявным образом приводится к настоящему NULL, в логических выражениях NULL приводится неявно к false. Аналогично есть преобразование 0 в false для bool, хотя внутреннее представление вовсе не обязано быть 0.
   3.0.33.0.3
+
-
edit
 
+
-
edit
 

umbriel

опытный

HolyBoy>И с разочарованием убеждаемся, что каждый раз одни и те же участки оперативки выделяются.

То есть ты полагал что память "выделяется" еще и каждый раз в разном месте? И как ты представлял себе обращение программы к переменным?

Еще раз повторяю, в С глобальные и статические переменные грузятся из ехе в сегмент данных.
Локальные переменные находятся либо в стеке, и обращение к ним идет через смещение относительно esp, либо в регистрах.
Память выделяется ОС по требованию в области heap и это не имеет отношения ни к локальным, ни к глобальным переменным.
В случае дебага локальные переменные и выделяемая память обычно заполняются спец значениями, но это зависит от компилятора.

Далее,

int a[20], i, *j;

int main(void){
i=0;
j=a;
while ( (i<20) && !(*j) ){
printf ("a[%d]=%d\\n",i,*j);
j+=i++;
}

у тебя на первой итерации указатель увеличивается на 0, в следующем на 1, 2, 3 и т.д.
Ты индексируешь элементы 0, 0, 1, 3, 6, 10, 15, 21, 28 ...

То есть ты не только не представляешь как работает программа, так еще и проиндексировать не в состоянии.
И куда ты спорить лезешь?
   
RU umbriel #21.10.2008 10:04  @Татарин#21.10.2008 03:49
+
-
edit
 

umbriel

опытный

mov не изменяет флага, да.
А это я написал? эээ ну да, погррячился

Еще for (int i = 0;.....) на си нельзя писать, да

"//" можно, в си это символ игнорирования строки до конца (а не одностроковый камент)
Вобщем разницы никакой.
   
Это сообщение редактировалось 21.10.2008 в 10:09
+
-
edit
 

umbriel

опытный

Mishka>umbriel> А в чем проблема?
Mishka>В том, что пойнтер не обязан входить в unsigned int. Почитай стандарт.

Ну можно преобразовывать к unsigned long long, хоть 1 пример где это работать не будет
Стандартов я уже начитался, я сам себе стандарт.
   
1 2 3

в начало страницы | новое
 
Поиск
Настройки
Твиттер сайта
Статистика
Рейтинг@Mail.ru