singlepost

Разница с/c++ << На главную или назад  

Всем доброго времени суток!
Возникла следующая задачка:
Есть ли разница в работе следующего кода скомпилированного с(си) компилятором и с++ компилятором.

int x;
int main() {
int y;
return 0;
}

Т.е. как будут инициализироваться эти переменные?
Есть подозрение, что если их объявить константными, то разницы не будет.

120 ответов в теме “Разница с/c++”

  1. 77
    Леонид Максимов ответил:

    ну дык ворнинги тоже читать полезно.

  2. 76
    Нгамдкхе Кверос ответил:

    "В языке С существует тесная взаимосвязь между типами "массив типа Т" и "указатель на Т". Когда в выражении встречается идентификатор массива, то тип идентификатора преобразуется из "массив типа Т" в "указатель на Т", значение же идентификатора конвертируется в указатель на первый элемент массива. Это одно из правил обычных унарных преобразований."

    возмжно, в стандарте это так и есть, но работает всё так как работает.
    надо точно смотреть в грамматику транслятора.
    в моём случае транслятор на &b вернул тотже (char*) а не с двумя… что с одной стороны логично раз такой ячейки памяти нет, а с другой если id(mass) он всегда заменяет на указатель на начало то &(b) он должен был бы получить &(char*) чего у него просто нет физически и от того должен бы дать ошибку

    "в общем, понятно. вы имели в виду логическое преобразование, в результирующем коде отсутствующее. я же, со своей стороны, такие преобразования преобразованиями не считаю."
    вот это зря, ведь (unsigned char)a+(signed char)b= ???хотя ни одной лишней команды асма добавлено не будет. так что все преобразования типов нужно считать за преобразования типов. у нас так в некоторых программах было когда таймеры вырубались :-) а там не чары а инты были, так что пол дня машина работает потом пол дня от неё данных нет, ловили долго и упорно.

  3. 75
    Леонид Максимов ответил:

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

  4. 74
    Анатолий Лютин ответил:

    >>"преобразовать массив к указателю"
    >>
    >>именно это я и имел ввиду.

    А не я ли это цитировал в #34?

  5. 73
    Нгамдкхе Кверос ответил:

    "преобразовать массив к указателю"

    именно это я и имел ввиду.

  6. 72
    Анатолий Лютин ответил:

    Здорово что вы сами разобрались. Мысль была в том, что в первом случае массив и указатель инициализировались одинаково, т.е. массив был тождественен указателю. А во втором массив инициализировался поэлементно. Считаю что надо отделить обсуждения "как должно быть по "языку" и "как поступает компилятор" друг от друга.

  7. 71
    Леонид Максимов ответил:

    #70

    прочитал, единственная разница – компилятор решил строку, которой инициализируется массив, не засовывать в сегмент данных. вполне разумно, так как char b[] = "blablabla"; /!!!!! лучше звучит как char b[] = {'b', 'l', 'a', 'b', 'l', 'a', 'b', 'l', 'a', 0};

  8. 70
    Леонид Максимов ответил:

    #67
    >> То есть по вашему, в код должно было "вшито" что-то типа
    <——>movl<–> 0xffff, %eax
    >> В примере что я привёл, адрес, фактически, вычисляется, а не вшивается, как вы утверждали.

    он же зашил туда -8 в составе -8(%ebp). конечно он вычисляется, так как положение esp зарание не известно.

    #68
    >> По-моему, я это и утверждал (после приведения цитаты)…

    вы утверждали, что массив всегда преобразуется в указатель: "Но в любом другом месте b будет указателем на массив данных."

    #69
    >> Это работает, только если массив глобальный.

    если массив не глобальный, то все то же самое происходит в стеке, с той лишь разницей, что метку туда поставить нельзя и приходится выкручиваться без нее.

    #70

    а словами?

  9. 69
    Анатолий Лютин ответил:

    Кстати, ещё из удивительного и невероятного:
    int main()
    {
    int a[] = {3,2,1,0};
    char b[] = "BLA";
    char *c = "AAA";
    return a[0] + b[0] + c[0];
    }
    .file "a.c"
    .section .rodata
    .LC1:
    .string "AAA"
    .LC0:
    .string "BLA"
    .text
    .globl main
    .type main, @function
    main:
    leal 4(%esp), %ecx
    andl $-16, %esp
    pushl -4(%ecx)
    pushl %ebp
    movl %esp, %ebp
    pushl %ecx
    subl $36, %esp
    movl %gs:20, %eax
    movl %eax, -8(%ebp)
    xorl %eax, %eax
    movl $3, -32(%ebp)
    movl $2, -28(%ebp)
    movl $1, -24(%ebp)
    movl $0, -20(%ebp)
    movl .LC0, %eax
    movl %eax, -12(%ebp)
    movl $.LC1, -16(%ebp)
    movl -32(%ebp), %edx
    movzbl -12(%ebp), %eax
    movsbl %al,%eax
    addl %eax, %edx
    movl -16(%ebp), %eax
    movzbl (%eax), %eax
    movsbl %al,%eax
    leal (%edx,%eax), %eax
    movl -8(%ebp), %edx
    xorl %gs:20, %edx
    je .L3
    call __stack_chk_fail
    .L3:
    addl $36, %esp
    popl %ecx
    popl %ebp
    leal -4(%ecx), %esp
    ret
    .size main, .-main
    .ident "GCC: (GNU) 4.3.2 20081105 (ALT Linux 4.3.2-alt9)"
    .section .note.GNU-stack,"",@progbits

    Но если
    int main()
    {
    int a[] = {3,2,1,0};
    char b[] = "blablabla"; /!!!!!
    char *c = "AAA";
    return a[0] + b[0] + c[0];
    }

    То:
    .file "a.c"
    .section .rodata
    .LC0:
    .string "AAA"
    .text
    .globl main
    .type main, @function
    main:
    leal 4(%esp), %ecx
    andl $-16, %esp
    pushl -4(%ecx)
    pushl %ebp
    movl %esp, %ebp
    pushl %ecx
    subl $52, %esp
    movl %gs:20, %eax
    movl %eax, -8(%ebp)
    xorl %eax, %eax
    movl $3, -40(%ebp)
    movl $2, -36(%ebp)
    movl $1, -32(%ebp)
    movl $0, -28(%ebp)
    movl $1650551906, -18(%ebp)
    movl $1818386796, -14(%ebp)
    movw $97, -10(%ebp)
    movl $.LC0, -24(%ebp)
    movl -40(%ebp), %edx
    movzbl -18(%ebp), %eax
    movsbl %al,%eax
    addl %eax, %edx
    movl -24(%ebp), %eax
    movzbl (%eax), %eax
    movsbl %al,%eax
    leal (%edx,%eax), %eax
    movl -8(%ebp), %edx
    xorl %gs:20, %edx
    je .L3
    call __stack_chk_fail
    .L3:
    addl $52, %esp
    popl %ecx
    popl %ebp
    leal -4(%ecx), %esp
    ret
    .size main, .-main
    .ident "GCC: (GNU) 4.3.2 20081105 (ALT Linux 4.3.2-alt9)"
    .section .note.GNU-stack,"",@progbits

  10. 68
    Анатолий Лютин ответил:

    #63

    По-поводу глобальных данных, ок – я согласен, что с натяжкой метку можно считать адресом. В случае локальных данных, я не согласен с вами. Вы сказали:

    "а он не хранится. с ним компилятор поступает так, как ему покажется удобным (например, вшивает в код). с указателем, например, такие издевательства не возможны (разве что это будет константный указатель), поскольку от указателя требуется иметь кусок памяти, куда можно записать новое его значение.
    "

    То есть по вашему, в код должно было "вшито" что-то типа
    <——>movl<–> 0xffff, %eax

    В примере что я привёл, адрес, фактически, вычисляется, а не вшивается, как вы утверждали.

  11. 67
    Анатолий Лютин ответил:

    #65
    "ссылки на него нету, а ссылка получается в момент foo=b, тут транслятор соображает и приводит тип _какой_то_внутренний_тип_массив_чаров к указателю на чар"

    По-моему, я это и утверждал (после приведения цитаты)…

  12. 66
    Анатолий Лютин ответил:

    #66

    "зачем такие сложности? компилятор помнит, где у него лежит массив (мы уже видели, что в сегменте данных он просто поставил метку), а потому может просто использовать это знание для того, чтобы преобразовать массив к указателю (банально использовать значение ассемблерной метки, которая есть ни что иное, как просто адрес без места для хранения, в качестве значения массива). это "преобразование" не связано с вычислительными затратами на этапе исполнения."

    Это работает, только если массив глобальный.

  13. 65
    Леонид Максимов ответил:

    >> работает, возвращает тоже что основание массива.

    гг. что-то вроде защиты от дурака :)

    >>это штука внутренняя транслятора места в сегменте данных на которое проецируется массив

    не понимаю. транслятор места в сегменте данных? о_О

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

    зачем такие сложности? компилятор помнит, где у него лежит массив (мы уже видели, что в сегменте данных он просто поставил метку), а потому может просто использовать это знание для того, чтобы преобразовать массив к указателю (банально использовать значение ассемблерной метки, которая есть ни что иное, как просто адрес без места для хранения, в качестве значения массива). это "преобразование" не связано с вычислительными затратами на этапе исполнения.

  14. 64
    Нгамдкхе Кверос ответил:

    >кстати, никогда не пробовал брать адрес от массива. работает?

    работает,возвращает тоже что основание массива.

    >далось вам это преобразование типов…
    >char*foo;
    >char bar[1];
    >foo=bar;
    >в данном случае никаких преобразований типов не требуется.

    как это не требуется? просто b вообще говоря не сущетсвует, нету такой ячейки памяти где хранится этот указатель, это штука внутренняя транслятора места в сегменте данных на которое проецируется массив, т.е. ссылки на него нету, а ссылка получается в момент foo=b, тут транслятор соображает и приводит тип _какой_то_внутренний_тип_массив_чаров к указателю на чар. если бы преобразования не требовалось то существовала бы отдельная ячейка памяти в которой лежал бы указатель на чар.

  15. 63
    Леонид Максимов ответил:

    #50
    >> прикольно. в gcc b сразу проецируется на память, т.е. как отдельная переменная под неё память не выделяется. в смысле что b==&b, прикольно, гадать на кофеной гуще сложно, надо точно смотреть грамматику транслятора, но фокус прикольный.

    я об этом и говорю: указатель – это место для хранения адреса. массив – это просто адрес, без места (чаще всего).

    #56
    >> char *q,b[1],**w;
    >> w=&b;

    кстати, никогда не пробовал брать адрес от массива. работает? :)

    #58
    >> вобще это логично чтобы для типа статическй массив не выделялась отдельная ячейка памяти с указателем, а память получалась в момент преобразования типа если очень надо.

    далось вам это преобразование типов…

    char*foo;
    char bar[1];
    foo=bar;

    в данном случае никаких преобразований типов не требуется. значением foo является адрес элемента типа char. значением bar является адрес элемента типа char. какое нафиг преобразование типов?

  16. 62
    Леонид Максимов ответил:

    #48
    >> Мимо и не по адресу, мы тут говорим о локальных переменных (смотрите посты с кодом выше).

    так я сказал, что адрес считается либо от начала сегмента, либо от положения указателя на стек при входе в функцию, что отлично прослеживается в коде:

    <——>movl<–>-8(%ebp), %eax

    #53
    >> Ваше предположение и моя догадка, что в случае [глобальных данных] будет тупо заменено адресом не имеет оснований

    то есть как это не имеет? а это что?

    <——>movl<–>a+12, %eax

  17. 61
    Анатолий Лютин ответил:

    Понятно, то есть каждый разговаривает сам с собой.

    Я вот не понимаю, зачем тогда было вмешиваться в тред?

    P.S: Хорошо хоть, что вы какой-то код написали, а то мне пока одному замучало и свои мысли проверять и чужие.

  18. 60
    Нгамдкхе Кверос ответил:

    всё так как есть а уж как и откуда чего вы цитируете и как понимаете адрес это отдельная статья.

  19. 59
    Анатолий Лютин ответил:

    #57 Мдя? А судя по коду, который публиковался (#42), мы о локальных говорили…….

  20. 58
    Анатолий Лютин ответил:

    #58
    Собственно, всё так, как я цитировал с референс мануала, как я понимаю?

  21. 57
    Нгамдкхе Кверос ответил:

    в локальной, кстати, тоже самое получается, вобще это логично чтобы для типа статическй массив не выделялась отдельная ячейка памяти с указателем, а память получалась в момент преобразования типа если очень надо.

  22. 56
    Анатолий Лютин ответил:

    Ок, повторюсь. Локально или глобально? :) )))))

  23. 55
    Нгамдкхе Кверос ответил:

    а нарвался кстати в процессе попытки изменить значение переменно b дабы понять защищает он её от изменений или нет. чтоб оттранстиловалось сделал
    char *q,b[1],**w;
    w=&b;
    *w=NULL;

  24. 54
    Нгамдкхе Кверос ответил:

    локально неинтересно, локально всё будет в стеке и законы там иные. если вы способны удерживать нить разговора то мы тут о глобальных говорили.

  25. 53
    Нгамдкхе Кверос ответил:

    char b[]="qwert";
    printf("%d %d \n",b, &(b));

  26. 52
    Анатолий Лютин ответил:

    #50 имеется ввиду b[1]? Оно локально или глобально в тесте?

  27. 51
    Анатолий Лютин ответил:

    Ха!!! Ваше предположение и моя догадка, что в случае
    int a[]= {3,2,1,0};
    int main()
    {
    return a[3];
    }

    будет тупо заменено адресом не имеет оснований (хотя казалось бы :) ), вот ассемблер:

    <——>.file<->"a.c"
    .globl a
    <——>.data
    <——>.align 4
    <——>.type<->a, @object
    <——>.size<->a, 16
    a:
    <——>.long<->3
    <——>.long<->2
    <——>.long<->1
    <——>.long<->0
    <——>.text
    .globl main
    <——>.type<->main, @function
    main:
    <——>leal<–>4(%esp), %ecx
    <——>andl<–>$-16, %esp
    <——>pushl<->-4(%ecx)
    <——>pushl<->%ebp
    <——>movl<–>%esp, %ebp
    <——>pushl<->%ecx
    <——>movl<–>a+12, %eax
    <——>popl<–>%ecx
    <——>popl<–>%ebp
    <——>leal<–>-4(%ecx), %esp
    <——>ret
    <——>.size<->main, .-main
    <——>.ident<>"GCC: (GNU) 4.3.2 20081105 (ALT Linux 4.3.2-alt9)"
    <——>.section<——>.note.GNU-stack,"",@progbits

    Так что я теперь совсем не понимаю, что вы имеете ввиду под словом "адрес" :) ))

  28. 50
    Анатолий Лютин ответил:

    #45

    int a[]={3,2,1,0};
    int main(){
    return a[3];
    };

    Мимо и не по адресу, мы тут говорим о локальных переменных (смотрите посты с кодом выше). Для глобальных и ёжику понятно, что там скорее всего адрес будет вшит в код. (Это всё равно что заявить, что циклов не бывает, аргументируя это тем, что int a= 0;for(int i=0;i <3; i++) a+=i; будет заменено на mov a,3;

    Для
    int main(){
    int a[]={3,2,1,0};
    return a[3];
    };

    Ассемблерный код будет выглядеть предсказуемо:
    <——>.file<->"a.c"
    <——>.text
    .globl main
    <——>.type<->main, @function
    main:
    <——>leal<–>4(%esp), %ecx
    <——>andl<–>$-16, %esp
    <——>pushl<->-4(%ecx)
    <——>pushl<->%ebp
    <——>movl<–>%esp, %ebp
    <——>pushl<->%ecx
    <——>subl<–>$16, %esp
    <——>movl<–>$3, -20(%ebp)
    <——>movl<–>$2, -16(%ebp)
    <——>movl<–>$1, -12(%ebp)
    <——>movl<–>$0, -8(%ebp)
    <——>movl<–>-8(%ebp), %eax
    <——>addl<–>$16, %esp
    <——>popl<–>%ecx
    <——>popl<–>%ebp
    <——>leal<–>-4(%ecx), %esp
    <——>ret
    <——>.size<->main, .-main
    <——>.ident<>"GCC: (GNU) 4.3.2 20081105 (ALT Linux 4.3.2-alt9)"
    <——>.section<——>.note.GNU-stack,"",@progbits

    (Всё засунуто в регистр, но тупо адресом уже не заменено).

    P.S: Всё-таки, когда иллюстрируете свою мысль в программистком треде, компилятор под рукой иметь желательно.

  29. 49
    Анатолий Лютин ответил:

    #46
    Естественно, присваивание – это бинарная операция. Но вданном обсуждении моя оговорка не критична (мы же говорим не об арности, а о трактовке выражения в целом). Всё равно в C++ при _возможной_ перегрузке, значение выражения a=b; вне контекста (кода a и b), не определено.

  30. 48
    Нгамдкхе Кверос ответил:

    прикольно. в gcc b сразу проецируется на память, т.е. как отдельная переменная под неё память не выделяется. в смысле что b==&b, прикольно, гадать на кофеной гуще сложно, надо точно смотреть грамматику транслятора, но фокус прикольный.

  31. 47
    Анатолий Лютин ответил:

    #47

    Лучше кодом, пожалуйста, а то я не совсем понял, что вы хотите сказать :(

  32. 46
    Нгамдкхе Кверос ответил:

    есть типы на уровне трансляции которые потом приобразуются к конкретным типам, так статический массив явлеяется указателем на первый элемент массива, т.е. в процессе выполнения
    char *b и char b[1]; примерно одно и тоже, но во втором случае умный транслятор(т.е. при правильной грамматике) не пропустит операцию переинициализации b так как это статический массив во внутреннем представлении транслятора это константа и помещать в неё что-то нельзя.
    во втором случае b содержит указатель на область памяти, а сам является постоянной, вполне вероятно что транслятор может не только на этапе трансляции выдать ошибку но и защитить b от изменений в процессе выполнения программы…
    ща может быть даже проверю.

  33. 45
    Леонид Максимов ответил:

    >> Теперь понятно, что разговор об унарных операциях в контексте взаимоотношений двух объектов (коллекций объектов) лишён смысла?

    кстати, что вы понимаете под унарными операциями? что-то мне подсказывает, что вы можете захотеть оспорить утверждение о том, что присваивание – бинарная операция.

  34. 44
    Леонид Максимов ответил:

    >> Теперь понятно, что разговор об унарных операциях в контексте взаимоотношений двух объектов (коллекций объектов) лишён смысла?

    не понятно. я же так и написал – может выполняться оператор преобразования типа (чем он там занимается – это другой вопрос).

    >> Разочаровать меня не удалось.

    и не мог, поскольку условие, при котором я бы вас разочаровал, не было выполнено.

    >> Где этот "адрес" хранится тогда, по вашему мнению?
    >> И объясните мне ваше понятие адреса, которое оторвано от указателя. Я решительно не понимаю, что вы хотите этим сказать.

    а он не хранится. с ним компилятор поступает так, как ему покажется удобным (например, вшивает в код). с указателем, например, такие издевательства не возможны (разве что это будет константный указатель), поскольку от указателя требуется иметь кусок памяти, куда можно записать новое его значение.

    >> Покажите мне в ассемблерном коде где написано начало сегмента данных+смещение = b.

    компилятора под рукой нет; дайте ассемблерный код следующией программы:

    int a[]={3,2,1,0};
    int main(){
    return a[3];
    };

  35. 43
    Анатолий Лютин ответил:

    > присваивание в обоих случаях.

    Не совсем так. Безусловно присваивание, но только в случае языка C. В С++ это может означать всё что угодно, от сериализации, до неявного уничтожения объектов. По-русски говоря, всё что придёт программисту в голову. Именно это я и хотел сказать. Теперь понятно, что разговор об унарных операциях в контексте взаимоотношений двух объектов (коллекций объектов) лишён смысла?

    > в случае C++ может выполняться оператор преобразования типа, если хотя бы один из элементов является классом. догадываюсь, что вы пытаетесь намекнуть, что массив в C++ – это объект (экземпляр класса). если я прав, то мне придется вас разочаровать – массив объектом не является.

    Массив объектом, естественно, не является, он является типом, имеющим возможность содержать коллекции объектов. Разочаровать меня не удалось.

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

    Я не понимаю, что значит "является просто адресом"? Где этот "адрес" хранится тогда, по вашему мнению?

    > с чего бы ему не работать? строкой c=b; вы записываете адрес в кусок памяти, предназначенный для хранения адреса. а вот строка b=c; лишена всякого смысла – адрес, которым является массив, не имеет места хранения (точнее, он чаще всего оказывается зашит в код в виде смещения от начала сегмента или входного положения указателя стека).

    Не согласен, вы явно путаете понятия. Покажите мне в ассемблерном коде где написано начало сегмента данных+смещение = b. И объясните мне ваше понятие адреса, которое оторвано от указателя. Я решительно не понимаю, что вы хотите этим сказать.

  36. 42
    Леонид Максимов ответил:

    #41
    >> Что значит выражение в языке программирования С: a=b;
    >> Что значит выражение в языке программирования С++: a=b;

    присваивание в обоих случаях. в случае C++ может выполняться оператор преобразования типа, если хотя бы один из элементов является классом. догадываюсь, что вы пытаетесь намекнуть, что массив в C++ – это объект (экземпляр класса). если я прав, то мне придется вас разочаровать – массив объектом не является.

    #42
    >> программист сказал, что тут хочет видеть якобы массив и ему затем присваивает указатель, скажу-ка ему что так нехорошо

    скорее уж, "скажу ему, что я так не могу – мне некуда записать новый адрес".

    #42
    >> Вторая гипотеза: в левой части выражения не происходит унарное преобразование.

    вот-вот. в отличие от указателя, который является куском памяти для хранения адреса, массив является просто адресом. как вы понимаете, просто адрес не может обрасти ячейкой памяти для того, чтобы можно было принять новое значение.

    #42
    >> Что прекрасно работает и компилятор считает такое преобразование корректным.

    с чего бы ему не работать? строкой c=b; вы записываете адрес в кусок памяти, предназначенный для хранения адреса. а вот строка b=c; лишена всякого смысла – адрес, которым является массив, не имеет места хранения (точнее, он чаще всего оказывается зашит в код в виде смещения от начала сегмента или входного положения указателя стека).

  37. 41
    Анатолий Лютин ответил:

    #38

    Проверил.

    Считаю что тут играет большая роль проверка семантики компилятора, типа "программист сказал, что тут хочет видеть якобы массив и ему затем присваивает указатель, скажу-ка ему что так нехорошо". Вторая гипотеза: в левой части выражения не происходит унарное преобразование.

    В оправдание моей мысли (и цитаты) привожу код:

    #include <stdio.h>

    int main()
    {
    char b[] = "blablabla";
    char *c = "aaa";

    c = b;

    printf("b=%s",b);
    return 0;
    }

    Что прекрасно работает и компилятор считает такое преобразование корректным.

  38. 40
    Анатолий Лютин ответил:

    #39

    Лишено смысла потому, что унарные отношения между объектами определяются самими объектами, как вы должны знать. Поэтому в С++ есть объект типа массив, который вееееесьма отдалёно _может быть_ связан с объектом типа указатель.

    С++, естественно, включает в себя множество языка С, но все операции трактуются совсем по-другому. Вот вам задачка, которую любили раньше задавать людям на собеседовании:

    Что значит выражение в языке программирования С:

    a=b;

    Что значит выражение в языке программирования С++:

    a=b;

    Теперь подумайте, и вы поймете мой пост #37. Если нет, то я с удовольствием объясню.

  39. 39
    Анатолий Лютин ответил:

    #38

    Харбисон, ну так всего лишь первый человек, который сделал описание языка С, со всеми разномастями. Это было за несколько лет до того, как Ритчи и Ко, нашли время написать хоть какую-нибудь документацию по С. Если вы не читали эту книгу (является классикой, кстати), то очень советую.
    Пруф (второй автор):
    //en.wikipedia.org/wiki/Guy_L._Steele,_Jr.

    "хорошо, почему тогда не работает следующая строка?
    char b[]="blabla"; b=(char*)malloc(7);"

    Завтра проверю на месте и скажу точно. Не хочу быть голословным без компилятора.

    >> Ну да, вы развили мою мысль.

    не развил, а поправил. по вашим словам выходило, что компилятор участвует в загрузке бинарника, а рид-онли замаркирована сама строка, а не весь сегмент.

    Согласен, что выразился не совсем корректно. Своей фразой я хотел сказать то, что имел ввиду то что вы сказали, но получилось слишком упрощёно и нестрого.

  40. 38
    Леонид Максимов ответил:

    #34
    >> b – идентификатор массива типа char. Но в любом другом месте b будет указателем на массив данных.

    хорошо, почему тогда не работает следующая строка?
    char b[]="blabla"; b=(char*)malloc(7);

    #34
    >> Все прочие поправки с ссылками на значимую литературу, пожалуйста.

    а кто такой Харбисон, что вы за него так держитесь? давайте либо цитировать стандарт, либо понимать самостоятельно.

    #34
    >> Ну да, вы развили мою мысль.

    не развил, а поправил. по вашим словам выходило, что компилятор участвует в загрузке бинарника, а рид-онли замаркирована сама строка, а не весь сегмент.

    #35
    >> Неверно. Смотрите выше.

    не вопрос. смотри выше.

  41. 37
    Леонид Максимов ответил:

    #37
    >> Да, кстати, чтобы не было лишнего срача: мысль по-поводу указатель-массив, для C++, естественно, лишена смысла.

    омфг. это еще почему?
    вы еще скажите, что C++ не поддерживает макроопределения.

  42. 36
    Анатолий Лютин ответил:

    Да, кстати, чтобы не было лишнего срача: мысль по-поводу указатель-массив, для C++, естественно, лишена смысла.

  43. 35
    Анатолий Лютин ответил:

    #32

    "#31 он сказал дословно: "b – опять же указатель, который трактуется как указатель на массив данных" на самом деле b это указатель на первый _элемент_ массива данных, а не на массив."

    Ок, чтобы не быть голословным обратимся к фактам (опять же, правки приветствуются):

    С.П. Харбисон, Г.Л. Стил Язык программирования С пятое издание, страница 157, 5.4.1 Массивы и указатели.
    Цитата:
    В языке С существует тесная взаимосвязь между типами "массив типа Т" и "указатель на Т". Когда в выражении встречается идентификатор массива, то тип идентификатора преобразуется из "массив типа Т" в "указатель на Т", значение же идентификатора конвертируется в указатель на первый элемент массива. Это одно из правил обычных унарных преобразований.
    Конец цитаты.

    То есть в строчке char b[]="blablabla", b – идентификатор массива типа char. Но в любом другом месте b будет указателем на массив данных. Соответственно, я не вижу тут ничего не правильного из того, что сказал выше. Все прочие поправки с ссылками на значимую литературу, пожалуйста.

    #29
    1. >> компилятор вообще ничего не может защитить, он может только у ОС попросить что-то и ОС может ему в этом отказать.

    компилятор не может ни о чем просить ос – все свои пожелания он записывает в бинарный образ. загрузчик этот образ читает и может следовать (а может и не следовать, в общем случае) пожеланиям компилятора.

    2. >> Он выделит 4 байта (в 32 разрядниках) для указателя b и поместит туда адрес строки из сегмента данных, которая замаркирована рид-онли.

    "которая замаркирована" -> "который может быть замаркирован". для маленьких программ вряд ли кто даст два сегмента данных (под изменяемые данные и под константы), хотя можно и намекнуть, что нам этого хотелось бы.

    Ну да, вы развили мою мысль.

  44. 34
    Анатолий Лютин ответил:

    #33
    b – это ввобще не указатель, а просто адрес.

    Неверно. Смотрите выше.

  45. 33
    Анатолий Лютин ответил:

    # 32

    Не только в linux, но и в windows тоже, проверьте, пожалуйста, кодом

  46. 32
    Леонид Максимов ответил:

    b – это ввобще не указатель, а просто адрес.

  47. 31
    Нгамдкхе Кверос ответил:

    > не факт. переменная может располагаться в стеке или даже в регистре.

    начиналось всё с глобальных переменных… ну гипотетически конечно может транслятор и так быть устроен. вобщем противоречий тут нет, но в линуксе действительно константа оказалась в защищённой от изменений памяти.

    теоретически счётчик цикла транслятор может додуматься и провести оптимизацию расположив его в регистре увидев что внутри цикла не используются вищи вроде &i вообще не выделяя памяти, а вот с глобальной переменной…

    #31 он сказал дословно: "b – опять же указатель, который трактуется как указатель на массив данных" на самом деле b это указатель на первый _элемент_ массива данных, а не на массив.

  48. 30
    Леонид Максимов ответил:

    >> а переменная окажется в сегменте данных (который останется естественно без защиты от изменения) проинициализируется указателем на константу.

    не факт. переменная может располагаться в стеке или даже в регистре.

    >> указатель трактуется как указатель, а массив элементов это указатель на первый элемент плюс размер элемента * номер элемента в массиве.

    не понимаю. давайте так: указатель – это место (важно!), где хранится адрес. массив – это адрес первого элемента массива, но не место.

  49. 29
    Нгамдкхе Кверос ответил:

    #29 в линуксе под гсс действительно прога вылетает значит она константы в отдельный сегмент защищённый на изменений качает.

    #28
    просит ос не компилятор(к тому времени как файл будет запущет компилер уже закончит работу, а может вообще отсутствовать на машине где файл будет исполняться)информация о защите диктуется форматом исполняемого файла, так что это задача обоюдня поддержка транслятора ос и формата исполняемого файла, говоря что транслятор не может защищать переменную я имел ввиду что он не может создать программу с переменной в сегменте который собирается сделать защищённым.

    "Он выделит 4 байта (в 32 разрядниках) для указателя b и поместит туда адрес строки из сегмента данных, которая замаркирована рид-онли."

    именно, эта строка есть константа что приходит на вход транслятору из синтаксического анализатора. поэтому он выделит место для этой константы в сегменте данных (как выяснилось в отдельном который затем сделает защищённым для модификации), а переменная окажется в сегменте данных (который останется естественно без защиты от изменения) проинициализируется указателем на константу.

    "b=(char*)malloc(15); – этим ты просишь выделить новый участок память объёмом 15 байт и положить его адрес в переменную b. Эта память со строкой "blablabla" может быть связана только случайно."

    если бы случайно эта память могла быть связана со строкой то у нас бы закономерно взрывались атомные станции.

    "b – опять же указатель, который трактуется как указатель на массив данных, а раз так, то выделится память в стеке и проинициализируется этой строкой, с исходной строкой "blablabla" он никак не будет связан"

    указатель трактуется как указатель, а массив элементов это указатель на первый элемент плюс размер элемента * номер элемента в массиве.
    потому если вдруг с жёсткого бодуна наш несчастненький транслятор решит что b это указатель на массив то элементом b[0] будет указатель на элемент данных а не данные. и также теперь мы договорились до того что b перекочевала в сегмент стека…

  50. 28
    Леонид Максимов ответил:

    все совсем так.

    1. >> компилятор вообще ничего не может защитить, он может только у ОС попросить что-то и ОС может ему в этом отказать.

    компилятор не может ни о чем просить ос – все свои пожелания он записывает в бинарный образ. загрузчик этот образ читает и может следовать (а может и не следовать, в общем случае) пожеланиям компилятора.

    2. >> Он выделит 4 байта (в 32 разрядниках) для указателя b и поместит туда адрес строки из сегмента данных, которая замаркирована рид-онли.

    "которая замаркирована" -> "который может быть замаркирован". для маленьких программ вряд ли кто даст два сегмента данных (под изменяемые данные и под константы), хотя можно и намекнуть, что нам этого хотелось бы.

    4. >> b – опять же указатель, который трактуется как указатель на массив данных, а раз так, то выделится память в стеке и проинициализируется этой строкой, с исходной строкой "blablabla" он никак не будет связан.

    неверно. b будет считаться массивом (где именно он находится – тоже вопрос, если честно), так как вы не сможете (в общем случае) заставить его ссылаться куда-то еще.

  51. 27
    Анатолий Лютин ответил:

    По-моему всё не совсем так.
    Во-первых,
    "в том-то и дело что защищать память с переменной компилятор не может, защищать он может только сегмент кода и сегмент данных которые константы."

    компилятор вообще ничего не может защитить, он может только у ОС попросить что-то и ОС может ему в этом отказать.

    Во-вторых,
    "он встретил переменная b типа (char*) присвоить значение, дальше транслятор на входе от синтаксического анализатора будет иметь константу: строку, а ждёт указатель на чар, потому он выделит под константу память(опционально может и в отдельном сегменте данных для констант) получит на неё указатель и присвоит b."

    Он выделит 4 байта (в 32 разрядниках) для указателя b и поместит туда адрес строки из сегмента данных, которая замаркирована рид-онли.

    В-третьих,

    "char *b="blablabla";
    b=(char*)malloc(15);
    b[0]='a';
    "

    b=(char*)malloc(15); – этим ты просишь выделить новый участок память объёмом 15 байт и положить его адрес в переменную b. Эта память со строкой "blablabla" может быть связана только случайно.

    "как видите b ничем не защищена, защищена память хранящая нашу блаблабла."
    С этим кто-то разве спорил? По-моему, это очевидно.

    в-четвёртых,
    "char b[]="blablabla"
    транслятор получит переменная типа массив => нужно выделить память под массив, на входе строка, потому он инициализирует массив этой строкой"

    b – опять же указатель, который трактуется как указатель на массив данных, а раз так, то выделится память в стеке и проинициализируется этой строкой, с исходной строкой "blablabla" он никак не будет связан.

  52. 26
    Нгамдкхе Кверос ответил:

    в том-то и дело что защищать память с переменной компилятор не может, защищать он может только сегмент кода и сегмент данных которые константы.
    в данном случае
    char *b="blablabla";
    транслятор сделал что? он встретил переменная b типа (char*) присвоить значение, дальше транслятор на входе от синтаксического анализатора будет иметь константу: строку, а ждёт указатель на чар, потому он выделит под константу память(опционально может и в отдельном сегменте данных для констант) получит на неё указатель и присвоит b.

    char *b="blablabla";
    b=(char*)malloc(15);
    b[0]='a';

    как видите b ничем не защищена, защищена память хранящая нашу блаблабла.
    в случае же с
    char b[]="blablabla"
    транслятор получит переменная типа массив => нужно выделить память под массив, на входе строка, потому он инициализирует массив этой строкой.

  53. 25
    Анатолий Лютин ответил:

    В том-то и фишка, что по адресу не константа хранится, идея в том, что поскольку компилятор произвёл склеивание строковых литералов, то он очень-очень сильно попросил ОС сделать память, где хранится blablabla, рид-онли. Если бы компилятор не производил склеивание, то b[0] = 'a' было бы разрешено.

    Сорри за занудство.

  54. 24
    Нгамдкхе Кверос ответил:

    переменная b и не строго не является константой, она и называется переменная а не константа. просто это указатель на чар, который инициализируется указателем на константу, константой как-раз является "blablabla" потому само присваивание ничему не противоречит, а вот через указатель на ячейку где хранится константа делается попытка изменения потому b[0] присваивать вообще говоря нельзя.

  55. 23
    Леонид Максимов ответил:

    >> Строго говоря в данном примере переменная b не являлась константой в контексте языка C.

    еще бы. ей же можно присвоить значение, что и проделывает строка char* b="blablabla";

    последующий b="albalbalb"; тоже не вызовет сегфолта.

  56. 22
    Анатолий Лютин ответил:

    что и т.д.

  57. 21
    Анатолий Лютин ответил:

    #18

    Строго говоря в данном примере переменная b не являлась константой в контексте языка C.

  58. 20
    Тимур Магомедов ответил:

    >и где в ричи написано что >неинициализированная глобальная переменная >должна инициализироваться нулём?

    В моём издании (Вильямс, 2006) – на 98-й странице.

  59. 19
    Анатолий Лютин ответил:

    Всё верно, это я и хотел сказать, притом ещё ОС маркирует эту память как рид-онли. Фишка в том, что можно напороться на какую-нибудь ОС без такой защиты (древние версии линукс) и не получить сегфолта. Это была иллюстрация спора о том, что, если выкинуть стандарт и целевые платформы, то тогда вообще возможны любые ситуации, а не только неинициализированные глобальные переменные.

  60. 18
    Нгамдкхе Кверос ответил:

    блин, я строки кода по отдельности смотрел. а попытки менять константу конечно не есть хорошо.

  61. 17
    Сергей Старовой ответил:

    О чем спор собсна? Дос работала в реальном режиме, там не могло быть никаких сегфолтов).
    Умные современные компиляторы кладут константные литералы в read-only память, это позволяет не дублировать одинаковые значения и защитить константы от случайного изменения…

  62. 16
    Леонид Максимов ответил:

    я тоже не вижу ничего плохого в приведенном куске кода.

    ну, разве что страница сегмента данных будет защищена от изменений, тогда могут и по голове надавать, но в таком развитии событий я не уверен.

  63. 15
    Анатолий Лютин ответил:

    Ок. Код в студию. У меня на Alt Linux + gcc, Windows Vista + MSVS получается закономерный сегфол. Собственно это не от компилятора зависит, а от ОС. Предполагаю, что ты пробуешь что-то вроде:

    char b[] = "blablabla";

  64. 14
    Нгамдкхе Кверос ответил:

    выньдос билдер, асп линукс 7.1 gcc(кстати и с педантиком и волл-ом)

  65. 13
    Анатолий Лютин ответил:

    Нгамдкхе Кверос

    Ну так попробуй и узнаешь :) ))

  66. 12
    Анатолий Лютин ответил:

    Только потом о результатах и о ОС сообщите, чтобы можно было подрастающему поколению объяснить почему в одних ОС так можно, а в других нельзя.

  67. 11
    Нгамдкхе Кверос ответил:

    вроде как не должно бы, ибо сказано в писании "перед использованием явно инициализируй переменную".

    char *b = "blablabla"; // очень правильная строка
    b[0] = 'a'; // а тут что предполагалось неверным?

  68. 10
    Анатолий Лютин ответил:

    Поправьте меня, если я не прав, но в стандарте это прописано. Про Ричи я имел ввиду то, что там написано откуда какие переменные память берут, а это ключ к понимаю того, какие значение ожидать.

    Во времена доса и билдера каждый делал так как хотел, наплевав на стандарт (который, кстати, только в 89 появился). Чего только стоили программы с char *b = "blablabla"; b[0] = 'a'; Кстати, в некторых версиях ДОСА, такое допускалось ;)

  69. 9
    Нгамдкхе Кверос ответил:

    Анатолий Лютин "переменная x, которая, естественно, проинициализируется 0"

    и где в ричи написано что неинициализированная глобальная переменная должна инициализироваться нулём?
    в досе и билдере совершенно точно бывали прецеденты когда люди закладывались на нули, а в один прекрасный момент после сборки туда начинало попадать что-то другое.(именно глобальная переменная которая не в стеке а сегменте данных лежит)

  70. 8
    Илья Тимофеев ответил:

    Да с константами это я лажанул :) Давно с этими языками дело не имел
    Спасибо большое за подробный ответ!

  71. 7
    Анатолий Лютин ответил:

    Для тех кто в танке и даже не пытался открыть википедию, Керригана и Ричи или Страуструпа объясняю. И там и там x будет помещён при компиляции в тело программы (фактически при компиляции в конец исполняемого кода будет добавлена переменная x, которая, естественно, проинициализируется 0), смотрится это любым hex-редактором. Память на y же будет выделена только во время выполнения программы и, соответственно, проинициализируется тем, что случайно окажется в памяти из стека.

  72. 6
    Анатолий Лютин ответил:

    Да и ещё, про второй вопрос. Это бред, если вы их объявите константами, то вы обязаны будете им присвоить значения… Быстро обратно в школу учиться!

  73. 5
    Анатолий Лютин ответил:

    Не проверял, но попробую угадать: и там и там x проинициализируется 0, а y – случайным числом. Сейчас проверю.

  74. 4
    Анатолий Лютин ответил:

    Проверил:
    [vostok@localhost ~/tmp]$ cat > a.cpp
    #include <iostream>

    using namespace std;

    int x;

    int main()
    {
    int y;

    cout << "x=" << x << " y = " << y << endl;
    return 0;
    }
    [vostok@localhost ~/tmp]$ g++ a.cpp -o a.exe
    [vostok@localhost ~/tmp]$ ./a.exe
    x=0 y = 134514633
    [vostok@localhost ~/tmp]$ cat > b.c
    #include <stdio.h>

    int x;

    int main()
    {
    int y;

    printf("x=%d:y = %d ", x, y );

    return 0;
    }
    [vostok@localhost ~/tmp]$ gcc b.c -o b.exe
    [vostok@localhost ~/tmp]$ ./b.exe
    x=0:y = -1208394544

  75. 3
    Илья Тимофеев ответил:

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

  76. 2
    Александр Васюченко ответил:

    ну да, main должен возвращать int на сколько я помню, это же код возврата для ОС

  77. 1
    Иван Горбачев ответил:

    master@computer1 ~/temp/12 $ cat main.cpp
    int x;
    void main() {
    int y;
    }
    master@computer1 ~/temp/12 $ g++ main.cpp -omain
    main.cpp:2: ошибка: ‘::main’ должна возвращать ‘int’

    C++ не компилит данный код :) ))))

Клуб программистов работает уже ой-ой-ой сколько, а если поточнее, то с 2007 года.