LINUX.ORG.RU

C + random + array + segfault

 , , ,


0

1
#include <stdio.h>

main() {
    srand(time(NULL));
    int arr[9999999];
    int i;
    for(i=0; i<9999999; i++) {
        arr[i] = rand() % 99999999;
        printf("%d \n", arr[i]);
    }
}

решил откопать книгу по Си. Первый пример, ловлю segfault. Что не так? вывод strace:

execve("./a.out", ["./a.out"], [/* 47 vars */]) = 0
brk(0)                                  = 0x87a1000
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7716000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=111260, ...}) = 0
mmap2(NULL, 111260, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb76fa000
close(3)                                = 0
open("/lib/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\0\227\1\0004\0\0\0"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1790836, ...}) = 0
mmap2(NULL, 1591836, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb7575000
mprotect(0xb76f3000, 4096, PROT_NONE)   = 0
mmap2(0xb76f4000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x17e) = 0xb76f4000
mmap2(0xb76f7000, 10780, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xb76f7000
close(3)                                = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7574000
set_thread_area({entry_number:-1 -> 6, base_addr:0xb75746c0, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
mprotect(0xb76f4000, 8192, PROT_READ)   = 0
mprotect(0xb7738000, 4096, PROT_READ)   = 0
munmap(0xb76fa000, 111260)              = 0
--- SIGSEGV (Segmentation fault) @ 0 (0) ---
+++ killed by SIGSEGV +++
Ошибка сегментирования
ничего не понимаю :)

Deleted

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

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

Тебе надо сделать так.

myunit.h

#ifndef _H_$MYUNIT
#define _H_$MYUNIT
extern int    unitx = 1337;
extern double unity;

void helloworld() //В загаловоных файлах надо обьявлять прототипы функций
#endif

myunith.c

void helloworld()
{
    puts("Hello world");
}

ex1.c

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

main()
{
    printf("%d\n", unitx);
    helloworld();
}

программа выведет:

1337
Hello World

Использовать одинаковые имена нелья.

nickionn ★☆
()
Последнее исправление: nickionn (всего исправлений: 1)
Ответ на: комментарий от ziemin

А что это и зачем? какую повторную загрузку? погоди. давай вначале с областью видимости подключаемых переменных. Как использовать в одном блоке импортированную переменную и локальную с одним именем?

Давай рассмотрим всё с точки зрения компилятора/линкера/загрузчика.

Алгоритм приблизительно такой (в общих чертах):

  • компилятор
    • Читает исходники. Строит AST. Выявляет простейшие ошибки...
    • Как я уже говорил, тело его не интересуте до тех пор, пока он на него не напорется в процессе обработки файлов.
    • А в процессе обработки он создаёт объектные файлы. Кстати в порядке их следования в командной строке.
    • Второго прохода он не делает, так что на сцену выходит...
  • линкер
    • На предыдущем этапе созданы объектные файлы. Здесь начиняется увязка. Если ты где-то написал extern, то линкеру об этом осталась запись.
    • Теоретически он должен сразу же найти, но есть порядок. Порядок указывается опять-таки аргументами командной строки.
ziemin ★★
()
Ответ на: комментарий от nickionn

спасибо. очень доходчиво написано. сделал так: myunit.h

#ifndef H_MYUNIT
#define H_MYUNIT

extern int    unitx = 1337;
extern double unity;

#endif
#include "myunit.h"
helloworld() {
    puts("Hello world");
}
#include <stdio.h>
#include "myunit.h"

main() {
    int unitx = 0;
    printf("%d\n", unitx);
    helloworld();
}
и компилируя gcc ex1.c myunit.c получается ошибка:
myunit.h:4:15: предупреждение: «unitx» initialized and declared «extern» [по умолчанию включена]
In file included from myunit.c:1:0:
myunit.h:4:15: предупреждение: «unitx» initialized and declared «extern» [по умолчанию включена]
/tmp/ccrPX4rI.o:(.data+0x0): multiple definition of `unitx'
/tmp/ccatgGv9.o:(.data+0x0): first defined here

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

Не надо в .h определять функции. Вот ещё отличие C от паскаля: паскаль бьёт по рукам, а C рассчитывает, что ты знаешь, что делаешь.

В терминах паскаля ты попытался разместить в interface функцию.

Кстате. Ошибку выдал именно линкер! Буду надеятся, что на таких простых примерах ты поймёшь метод компиляции.

ziemin ★★
()
Последнее исправление: ziemin (всего исправлений: 1)
Ответ на: комментарий от Deleted

multiple definition of `unitx'

Ты 2 раза обьявляешь unitx, 1 раз в myuint.h второй раз в main(). Используй другое имя.

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

Спасиииибо! Работает! А что возвращает void? и почему нельзя использовать одинаковые имена? Ведь есть области видимости в функциях же!

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

А что возвращает void

Ничего.

и почему нельзя использовать одинаковые имена? Ведь есть области видимости в функциях же!

Потому что unitx глобальная переменная.

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

я не могу создать файлик myunit2.c в котором определю функцию пользуясь определениями из myunit.h и при этом, использовать эту функцию в ex1.c?

чтоб потом gcc ex1.c myunit.c myunit2.c? попробовал сейчас - не получается. пишет:

alex@localhost $ gcc ex1.c myunit.c muynit2.c 
In file included from ex1.c:2:0:
myunit.h:4:15: предупреждение: «unitx» initialized and declared «extern» [по умолчанию включена]
myunit.h:7:1: предупреждение: data definition has no type or storage class [по умолчанию включена]
In file included from muynit2.c:1:0:
myunit.h:4:15: предупреждение: «unitx» initialized and declared «extern» [по умолчанию включена]
myunit.h:7:1: предупреждение: data definition has no type or storage class [по умолчанию включена]
muynit2.c: В функции «helloworld2»:
muynit2.c:4:5: предупреждение: несовместимая неявная декларация внутренней функции «printf» [по умолчанию включена]
/tmp/ccO059lZ.o:(.data+0x0): multiple definition of `unitx'
/tmp/ccsWP4nY.o:(.data+0x0): first defined here
/tmp/ccO059lZ.o: In function `helloworld2':
muynit2.c:(.text+0x7): undefined reference to `unity'
muynit2.c:(.text+0xd): undefined reference to `unity'
collect2: ошибка: выполнение ld завершилось с кодом возврата 1

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

Потому что unitx глобальная переменная.

непонял. это когда я импортировал директивой #include да она стала глобальной (за функцией main)? а в main нельзя адресоваться к этой глобальной переменной имея локальную с таким же именем?

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

Ты опять 2 раза определяешь unitx, в этот раз уже в myunit2.c. Для myunit2.c тоже нужно создать заголовочный файл.

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

я почти, почти. помоги еще с myunit2.c{h} которая б использовала myunit.c{h} и использовалась в ex1.c и я совсем уж тогда :)

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

Научись отличать предупреждения от ошибок.

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

myunit.h

#ifndef H_MYUNIT
#define H_MYUNIT

extern const int unitx;
extern const int unity;

#endif

myunit.c

#include "myunit.h"

const int unitx = 1337;
const int unity = 4000;

myunit2.h

#ifndef H_MYUNIT2
#define H_MYUNIT2

void foo();

#endif

myunit2.c

#include "myunit2.h"
#include "myunit.h"

void foo()
{
    printf("%i/n",unitx);
    printf("%i/n",unity);
}

ex1.c

#include "myunit2.h"

int main()
{
foo()
}
nickionn ★☆
()
Ответ на: комментарий от nickionn

АаааааАааа! понял! Спасибо тебе, и ziemin. Благодаря вам. Из этого непонятно только зачем void, если функцию можно объявлять без возвращаемого значения.

P.S. const означает константу, да? неизменяемое значение?

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

пожалуйста, друг, только это осталось: зачем void, если функцию можно объявлять без возвращаемого значения? :)

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

АаааааАааа! понял!

Ничё ты не понял... Перевари.

Из этого непонятно только зачем void, если функцию можно объявлять без возвращаемого значения.

void значит ничего. Но это с одной стороны. С другой void это супертип, который включает в себя всЁ. Например: что такое 'c'? Это символ. Неважно в какой кодировке... Или (char (void *) &string)

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

Ничё ты не понял... Перевари.

я сделал так: определил в одном хедере переменные и функции (interface) и в c-файле их реализацию(implementation) (тонкость в том, что я определил содержимое этих переменных в interface - но в Си это работает, а в implementation - только функцию). В другом хедере я определил другую функцию(interface), в которой буду использовать переменные из первого хедера, а в втором c-файле подключил хедер этого файла и хедер содержащий объявления тех переменных которые я хочу использовать в этой функции. Затем в главной программе я подключил хедер системный, т.к. все они используют libc-функции, а затем второй хедер, так как он содержит объявления в том числе и первого хедера. Затем при компиляции передал в качестве аргументов компилятору главную программу (ex1.c) и вскомпилировал главный файл ex1 и второй с файл, а затем первый и второй файлы *.c содержащие реализацию хедеров (implementation).

Мне кажется понял, буду переваривать :)

void значит ничего

nil? NULL?

С другой void это супертип, который включает в себя всЁ. Например: что такое 'c'? Это символ. Неважно в какой кодировке... Или (char (void *) &string)

охохо, всё на сегодня видимо. я ничего не понял :)

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

Нельзя не указывать тип данных

Немедленно прекрати дезинформировать. void это без указания типа данных.

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

охохо, всё на сегодня видимо. я ничего не понял :)

Всё просто.Только не рассуждай с житейских понятий.

У компилятора машинное представление. Встань на его место, если хочешь понять.

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

охохо, всё на сегодня видимо. я ничего не понял :)

чувак тебе мозги пудрит и запутывает, он сам до конца не понимает что пытается объяснить. Лучше таки осиль прочитать какую-нибудь книжку по си.

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

int на самом деле..ещё честнее - нечто зависящее от соглашений по конкретной аппаратуре. Для x86, это то что поместилось хотя-бы в AX (регистр используемый для возврата значений).

void говорит компилятору что возвращаемое значение (AX или AX:DX и так далее) не имеет смысла и не может быть задействовано (хотя оно есть) ни в коде программы, ни при оптимизациях.

исторически: функция без указания типа (либо без прототипа) - int.

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

int на самом деле..ещё честнее

Такое ощущение, что ты продолжаешь разговор.

Тем не менее мои доводы (и не только мои) состоят в том, что void позволяет комиплятору не предполагать (как он обычно делает), а располагать. Неизвесным значением. Он только на свои возможности и опирается.

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

Сильно сказано. Аргументы будут?

Так не очиводно? Как начинаешь что-то объяснять, так сразу скатываешься в какой-то забористый бред. Вот сейчас, например, c void'ом фигню несёшь.

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

это ты из rsp вычитаешь 40000000

а нельзя ли тут вставить код который будет проверять можно вычитать или нет?

можно конечно! Можно вставить код проверки, можно сделать освобождение/вставку не только LIFO. Получится ещё один malloc(3).

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

В чём разница поведения?

в сишке локальные переменные объявляются в стеке, ВСЕГДА.

В паскале ЕМНИП массивы объявляются в куче (т.е. через malloc(3)).

Подход сишки — скорость. Паскаля — надёжность. Но в сишке ты можешь и в куче выделить, и в стеке. А в паскале ты можешь только молится святому Вирту, что-бы компилятор догадался сделать то, что тебе нужно.

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

еще говорят что паскаль многословный

правильно говорят.

extern

из-за краткости синтаксиса компилятор не может отличить объявление от описания. Потом считает, что если ты пишешь в глобальной области

int x;

то ты объявляешь x (выделяешь для него 4 байта). А если ты хочешь просто описать x, который объявлен в другом месте, юзай extern

register

не нужная подсказка компилятору, что переменную _лучше_ хранить в регистре, а не в стеке. В 21ом веке таких тупых компиляторов не используется.

auto

вредное слово, которое НЕ НУЖНО использовать, ибо в C++ там совсем иной смысл.

значит оно «использовать стек», и работает по умолчанию. Для единообразия ввели, а потом было уже поздно что-то менять.

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

static в отличие от объявления переменной за main определяет её область видимости только в main?

да.

а как в другой функции использовать эту статическую переменную?

функция f() может отдать другой функции указатель на свою static int x;, и тогда другая f2() может юзать этот x.

А вот если просто int x, то отдать указатель нельзя, ибо x будет уничтожена.

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

тогда зачем void? ведь и объявление просто функции ничего не возвращает.

«просто» возвращает int.

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

void значит ничего

nil? NULL?

void значит НИЧЕГО

NULL это указатель, который НИКУДА не указывает.

nil, ЕМНИП это просто пустой объект. Хотя я не в курсе, что это.

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

Тем не менее мои доводы (и не только мои) состоят в том, что void позволяет комиплятору не предполагать (как он обычно делает), а располагать. Неизвесным значением. Он только на свои возможности и опирается.

ты чушь порешь.

void — просто функция, которая просто ничего не возвращает в рамках C/C++. Нет, в рамках машинного представления конечно остаётся какой-то мусор в регистрах, но он не имеет смысла.

Да, это важно при оптимизации, ибо функция может быть объявлена в другом модуле, и компилятор не увидит, нужно-ли кому-то возвращаемое значение, или нет. Ну и придётся что-то возвращать, и тратить время. Как например в printf(3), которая возвращает число напечатанных байт(а может символов, я не помню). Обычно эта информация не используется, но всё равно формируется и передаётся в коде.

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

можно конечно! Можно вставить код проверки

вот про то речь и шла, чтобы ОПЦИОНАЛЬНО по флагу вставлять код который при исчерпании стека будет завершать процесс с более вменяемым сообщением.

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

вот про то речь и шла, чтобы ОПЦИОНАЛЬНО по флагу вставлять код который при исчерпании стека будет завершать процесс с более вменяемым сообщением.

ты вообще представляешь себе, что этот твой код будет вставляться В КАЖДЫЙ сраный int a???

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

детка, иди кури стандарт на предмет осознания того, что такое void, и что такое void*. Ты не поверишь, но это не просто разные вещи, это тёплое и мягкое.

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