LINUX.ORG.RU

Определение размерности массива в «рантайме» в gcc

 ,


0

2

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

#include "stdio.h"

int foo(int x) {
	return x + 5;
}

int bar() {
	return 3;
}


int main(int argc, char const *argv[]) {
	
	int x[foo(3)];
	printf("%zu\n", sizeof(x));

	int y = foo(3);
	int ya[y];
	printf("%zu\n", sizeof(ya));

	int z = 3;
	int zz = foo(z);
	int za[zz];
	printf("%zu\n", sizeof(za));

	int a[foo(bar())];
	printf("%zu\n", sizeof(a));
	
	return 0;
}
Но GCC компилирует, и даже не кидает ворнингов с -Wall. Пробовал компилить с -ansi, -std=iso9899:199409, -std=gnu90, также отключал инлайнинг (-fno-default-inline) и вообще всю оптимизацию (-O0) - результат один и тот же - всё компилится без предупреждений и даже запускается.

Что поэтому поводу говорит стандарт? Когда так делать можно, а когда нельзя?



Последнее исправление: cetjs2 (всего исправлений: 1)

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

Eddy_Em ☆☆☆☆☆
()

Всё время считал, что размерность массива должна быть определена на этапе компиляции

ЕМНИП, gcc тупо всегда позволяет использует VLA, не только с C99, это у них extension такой.

П.С. точно:

https://gcc.gnu.org/onlinedocs/gcc/Variable-Length.html

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

Поздравляю, ты открыл для себя c99

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

anonymous
()

аноним все верно сказал, это экстеншн от g++.

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

И правда, невнимательно прочел.

ТС, ну-ка, запусти вот это:

#include "stdio.h"
#include <stdlib.h>

int foo(int x) {
    return x + 5;
}

int bar() {
    return 3;
}


int main() {
    int x[foo(3+rand())];
    printf("%zu\n", sizeof(x));

    int y = foo(3+rand());
    int ya[y];
    printf("%zu\n", sizeof(ya));

    int z = 3 + rand();
    int zz = foo(z);
    int za[zz];
    printf("%zu\n", sizeof(za));

    int a[foo(bar()+rand())];
    printf("%zu\n", sizeof(a));

    return 0;
}
чтобы сегфолта не было

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

Что за говно ты мне даешь?

Давай, компиляй в gcc! И показывай выхлоп запуска. У меня вот:

gcc 111.c && ./a.out 
Ошибка сегментирования (core dumped)

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от Eddy_Em
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int foo() {
	srand(time(NULL));
	return rand() % 10;
}

int main(int argc, char const *argv[]) {

	int x[foo()];

	*x = 5;

	printf("%zu\n", sizeof(x));
	printf("%d\n", *x);
	return 0;
}

Это конечно полный трэш, но ни предупреждения, ни сегфолта нет. С -pedantic только предупреждение

Anvill
() автор топика
$ gcc -pedantic main.c
main.c: In function ‘main’:
main.c:14:2: warning: ISO C90 forbids variable length array ‘x’ [-Wvla]
  int x[foo(3)];
  ^
main.c:15:2: warning: ISO C90 does not support the ‘z’ gnu_printf length modifier [-Wformat=]
  printf("%zu\n", sizeof(x));
  ^
main.c:17:2: warning: ISO C90 forbids mixed declarations and code [-Wpedantic]
  int y = foo(3);
  ^
main.c:18:2: warning: ISO C90 forbids variable length array ‘ya’ [-Wvla]
  int ya[y];
  ^
main.c:19:2: warning: ISO C90 does not support the ‘z’ gnu_printf length modifier [-Wformat=]
  printf("%zu\n", sizeof(ya));
  ^
main.c:21:2: warning: ISO C90 forbids mixed declarations and code [-Wpedantic]
  int z = 3;
  ^
main.c:23:2: warning: ISO C90 forbids variable length array ‘za’ [-Wvla]
  int za[zz];
  ^
main.c:24:2: warning: ISO C90 does not support the ‘z’ gnu_printf length modifier [-Wformat=]                                                                                                                                                                                  
  printf("%zu\n", sizeof(za));                                                                                                                                                                                                                                                 
  ^
main.c:26:2: warning: ISO C90 forbids variable length array ‘a’ [-Wvla]
  int a[foo(bar())];
  ^
main.c:26:2: warning: ISO C90 forbids mixed declarations and code [-Wpedantic]
main.c:27:2: warning: ISO C90 does not support the ‘z’ gnu_printf length modifier [-Wformat=]
  printf("%zu\n", sizeof(a));
cyanide_regime
()
Ответ на: комментарий от Eddy_Em
plcmn@equestria:/tmp$ gcc mas.c
plcmn@equestria:/tmp$ ./a.out 
Ошибка сегментирования (core dumped)
plcmn@equestria:/tmp$ clang mas.c
plcmn@equestria:/tmp$ ./a.out 
7217157564
3387723576
6726771140
6858547692
plcmn@equestria:/tmp$ gcc --version
gcc (GCC) 4.9.2 20150304 (prerelease)
Copyright (C) 2014 Free Software Foundation, Inc.
Это свободно распространяемое программное обеспечение. Условия копирования
приведены в исходных текстах. Без гарантии каких-либо качеств, включая 
коммерческую ценность и применимость для каких-либо целей.

plcmn@equestria:/tmp$ clang --version
clang version 3.6.0 (tags/RELEASE_360/final)
Target: x86_64-unknown-linux-gnu
Thread model: posix

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

3+rand()

Ты с головой дружишь?

The rand() function returns a pseudo-random integer in the range 0 to RAND_MAX inclusive (i.e., the mathematical range [0, RAND_MAX]).

От пары гигабайт на стеке и я бы засегфолтился.

3+rand() % 20 работает, если что.

post-factum ★★★★★
()
Ответ на: комментарий от Anvill

Это не треш, а VLA. Какие все знатоки здесь собрались.

post-factum ★★★★★
()
Ответ на: комментарий от Eddy_Em

Ты в курсе, что rand() только прикидывается rand? ;)

И что 1103527590 +- валенок в стек вряд ли влезут.

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

Ничего не понимаю. Как оно по умолчанию в с99 прет?

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

Я про бензопилу, зажатую промеж яиц =D

Так и NULL разыменовывать можно.

сам не использую VLA и никому не советую

Обоснуй.

post-factum ★★★★★
()
Ответ на: комментарий от Eddy_Em

сам не использую VLA и никому не советую

Ну и ... не бойсь ножа, а бойся вилки — один удар — четыре дырки!

Инструментом нужно уметь пользоватся, а не боятся.

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

пользоватся

боятся

Ну что же ты!

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

Можно, если компилить код по новому стандарту компилятором по старому стандарту. И кто виноват?

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

Так и NULL разыменовывать можно.

((void(*)(void))NULL)()

Причём это абсолютно валидный метод разименовывания нуля, который я использую для софт-ребута AVR. :D

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

Вот ещё, можетпоказаться интересным: Вот это:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

size_t foo() {
	int x[rand()];
	return sizeof(x);
}

int main(int argc, char const *argv[]) {

	printf("%zu\n", foo());
	return 0;
}

Компилится в это:

size_t foo() {
   0:	55                   	push   rbp
   1:	48 89 e5             	mov    rbp,rsp
   4:	41 57                	push   r15
   6:	41 56                	push   r14
   8:	41 55                	push   r13
   a:	41 54                	push   r12
   c:	53                   	push   rbx
   d:	48 83 ec 18          	sub    rsp,0x18
  11:	48 89 e0             	mov    rax,rsp
  14:	48 89 c3             	mov    rbx,rax
	int x[rand()];
  17:	e8 00 00 00 00       	call   1c <foo+0x1c>
  1c:	89 c1                	mov    ecx,eax
  1e:	48 63 c1             	movsxd rax,ecx
  21:	48 83 e8 01          	sub    rax,0x1
  25:	48 89 45 c0          	mov    QWORD PTR [rbp-0x40],rax
  29:	48 63 c1             	movsxd rax,ecx
  2c:	49 89 c6             	mov    r14,rax
  2f:	41 bf 00 00 00 00    	mov    r15d,0x0
  35:	48 63 c1             	movsxd rax,ecx
  38:	49 89 c4             	mov    r12,rax
  3b:	41 bd 00 00 00 00    	mov    r13d,0x0
  41:	48 63 c1             	movsxd rax,ecx
  44:	48 c1 e0 02          	shl    rax,0x2
  48:	48 8d 50 03          	lea    rdx,[rax+0x3]
  4c:	b8 10 00 00 00       	mov    eax,0x10
  51:	48 83 e8 01          	sub    rax,0x1
  55:	48 01 d0             	add    rax,rdx
  58:	be 10 00 00 00       	mov    esi,0x10
  5d:	ba 00 00 00 00       	mov    edx,0x0
  62:	48 f7 f6             	div    rsi
  65:	48 6b c0 10          	imul   rax,rax,0x10
  69:	48 29 c4             	sub    rsp,rax
  6c:	48 89 e0             	mov    rax,rsp
  6f:	48 83 c0 03          	add    rax,0x3
  73:	48 c1 e8 02          	shr    rax,0x2
  77:	48 c1 e0 02          	shl    rax,0x2
  7b:	48 89 45 c8          	mov    QWORD PTR [rbp-0x38],rax
	return sizeof(x);
  7f:	48 63 c1             	movsxd rax,ecx
  82:	48 c1 e0 02          	shl    rax,0x2
  86:	48 89 dc             	mov    rsp,rbx
}
  89:	48 8d 65 d8          	lea    rsp,[rbp-0x28]
  8d:	5b                   	pop    rbx
  8e:	41 5c                	pop    r12
  90:	41 5d                	pop    r13
  92:	41 5e                	pop    r14
  94:	41 5f                	pop    r15
  96:	5d                   	pop    rbp
  97:	c3                   	ret    

0000000000000098 <main>:

int main(int argc, char const *argv[]) {
  98:	55                   	push   rbp
  99:	48 89 e5             	mov    rbp,rsp
  9c:	48 83 ec 10          	sub    rsp,0x10
  a0:	89 7d fc             	mov    DWORD PTR [rbp-0x4],edi
  a3:	48 89 75 f0          	mov    QWORD PTR [rbp-0x10],rsi

	printf("%zu\n", foo());
  a7:	b8 00 00 00 00       	mov    eax,0x0
  ac:	e8 00 00 00 00       	call   b1 <main+0x19>
  b1:	48 89 c6             	mov    rsi,rax
  b4:	bf 00 00 00 00       	mov    edi,0x0
  b9:	b8 00 00 00 00       	mov    eax,0x0
  be:	e8 00 00 00 00       	call   c3 <main+0x2b>
	return 0;
  c3:	b8 00 00 00 00       	mov    eax,0x0
  c8:	c9                   	leave  
  c9:	c3                   	ret    
Anvill
() автор топика
Ответ на: комментарий от post-factum

Ну вот выше я такой пример и дал.

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

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

//offtop как у вас тут код в спойлер прятать?

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

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

Т.е., ты не следишь за кодом, который пишешь :)?

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

Когда его дофига и он старый, как-то не хочется опять все пересматривать. Хочется просто взять файл, да скопипастить. И из него нужные функции вызывать — уж заголовочный-то посмотреть несложно.

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

В начальном топике их нет. Я предположил, что говоря о разрешённых VLA в C99 ты намекаешь на запрещённые VLAiS.

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

Это конечно холиварный вопрос, но нас в универе учили именно интеловскому ассемблеру, поэтому он мне привычен

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

Работа со стеком, она вообще такая. Скажем, пользуешься ты спокойненько рекурсией с небольшой глубиной вложенности. Там-сям пользуешься, всё ОК. И тут тебе приспичивает то же самое сделать для глубины, скажем, 100500. И привет.

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

Вот то ж!

$ ulimit -s $((4*1024*1024))
$ gcc q.c && ./a.out 
7217157564
3387723576
6726771140
6858547692
i-rinat ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.