Конвенции за стил на кода

Coding Style Conventions

Какво са "Конвенции за стил на кода"? "Грозен" и "красив" код. Примерен стил.
Автор: Александър Георгиев

Тази тема е една от няколкото, които не покриват материал, който можете да срещнете в задачи (тоест алгоритъм или структура данни). Все пак според нас тя ще ви помогне много за всяка задача, която пишете, както и в по-дългосрочен план.

По-дълъг код отнема по-малко време за писане?

Нещо, което почти никога не бива преподавано на начинаещи програмисти (и, според нас, e голяма грешка) е това, как да форматираме кода си, така че да избягваме някои чести имплементационни грешки, като същевременно го направим много по-лесен за четене и разбиране.

Ако все още не сте, то със сигурност скоро ще се сблъскате с тъпа грешка, която сте допуснали при имплементацията, и в последствие ви е коствала над десет минути за дебъгване. Чрез няколко прости правила при писането на кода на програмата, в тази тема ще ви научим как да намалите шанса за част от тези грешки. Дори дребни неща като това къде слагате къдравите скоби, как именувате променливите си или това дали подравнявате кода си, могат много да помогнат за неговото дебъгване и като цяло изобщо да не се стига до него.

?Много от модерните редактори идват с вграден "auto completion" -- тоест автоматично завършване на имена на променливи, функции, и т.н. Това значително помага за бързината на писане на код с дълги имена. Свиквайки да го ползвате, вие ще можете да пишете също толкова бързо кода си, както бихте го правили ако ползвахте еднобуквени имена.
Интересното е, че това не ви коства почти нищо. Хубавият стил на писане изисква пренебрежимо малко допълнително време, както и почти никакво мислене (веднъж свикнали с него). Дори да ви се струва, че отделяте по няколко секунди повече на ред, за да напишете по-дълга променлива, да сложите къдрави скоби, където не е задължително да има, или да подравните тялото на цикъл с една табулация надясно, всъщност така спестявате много повече по-късно, ако се наложи да дебъгвате.

Конвенции за стил на кода

Какво всъщност представляват "Конвенциите за стил на кода"? Накратко - това е как форматираме програмата си. Няколко от основните неща са:
  • Как подравняваме кода
  • Къде и кога слагаме къдрави скоби
  • Как да се справяме с константите из кода
  • Как кръщаваме променливите, функциите и т.н.
  • Как разграничаваме променливи от константи, класове от функции и т.н.

"Красив" и "грозен" код

Всъщност, може би най-лесният начин за демонстрация на това е, както обикновено, чрез пример. За целта ще покажем три кода от истинско състезание на една и съща задача. Забележете, че времената на второто и третото решение са значително по-ниски, въпреки ползването на (относително) добър стил.

Решение на Swetko: 15 минути и 15 секунди (C++)
#include<vector>
#include<string>
#include<algorithm>
using
namespace
std
;
#define VI vector < int >
#define VS vector < string >
#define pb push_back
#define cs c_str()
#define sz size()
#define ALL(a) (a).begin(),(a).end()
///////////////////////////////////////////
char
s[
21
]
=
"abkdeghilmnzoprstuwy"
;
class
TagalogDictionary {
public
:
vector
<
string
>
sortWords(
vector
<
string
>
words) {
int
q1
,
q2
,
q3
,
c1
,
c2
,
c3
;
vector
<
VI
>
w
;
VI v(
128
)
;
for
(q1
=
0
;
q1
<
20
;
q1
+
+
)v[s[q1]]
=
q1
+
1
;
for
(q1
=
0
;
q1
<
words.sz
;
q1
+
+
) {
string
e1
=
words[q1]
;
VI e2
;
for
(q2
=
0
;
q2
<
e1.sz
;
q2
+
+
)
if
(q2
<
e1.sz
-
1
&
&
e1[q2]
=
=
'n'
&
&
e1[q2
+
1
]
=
=
'g'
) {e2.pb(v[
'z'
])
;
q2
+
+
;
}
else
e2.pb(v[e1[q2]])
;
w.pb(e2)
;
} sort(ALL(w))
;
VS ans
;
for
(q1
=
0
;
q1
<
w.sz
;
q1
+
+
) {
string
e1
;
for
(q2
=
0
;
q2
<
w[q1].sz
;
q2
+
+
)
if
(s[w[q1][q2]
-
1
]
=
=
'z'
) e1
=
e1
+
"ng"
;
else
e1.pb(s[w[q1][q2]
-
1
])
;
ans.pb(e1)
;
}
return
ans
;
}}
;

Решение на dskloet: 8 минути и 31 секунди (Java)
import java.util.
*
;
import java.math.
*
;
public
class
TagalogDictionary { String[] abc
=
{
"a"
,
"b"
,
"k"
,
"d"
,
"e"
,
"g"
,
"h"
,
"i"
,
"l"
,
"m"
,
"n"
,
"ng"
,
"o"
,
"p"
,
"r"
,
"s"
,
"t"
,
"u"
,
"w"
,
"y"
}
;
public
String[] sortWords (String[] words) { Arrays.sort(words
,
new
Comp())
;
return
words
;
}
class
Comp implements Comparator
<
String
>
{
public
int
compare(String s1
,
String s2) { String[] t1
=
tokens(s1)
;
String[] t2
=
tokens(s2)
;
for
(
int
i
=
0
;
i
<
t1.length
&
&
i
<
t2.length
;
i
+
+
) {
int
cmp
=
index(t1[i])
-
index(t2[i])
;
if
(cmp
!
=
0
)
return
cmp
;
}
return
t1.length
-
t2.length
;
} } String[] tokens(String s) { ArrayList
<
String
>
result
=
new
ArrayList
<
String
>
()
;
for
(
int
i
=
0
;
i
<
s.length()
;
i
+
+
) {
char
c
=
s.charAt(i)
;
if
(c
!
=
'n'
) { result.add(
""
+
c)
;
continue
;
}
if
(i
=
=
s.length()
-
1
|
|
s.charAt(i
+
1
)
!
=
'g'
) { result.add(
""
+
c)
;
continue
;
} result.add(
"ng"
)
;
i
+
+
;
} String[] r
=
new
String[result.size()]
;
return
result.toArray(r)
;
}
int
index(String s) {
int
i
=
0
;
for
(String l
:
abc) {
if
(l.equals(s))
return
i
;
i
+
+
;
}
return
i
;
} }

Решение на Petr: 7 минути и 6 секунди (C#)
using
System
;
using
System.Collections.Generic
;
public
class
TagalogDictionary {
class
Word
:
IComparable
<
Word
>
{
public
string
a
;
string
b
;
public
Word(
string
a
,
string
b) { this.a
=
a
;
this.b
=
b
;
}
public
int
CompareTo(Word other) {
return
string.CompareOrdinal(b
,
other.b)
;
} }
public
string
[] sortWords(
string
[] words) { Dictionary
<
char
,
char
>
map
=
new
Dictionary
<
char
,
char
>
()
;
map
[
'a'
]
=
'A'
;
map
[
'b'
]
=
'B'
;
map
[
'k'
]
=
'C'
;
map
[
'd'
]
=
'D'
;
map
[
'e'
]
=
'E'
;
map
[
'g'
]
=
'F'
;
map
[
'h'
]
=
'G'
;
map
[
'i'
]
=
'H'
;
map
[
'l'
]
=
'I'
;
map
[
'm'
]
=
'J'
;
map
[
'n'
]
=
'K'
;
map
[
'o'
]
=
'M'
;
map
[
'p'
]
=
'N'
;
map
[
'r'
]
=
'O'
;
map
[
's'
]
=
'P'
;
map
[
't'
]
=
'Q'
;
map
[
'u'
]
=
'R'
;
map
[
'w'
]
=
'S'
;
map
[
'y'
]
=
'T'
;
List
<
Word
>
l
=
new
List
<
Word
>
()
;
foreach (
string
x in words) {
string
y
=
""
;
for
(
int
i
=
0
;
i
<
x.Length
;
+
+
i)
if
(i
<
x.Length
-
1
&
&
x[i]
=
=
'n'
&
&
x[i
+
1
]
=
=
'g'
) {
+
+
i
;
y
+
=
'L'
;
}
else
y
+
=
map
[x[i]]
;
l.Add(
new
Word(x
,
y))
;
} l.Sort()
;
Word[] newWords
=
l.ToArray()
;
return
Array.ConvertAll
<
Word
,
string
>
(newWords
,
delegate(Word w) {
return
w.a
;
})
;
} }

Примерен стил

Ако погледнете ваш код от преди година, най-вероятно ще го намерите далеч по-различен от скорошен такъв. Това е много типично, особено в началото на програмистската ви кариера. С времето адаптирате по-хубави практики, като кодът ви става по-гъвкав и лесен за четене. Това е един вид "еволюция" на стила ви за писане. След време се доближавате все повече до някои от отвърдените добри практики, като все по-малко се променя с течение на времето.

Ако искате да "пропуснете" този период на еволюция (който отнема обикновено 3-5 години), тук ще ви предложим примерен стил, който можете да следвате. Макар и някои от нещата да ви се струват ненужни или глупави, с времето ще се убедите, че от тях има смисъл. Що-годе към този стил ще гледаме да се придържаме и в кода, който прилагаме към темите на сайта.

Забележете, че това да пишете хубав код не е изискване да сте добър състезател. Някои от най-добрите състезатели в света пишат ужасен код по състезания. Примерно Светко (Светослав Колев), чиито код дадохме по-горе за пример, е един от най-добрите състезатели, които България е имала (три медала от IOI). Искаме да ви научим на хубав стил, защото това няма да ви навреди, а в някои случаи ще ви помогне.

CamelCase

За имена на променливи, функции, класове и т.н. ще ползваме така наречения CamelCase. При него началото на всяка (евентуално с изключение на първата) от думите, от които се състои името, е с главна буква, а останалите букви са малки. Например numGolfBalls или RedBlackTree. Тъй като главните букви в имената приличат на гърбици на камила, оттам и името на стила.

Защо?
Хубаво е да можем лесно да разграничаваме различните думи в едно име. Например numgolfballs е по-трудно за осмисляне от numGolfBalls. Нещо повече, понякога може да се получат двусмислици - например ihaveagirlfriendwhoishot може да е както i_have_a_girlfriend_who_is_hot, така и i_have_a_girlfriend_who_i_shot.
?Понякога в професионален код се позволява ползването и на двата стила - но единият се ползва за един тип променливи и методи, докато другия - за друг. Реално се използва за още по-лесно осмисляне на функцията на променливите. В състезания рядко се пишат толкова сложни програми, затова не го правете.

Впрочем, това е другата най-разпространена алтернатива на CamelCase - separate_by_underscores (различните думи се разграничават с подчертавка). Ползвайте или единия тип, или другия, но не и двата едновременно! Смесването им води до неконсистентен и донякъде грозен код.

Значещи имена

Ползвайте значещи имена за променливите, и особено за функциите, които пишете. Използвайте по възможност цели думи (или достатъчно ясни съкращения на цели думи). В по-кратки програми можете да ползвате и еднобуквени имена за променливи, стига буквата да е добре избрана.

Защо?
В сравнително малка програма (до 30-50 реда) може и да успеете да помните всеки масив за какво е, но в по-голяма ще ви се налага да проверявате по няколко пъти "кое какво беше".

Например нека имате задача за баскетбол и ви трябват два масива - един за пазене на информация за топки (balls), и един за кошове (baskets). Далеч по-добре е те да се казват balls[] и baskets[], отколкото, например, b1 и b2 или b и bb. Ако не искате да хабите много време за писане на дълги имена, поне ползвайте смислени (логични) еднобуквени такива. Ако, например, имате три вида топки - червени, сини и зелени - ползвайте r, b и g (от "red", "blue" и "green"), вместо, примерно, a, b, и c.

Език

Ползвайте имена на един единствен език, за предпочитане английски.

Защо?
Макар и да сме Българи и да са ни учили да пишем на Български език, само един на хиляда хора по света говори нашия език. В кода най-често ще ви се налага да пишете на (неофициално) приетия за интернационален език - Английски.
?В някои състезания като TopCoder входът директно ви се подава в променливи - които най-често са думи на Английски език. За да запазите кода си едноезичен, или ще се налага да прекръстите тези променливи, или да пишете собствения си код също на Английски.
Принципно за млади състезатели не е проблем да се пише и на "шльокавица" - примерно broiGolfTopki или ChervenoChernoDyrvo. Но когато правите своя избор, правете го консистентен - или всички променливи да са на Английски, или всички на шльокавица.

Имена на променливи и функции

Имената на променливите и функциите започват с малка буква; всяка следваща дума в името започва с главна буква с останали малки. Примерно numGolfBalls.

Защо?
Хубаво е да има начин да разграничаваме имена на променливи от имена на класове и структури. С въведената конвенция, това става много лесно.

Имена на класове и структури

Имената на класовете и структурите започват с главна буква; всяка следваща дума в името започва с главна буква с останали малки. Примерно RedBlackTree.

Защо?
Отново - за лесно разграничаване между {променлива, функция} и {структура, клас}.

Имена на константи

Имената на константи са с изцяло главни букви; различни думи в едно име са разделени с подчертавки. Примерно MAX_NODES или MIN_SIDE_LEN. За предпочитане е да са изведени в началото на сорса.

Защо?
Константите са обикновено нещо, което можем да ползваме за размер на масиви или ограничение на цикъл. Изкарвайки ги най-отгоре, лесно можем да видим или променим тяхната стойност (тя се дефинира само веднъж -- защо не там?).

Блуждаещи константи

Избягвайте ползването на числови константи из самия код - изведете ги като именувани такива в началото на файла. Например вместо да умножавате по 3.1415926535, направете константа const double PI = 3.1415926535; и в кода умножавайте по PI.

Защо?
Докато константи като числото Пи може да са очевидни, други със сигурност няма да са, което ще прави кода неясен. Например какво ще разберете ако някъде в кода се умножава по 0.318309886? Най-вероятно нищо. А тази константа всъщност е 1/pi, или бърз начин да делите на числото Пи.

Основната причина за ограничаването на блуждаещи числа в кода, обаче, е друга. Нека например имате квадратна матрица, с размер 100, и правите цикли за да я инициализирате. Това като код ще изглежда нещо от сорта на:
double
ma3x[
100
][
100
]
;
double
getProbability(
int
perc) {
double
prob
=
(
double
)perc
/
100
;
for
(
int
i
=
0
;
i
<
100
;
i
+
+
) {
for
(
int
c
=
0
;
c
<
100
;
c
+
+
) { ma3x[i][c]
=
prob
;
} }
return
0
;
}
По някое време, обаче, забелязвате, че ограничението на задачата е до 200, не до 100. За да оправите кода си, трябва да минете навсякъде през него и да замените всяко срещане на числовата константа 100, с 200. И то само на местата, където това точно 100 трябва да бъде променено (може да има друго 100, което означава нещо съвсем различно и не трябва да бъде пипано, като например превръщане от проценти в число с плаваща запетая в [0, 1]).
Вместо това, дефинирайки размера на масива като именувана константа най-отгоре и ползвайки нея навсякъде в кода, където това е нужно, води до буквално едносимволна промяна за целия код.
const
int
MAX
=
100
;
// Това е единственото място, което трябва да променим
double
ma3x[MAX][MAX]
;
double
getProbability(
int
perc) {
double
prob
=
(
double
)perc
/
100
;
for
(
int
i
=
0
;
i
<
MAX
;
i
+
+
) {
for
(
int
c
=
0
;
c
<
MAX
;
c
+
+
) { ma3x[i][c]
=
prob
;
} }
return
0
;
}

Променливите са предмети, функциите са действия

Използвайте предмети за имена на променливи (масиви, класове), и действия за имена на функции. Например int numStudents или double probability, докато int countStudents() или double getProbability().

Защо?
Така смислово може да се разграничават функции от променливи (които иначе са неразличими откъм име - и двете са с еднакъв тип CamelCase). Обикновено променливите всъщност са някакъв предмет (абстрактен или не), докато функциите са действия, така че това би било логично да направите и по принцип.

Подравняване

Подравнявайте смисловите компоненти на кода заедно и консистентно.

Защо?
Подравняване,
?В някои езици, като например Python, индентацията е начинът, по който се определя тялото на функция, цикъл или условие. Така не се налага ползването на къдрави скоби въобще и в същото време програмистите са "задължени" да пишат хубав код.
или индентация, както по-често ще го срещате, е един от най-добрите начини да направите кода си много по-четим и в същото време помага да избегнете някои грешки. Поради полезността си, почти всички редактори поддържат автоматична индентация, като това не налага почти никаква "допълнителна" работа от ваша страна.

Когато кодът е добре подравнен, лесно можете да видите кои стейтмънти (присвоявания, промени, декларации) принадлежат към кои компоненти (функции, цикли, условни оператори).

В следващия пример е сравнително трудно да се види кое се върши в кой цикъл.
const
int
MAX_N
=
128
;
int
numNodes
;
int
graph[MAX_N][MAX_N]
;
bool
checkForPath(
int
startNode
,
int
endNode) {
int
numComparisons
=
0
;
for
(
int
k
=
0
;
k
<
numNodes
;
k
+
+
) {
if
(graph[startNode][endNode]) {
return
true
;
}
for
(
int
i
=
0
;
i
<
numNodes
;
i
+
+
) {
if
(graph[startNode][endNode])
return
true
;
for
(
int
c
=
0
;
c
<
numNodes
;
c
+
+
) {
if
(graph[i][k]
!
=
0
&
&
graph[k][c]
!
=
0
) { graph[i][c]
=
1
;
if
(i
=
=
startNode
&
&
c
=
=
endNode)
return
true
;
} numComparisons
+
+
;
} } } fprintf(
stderr
,
"Wasted %d comparisons!\n"
,
numComparisons)
;
return
false
;
}
Когато е подравнен, това става далеч по-лесно.
const
int
MAX_N
=
128
;
int
numNodes
;
int
graph[MAX_N][MAX_N]
;
bool
checkForPath(
int
startNode
,
int
endNode) {
int
numComparisons
=
0
;
for
(
int
k
=
0
;
k
<
numNodes
;
k
+
+
) {
if
(graph[startNode][endNode]) {
return
true
;
}
for
(
int
i
=
0
;
i
<
numNodes
;
i
+
+
) {
if
(graph[startNode][endNode])
return
true
;
for
(
int
c
=
0
;
c
<
numNodes
;
c
+
+
) {
if
(graph[i][k]
!
=
0
&
&
graph[k][c]
!
=
0
) { graph[i][c]
=
1
;
if
(i
=
=
startNode
&
&
c
=
=
endNode)
return
true
;
} numComparisons
+
+
;
} } } fprintf(
stderr
,
"Wasted %d comparisons!\n"
,
numComparisons)
;
return
false
;
}

Подравняване чрез шпации

Накарайте редактора си да замества таблуации с фиксиран брой шпации - например 2 или 4.

Защо?
Макар и само препоръчително, това правило има известен смисъл - така ще гарантирате, че кодът ви ще се показва по еднакъв начин в различни среди. Примерно в някои редактори една табулация е с размер на две шпации, на някои с четири, а на трети - осем. Използвайки шпации вместо табулации, кодът ви ще бъде еднакъв и в трите редактора. Всъщност вие ще продължите да ползвате табулация, когато искате да индентирате даден ред или блок, просто редакторът ви "незабелязано" ще ползва фиксиран брой шпации, вместо знакът за табулация.

Кратки редове

Ограничавайте дължината на редовете си до 80, 100 или най-много 120 символа на ред.

Защо?
Много дългите редове са трудни за четене и осмисляне. Стигането до дълъг ред означава, че вършите много неща в него, което обикновено е лоша практика и предпоставка за грешки.

Кратки функции

Избягвайте писането на дълги и особено на много дълги функции. Освен ако не е наистина наложително (няма как да бъде разбита на по-малки такива), старайте се тялото на функцията ви почти никога да не надхвърля 40-50 реда, като рядко да е над 20-30.

Защо?
Обикновено писането на дълги функции означава, че вършите много неща в тях или имате много копиран код, което не са хубави практики. Повечето на брой, но по-кратки функции, са по-лесни за промяна, дебъгване, а дори в някои случаи и за оптимизиране. Чрез имената на функциите можете да "подсказвате" какво всъщност правите вътре, като така до известна степен се коментира кода дори без писане на коментари.

Къдрави скоби

Отварящата скоба е на същия ред, разделена със шпация от елемента, на който принадлежи. Затварящата е на отделен ред, със същата индентация, с която е елемента, който затваря.

Защо?
Така лесно ще знаете коя скоба кое затваря, и откъде докъде се простира даден блок код - просто ходите нагоре по кода, докато стигнете до друг видим символ със същата индентация.

За това къде да са скобите има много варианти и стандарти - това е само един от тях. Той е и един от най-разпространените, така че често ще го срещнете. Предимството му е, че е сравнително балансиран между добро отделяне на блока от останалия код и използване на допълнителни редове. Друга алтернатива, която е (или поне доскоро беше) моят личен фаворит е Allman (ANSI, BSD) стилът. Няколко от основните стилове можете да видите ето тук.

Един стейтмънт на ред

Не слагайте по повече от един стейтмънт на ред. Потенциално изключение правят стейтмънти, които са много кратки, подобни и тясно свързани. В този случай ги разделяйте със запетая, вместо точка и запетая.

Защо?
Макар и понякога много примамливо, слагането на повече от един смислов елемент на ред може да доведе до грешки. Дори и в момента на писане на кода тяхната употреба да е вярна, по-нататъшни промени могат да доведат до грешки.
Отделянето на операции чрез запетаи е почти идентично на това с точка и запетая, но кара операциите да се третират като блок (все едно са групирани в къдрави скоби).

Пример за грешка:
Нека имаме парче код, което проверява дали дадена стойност е надхвърлила определен лимит, и ако да - да я запазваме в друга променлива и намаляме, след което печатаме дебъг информация за това.
if
(someVariable
>
=
SOME_LIMIT) { lastVariable
=
someVariable
;
someVariable
-
=
SOME_LIMIT
;
fprintf(
stderr
,
"DEBUG: Reducing variable to %d\n"
,
someVariable)
;
}
След като сме написали програмата, решаваме да премахнем дебъг информацията и (в случая грешно) къдравите скоби.
if
(someVariable
>
=
SOME_LIMIT) lastVariable
=
someVariable
;
someVariable
-
=
SOME_LIMIT
;
Ако бяхме ползвали запетая, вместо точка и запетая, кодът все още би бил верен.
if
(someVariable
>
=
SOME_LIMIT) lastVariable
=
someVariable
,
someVariable
-
=
SOME_LIMIT
;

Локалност

Декларирайте локалните променливи близо до мястото, където ги използвате за пръв път, вместо в началото на функцията.

Защо?
Да се декларират всички променливи в началото на функцията е нещо, наследено от C. В C++, обаче, няма никакъв смисъл това да се прави, тъй като езикът позволява те да бъдат декларирани навсякъде в тялото на функцията. Колкото по-късно декларирате променливата, толкова по-малък е шансът да я използвате погрешка по-рано в кода, или да промените стойността ѝ (ако е инициализирана).

Същото важи за променливи, които ползвате за итератори в цикли. Например защо да декларирате int i и после да го ползвате във for (i = 0; i < n; i++)..., след като можете да направите това директно във for-цикъла? Нещо повече, така не се налага да мислите предварително какви променливи ще ви трябват - ще си ги създавате в процеса на писане. Също така няма да се налага да се връщате до началото на функцията, за да добавяте нови променливи, когато ви потрябват такива!

Коментари

Оставяйте кратки коментари преди сложно парче код. Ако в дадена функция (алгоритъм) трябва да свършите няколко неща, преди да почнете имплементацията напишете по един коментар за всяка негова част, за да не забравите, че трябва да я свършите, докато се занимавате с останалите.

Защо?
Коментарите, макар и "излишен" код, могат да бъдат полезни при имплементиране на парчета код с по-сложна логика.
?// sometimes I believe compiler ignores all my comments
В общия случай една или две думи са напълно достатъчни за напомняне на какво трябва да свършите.

Например, пишейки някаква функция, преди да почнем с "реалната" имплементация, бихме могли да си оставим следните подсказки.
int
someFunction(
int
someArgument1
,
int
someArgument2) {
// Handle corner cases
// Handle primes
// Handle even non-primes
// Handle odd non-primes
// Memoize
}
Разбира се, в реално състезание можем да пропуснем дори "Handle" от коментарите, тъй като е очевидно какво имаме предвид.

Коментиран код

Коментирайте код в тялото на функцията с //, докато цели функции с /*...*/.

Защо?
/*...*/ е удобно за коментиране както на малък брой редове, така и за голям. //,
?//////////////////////// this is a well commented line
от друга страна (освен ако не ползвате шорткът за това) коментира само по един ред и изисква повече работа за три или повече реда.

Ако искате да коментирате цяла функция, обаче, няма да можете да сторите това, ако вътре в нея има друг /*...*/ коментар. От друга страна, ако всички "вътрешни" коментари са от тип //, то ще можете.

Използвайте sizeof() върху променливи

Използвайте sizeof() върху променливи, не върху типове. Например за да вземете броя байтове на масив int someArray[MAX][MAX], ползвайте sizeof(someArray) вместо sizeof(int) * MAX * MAX.

Защо?
sizeof() работи правилно както за типове, така и за променливи, едномерни масиви и дори многомерни масиви. Подавайки самата променлива като аргумент, а не нейния тип, помага да избегнете следната потенциална грешка: ако по някое време решите да смените типа на масива (на, примерно, short или double), sizeof(someArray) ще връща отново правилен размер, докато sizeof(int) * MAX * MAX вече ще бъде грешно.

Допълнителни материали




За да предложите корекция, селектирайте думата или текста, който искате да бъде променен,
натиснете Enter и изпратете Вашето предложение.
Страницата е посетена 3335 пъти.

Предложете корекция

Selected text (if you see this, there is something wrong)

(Незадължително) E-mail за обратна връзка: