5

No entiendo por qué este código falla en tiempo de ejecución, compila sin errores y por mucho que reviso el código no veo por qué debería saltar un segmentation fault:

#include <iostream>

constexpr std::size_t BUFF_SIZE { 256u };

int buffer[BUFF_SIZE] {};
int *_begin = buffer;
int *_end   = buffer + (BUFF_SIZE / 2u);

int main()
{
    for (unsigned indice{}; indice < (BUFF_SIZE / 2u); ++indice) {
        *(_begin + indice) = indice;
        *(_end + indice)   = (BUFF_SIZE / 2u) - indice;
    }

    for (const auto &v : buffer) std::cout << v << '\n';

    return 0;
}

Puede verse el código funcionando [aquí].

Neoniet
  • 1,909
  • 9
  • 24
PaperBirdMaster
  • 44,474
  • 6
  • 44
  • 82

1 Answers1

6

Problema.

El problema es que _begin y _end no son nombres aconsejables para utilizar en variables.

Los nombres que empiezan con guión bajo (_), pese a no ser palabras clave del lenguaje C++ están reservados para ser usados por los compiladores, así que usarlos puede interferir con su funcionamiento.

¿Qué ha pasado?.

Por ejemplo, el compilador gcc (usado en el código que falla) declara los símbolos _etext, _edata y _end (la traducción y es mía):

DESCRIPCIÓN
       La dirección de estos símbolos indica el final de varios segmentos
       del programa:

       etext  Esta es la primera dirección tras el final del segmento de
              texto (el código del programa).

       edata  Esta es la primera dirección pasado el final del segmento
              datos inicializados.

       end    Esta es la primera dirección pasado el final del segmento
              de datos no inicializados (también conocido como segmento
              BSS).

NOTAS
       En algunos sistemas los nombres de estos símbolos son precedidos
       por guiones bajos, esto es: _etext, _edata y _end. Estos símbolos
       también son definidos por programas compilados en Linux.

Seguramente el símbolo _end del compilador ha ocultado la variable _end y se ha intentado modificar una dirección de memoria a la que no deberíamos tener acceso, dando lugar al segmentation fault.

Detalles técnicos.

El estándar de C++ indica que los compiladores pueden usar variables que empiecen por guión bajo (_) a su conveniencia:

En la sección §2.10 (la traducción y el resaltado son míos):

2.10 Identificadores

...

  1. Adicionalmente, algunos identificadores están reservados para las implementaciones de C++ y no deben ser usados para otros fines; no se requiere ningún diagnóstico.

    3.1. — Cada identificador que contenga un doble guión bajo __ o empiece por un guión bajo seguido de una letra en mayúscula está reservado para cualquier uso que requiera la implementación.

    3.2. — Cada identificador que empiece por un guión bajo está reservado para ser usado por la implementación como nombre en el espacio de nombres global.

El propio estándar indica que no se requiere ningún diagnóstico en caso de que un identificador considerado como reservado sea usado en el código. Esto significa que a diferencia de las palabras clave que emiten un error de compilación si son usadas como identificadores los identificadores reservados no emiten error ni alarma de compilador al ser usados en el código.

Solución.

No uses el guión bajo (_) para nombrar variables o macros:

#include <iostream>

constexpr std::size_t BUFF_SIZE { 256u };

int buffer[BUFF_SIZE] {};
int *begin = buffer;
int *end   = buffer + (BUFF_SIZE / 2u);

int main()
{
    for (unsigned indice{}; indice < (BUFF_SIZE / 2u); ++indice) {
        *(begin + indice) = indice;
        *(end + indice)   = (BUFF_SIZE / 2u) - indice;
    }

    for (const auto &v : buffer) std::cout << v << '\n';

    return 0;
}

El código anterior imprime:

0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
127
126
125
124
123
122
121
120
119
118
117
116
115
114
113
112
111
110
109
108
107
106
105
104
103
102
101
100
99
98
97
96
95
94
93
92
91
90
89
88
87
86
85
84
83
82
81
80
79
78
77
76
75
74
73
72
71
70
69
68
67
66
65
64
63
62
61
60
59
58
57
56
55
54
53
52
51
50
49
48
47
46
45
44
43
42
41
40
39
38
37
36
35
34
33
32
31
30
29
28
27
26
25
24
23
22
21
20
19
18
17
16
15
14
13
12
11
10
9
8
7
6
5
4
3
2
1
Kiko_L
  • 6,455
  • 1
  • 11
  • 25
PaperBirdMaster
  • 44,474
  • 6
  • 44
  • 82
  • 1
    Yo consideraría: 3.2. — Cada identificador que empiece con un guión bajo queda reservado para la implementación, para ser usado como un nombre en el espacio de nombres global. –  Sep 21 '16 at 16:06
  • 1
    @asdasdasd correcto. Hice mal la traducción... *falta de cafeína*. – PaperBirdMaster Sep 21 '16 at 16:15