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) - результат один и тот же - всё компилится без предупреждений и даже запускается.

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

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

Eddy_Em ☆☆☆☆☆ ()

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

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

П.С. точно:

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

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

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

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

anonymous ()
Ответ на: комментарий от 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 ★★★★★ ()
Ответ на: комментарий от Eddy_Em

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

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

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

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

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

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

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

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

beastie ★★★★★ ()
Ответ на: комментарий от 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 ☆☆☆☆☆ ()
Ответ на: комментарий от 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 ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.