LINUX.ORG.RU

[C] Объявление переменных в теле функции


1

1

Я стал изучать C после C++ и обнаружил, что часто локальные переменные функции в С объявляются в самом начале этой функции. Стив Макконнелл пишет, что лучше объявлять переменные по необходимости (стараясь уменшить область видимости и persistense).

Я попробовал масдаевский сишный компилятор (непомню его название, вроде MSVC), так вот он действительно выдает ошибку, если объявить локальные переменные не в начале функции. Вопрос: зачем сейчас продолжают писать на С, объявляя локальные переменные в начале функции, а то получается Паскаль с его var-разделом?

Как возможный ответ - ограничение компилятора, и если я пишу прогу, которая должна быть переносимой, то надо писать так. Тогда встречный вопрос: неужели это актуально и нет «современных» компиляторов, которые позволяют объявлять переменные «по-нормальному»? Может, в крайнем случае, скопилировать прогу на С с помощью С++ компилятора?

Если возможно, дайте подробный ответ.

★★

Вопрос был уже давно, просто сейчас скачал исходники freeciv 2.2, изучаю. Вот он и всплыл.

bk_ ★★ ()

Если возможно, дайте подробный ответ.

D programming Language

wfrr ★★☆ ()

>Если возможно, дайте подробный ответ.
ты врёшь

dimon555 ★★★★★ ()

а вообще если использовать enter/leave, а не call/ret то действительно придётся знать, сколько места отвести под локальные переменные функции.

dimon555 ★★★★★ ()

Не вижу смысла объявления переменных не в начале

Zodd ★★★★★ ()
Ответ на: комментарий от bk_

Компилятор все равно же в начале память под него выделит.

Да и чисто визуально удобнее видеть все переменные вместе.

Zodd ★★★★★ ()
Ответ на: комментарий от Zodd

Тоже особо не вижу, тем более что рекомендуемая длина функции не должна быть больше одного экрана.

urxvt ★★★★★ ()

Что-то я такого не замечал:

cat 1.c 
#include <stdio.h>
int main(){
	int i = 10;
	printf("i=%d\n", i);
	int j;
	for(j=0; j<i; j++)
		printf("j= %d\n", j);
	char *word="Hello";
	printf("word=%s\n", word);
}
gcc 1.c
./a.out 
i=10
j= 0
j= 1
j= 2
j= 3
j= 4
j= 5
j= 6
j= 7
j= 8
j= 9
word=Hello
Я иногда объявляю в небольших функциях локальные переменные «по месту требования». Но в больших функциях лучше объявлять все переменные в самом начале, иначе легко запутаться.

Eddy_Em ☆☆☆☆☆ ()
Ответ на: комментарий от Zodd

> Да и чисто визуально удобнее видеть все переменные вместе.

Наоборот, удобнее видеть переменные там, где они используются. Т.е. при первом использовании явно писать(и читать) тип. Что там выделяет компилятор - вторично. Второй плюс такого объявление - ограничение использования переменных внутри скопов, чуть больше защиты от дурацких ошибок.

P.S. В C99 afaik так тоже можно. Но вот поддерживает ли msvc c99 - я не знаю.

YesSSS ★★★ ()

6.5 PROCEDURE CALLS FOR BLOCK-STRUCTURED
LANGUAGES
The IA-32 architecture supports an alternate method of performing procedure calls
with the ENTER (enter procedure) and LEAVE (leave procedure) instructions. These
instructions automatically create and release, respectively, stack frames for called
procedures. The stack frames have predefined spaces for local variables and the
necessary pointers to allow coherent returns from called procedures.


но имеет ли это отношение к данному случаю, большой вопрос.

dimon555 ★★★★★ ()
Ответ на: комментарий от YesSSS

Не хочу читать С99 - скажите, там разрешено объявлять в любом месте функции? А то http://www.itee.uq.edu.au/~comp2303/Leslie_C_ref/C/SYNTAX/glo_int_vars.html здесь написано, что объявлять можно только после начала блока {. Правда, что за стандарт, хреново знает, скорее всего старый, до С99.

bk_ ★★ ()
Ответ на: комментарий от bk_
#include <stdio.h>

int main(int argc, char **argv)
{
	int i = 0;
	printf("%d\n", i);
	int j = 1;
	printf("%d\n", i);
	return 0;
}

Вот это не пашет в Visual Studio 2008:

Error 1 error C2143: syntax error : missing ';' before 'type' d:\projects\test-c99\test-c99\test-c99.c 7 test-c99

bk_ ★★ ()
Ответ на: комментарий от bk_

Даже у Страуструпа написано, что лучше объявлять по мере надобности. Я думаю, он имеет определенное отношение к стандарту)

anonymous ()
Ответ на: комментарий от anonymous

> Даже у Страуструпа написано, что лучше объявлять по мере надобности. Я думаю, он имеет определенное отношение к стандарту)

к стандарту на Си? Вы ничего не путаете?

korvin_ ★★★★★ ()
Ответ на: комментарий от Zitzy

Значит MSVC 2008 не поддерживает С99, /* сучара */.

bk_ ★★ ()

Не то чтобы компилятор «не поддерживал», просто это стандарт. А что будет если никто не будет следовать стандартам? Повторится 99ый год(или когда там) как с html?
Так вот. В реальных проектах все переменные можно объявить в начале функций без проблем. Сложностей от этого не добавляется. Говорю как человек, который писать opengl-based игровой движок на чистом С.
Не знаю как в стандарте, но для дебага можно использовать nested-блоки:

#include <stdio.h> 
int main(){ 
   int i = 10; 
   printf("i=%d\n", i); 
   {
       int c = 20;
       printf("c=%d\n", c); 
   }
} 

tia ()
Ответ на: комментарий от bk_

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

tia ()
Ответ на: комментарий от YesSSS

Ой не факт.

				CosObj BBoxArray = CosNewArray(pdDocCos, false, 4);

				CosArrayPut(BBoxArray,0, CosNewInteger(pdDocCos, false, bleft));
				CosArrayPut(BBoxArray,1, CosNewInteger(pdDocCos, false, bbottom));
				CosArrayPut(BBoxArray,2, CosNewInteger(pdDocCos, false, bright));
				CosArrayPut(BBoxArray,3, CosNewInteger(pdDocCos, false, btop));
				CosDictPutKeyString(formXObject, "BBox", BBoxArray);

				CosDictPutKeyString(formXObject, "Name", CosNewNameFromString(pdDocCos, false, "FRM"));
				CosDictPutKeyString(formXObject, "Type", CosNewNameFromString(pdDocCos, false, "XObject"));
				CosDictPutKeyString(formXObject, "FormType", CosNewInteger(pdDocCos, false, 1));

				// and add to the appearance (MK) key.
				CosObj cMKDictObj =  CosDictGetKeyString (cAnnotObj, "MK"); // CosNewDict(pdDocCos, false, 1L);
				CosDictPut( cMKDictObj, ASAtomFromString("TP"), CosNewInteger (pdDocCos, false, 1 ));
				CosDictPut( cMKDictObj, ASAtomFromString("I"), formXObject);

				CosObj cNullObj = CosNewNull();

И такого километры. Удобно?

Имхо, иметь декларацию состояния в одном месте

            MemoryStream m;
            StreamReader r;
            MatchCollection c;
            List<Tuple2<string, string>> result;

            result = new List<Tuple2<string, string>>();
            m = new MemoryStream(input);
            m.Seek(0, SeekOrigin.Begin);
            r = new StreamReader(m);
            c = _selector.Matches(r.ReadToEnd());
            foreach (Match t in c)
            {
                result.Add(Func.Tuple<string, string>(RemoveDelimiters(t.Groups[2].Value), RemoveDelimiters(t.Groups[4].Value)));
            }
            return result;
, несоменно, удобнее - бегать по коду не надо. Т.к. видимость и использование - они ведь сильно разные бывают.

impfp ()
Ответ на: комментарий от impfp

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

tia ()
Ответ на: комментарий от anonymous

> Даже у Страуструпа написано, что лучше объявлять по мере надобности

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

GArik ★★★ ()
Ответ на: комментарий от GArik

Создание объекта класса может быть довольно дорогостоящи

Не только. Класс может попросту не иметь конструктора по умолчанию.

Begemoth ★★★★★ ()
Ответ на: комментарий от bk_

>Не хочу читать С99 - скажите, там разрешено объявлять в любом месте функции?

Да. В том числе там разрешены и объявления внутри цикла for.

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

linuxfan ()
Ответ на: комментарий от linuxfan

+1

[legolegs@battlehummer ~]$ gcc -O 1.c  -Wall -Wextra -pedantic -O3 -std\=c89
1.c: В функции ‘main’:
1.c:3: предупреждение: неявная декларация функции ‘printf’
1.c:3: предупреждение: несовместимая неявная декларация внутренней функции ‘printf’
1.c:4: предупреждение: ISO C90 forbids mixed declarations and code
1.c:7: предупреждение: ISO C90 forbids mixed declarations and code
1.c:9: предупреждение: control reaches end of non-void function

1.c отсюда

legolegs ★★★★★ ()
Ответ на: комментарий от wfrr

> D programming Language

Не знаешь что ответить по делу — молчи и не высовывайся из своей дырки^W простите, дупла.

a0 ()

«В начале» обьяляют потому что до появления стандарта C99, другой способ обьявления был некорректен.

С появлением нового стандарта C99 стало допустимо обьявлять переменные где угодно, однако многие до сих пор «по старинке» пишут все в начале функции. Почти все современные компиляторы поддерживают C99, но где-то эта поддержка может быть не включена по умолчанию.

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

Да, стандарт C99 выводит язык C из подмножества языка C++, в нем появились возможности, которые C++ не умеет (за ненадобностью), не помню подробностей, но это так :)

a0 ()
Ответ на: комментарий от a0

В хорошо спроектированном, сдизайненном коде, большая часть функций должна помещаться на 1 экран классического 80x25

И как же, интересно, разместить парсер аргументов командной строки, если их очень много, на одну страницу?

Eddy_Em ☆☆☆☆☆ ()
Ответ на: комментарий от Eddy_Em

Читай внимательно. Бóльшая часть функций != все функции. Verstehen?

anonymous ()
Ответ на: комментарий от anonymous

И да, если так нравится обьявлять «где попало», то даже в
классическом C есть хак, позволяющий это сделать:


int foo(int bar, ...)
{
int a=0, b=1, c=2, etc=MANY;

....



// Хочу се.. перемиенную!
{
// Тут можно обявлять так-же,. че угодно, это новый scope.
int a, d, e; // равно как и имя старое повторно использовать тут можно, но это ужасный стиль.
}

}

если мозолит глаза просто висящий блок { }, то можно писать if (1) { }, do { } while (0), итп.

a0 ()
Ответ на: комментарий от anonymous

Читай внимательно. Бóльшая часть функций != все функции.

Пытался было опровергнуть, но быстрым просмотром нашел у себя всего пару функций main на 4-8 экранов и функции захвата кадров с v4l2 (2-3 экрана). Остальные и правда на экран влезают, а многие даже пол-экрана всего занимают.

Eddy_Em ☆☆☆☆☆ ()

msvc: гуглить Visual C++ C99 C++ mode
большинство поддерживают c99, но остались и исключения

gavv ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.