IMHO.WS

IMHO.WS (http://www.imho.ws/index.php)
-   Программирование (http://www.imho.ws/forumdisplay.php?f=40)
-   -   Перераспределение памяти - realloc (http://www.imho.ws/showthread.php?t=141073)

Hubbitus 31.08.2009 12:22

Перераспределение памяти - realloc
 
Здравствуйте, Уважаемые.

Честно говоря, даже немного стыдно с таким вопросом выходить, но уже сколько бьюсь, никак не могу решить - не понимаю.

Задача элементарнейшая - надо перераспределиить память и увеличить массив еще на один элемент. Лучше сразу покажу код:
Код:

#include <iostream>
#include <stdlib.h>

void
test_func(int * argc, char ** argv){
*argv = (char*)realloc( *argv, sizeof(char*) * (++(*argc)) );
}

int
main(int argc, char **argv){
test_func(&argc, argv);
}

То есть в функции (метод в реале, но не суть), по указателю на указатель осуществляется доступ. Надо добавить еще один элемент.
НО, у меня все падает с сигналом SIGABRT.
Пожалуйста, объясните что я делаю не так и как надо?

Да, на всякий случай, компилятор - gcc-4.4.1
Вроде как все правильно

L0rd 01.09.2009 23:53

Во-первых, если тебя интересует почему падает с SIGABRT - потому что ты пытаешься перевыделить память по указателю argv, который любезно сформирован для тебя библиотекой CRT и является указателем на массив строк аргументов командной строки, а argc - их числом, и использовать realloc по указателю *argv (вообще каким-то образом изменять эти переменные) неверно.

Теперь - твой realloc по *argv будет перевыделять память по указателю на первую строку из этого массива - т.е., если мы запускаем программу без аргументов, то это будет указатель на строку с именем файла - а чтобы добавить элемент в подобный массив строк тебе надо использовать функцию примерно так:

Код:

argv = (char**)realloc( argv, sizeof(char*) * (++(*argc)) );

Hubbitus 02.09.2009 12:40

нет,
Цитата:

Сообщение от L0rd (Сообщение 1672357)
а чтобы добавить элемент в подобный массив строк тебе надо использовать функцию примерно так:

Результат тот же - SIGABRT.

На реалкодинге высказали мысль, что его менять вообще нельзя - http://forums.realcoding.net/index.php?showtopic=23479

L0rd 03.09.2009 01:19

Цитата:

Сообщение от Hubbitus (Сообщение 1672397)
Результат тот же - SIGABRT.

Вот ведь.. Я эту конструкцию написал для примера - просто как добавлять элемент в подобный массив строк. Если прочтешь выше внимательней - то найдешь ту же самую мысль, что и высказали тебе на стороннем ресурсе!
Эти переменные нельзя изменять!
Прочитай в интернете что-нибудь про библиотеку CRT и вызов функции main() - узнаешь как оно все устроено ;)

Hubbitus 03.09.2009 10:44

L0rd, как добавить в подобный массив, я и так знаю, и привел выше пример. Меня интересует конкретная проблема, поставленная в первом посте. Что вообще нужно искать ответ в CRT и стандартах C/C++ я знаю и без Вас. Поэтому, если есть конкретные предложения, решения, мысли, ссылки (на те же стандарты, где описано по этому поводу) - велкам. А просто писать непроверенные неработающие примеры лучше не стоит. Уж извините за резкость.

L0rd 03.09.2009 19:49

Например здесь можно прочитать про функцию main http://en.wikipedia.org/wiki/Main_fu...programming%29

Hubbitus 04.09.2009 11:33

L0rd, и что? Там же ни слова нету где и как выделяется память под массив argv... Или про то что его нельзя менять... Или я плохо читал?

Borland 04.09.2009 11:49

Цитата:

Сообщение от Hubbitus (Сообщение 1672704)
где и как выделяется память под массив argv...

Вообще-то она выделяется ОС до запуска собственно программы, что вполне логично. И принадлежит эта память, соответственно, системе, а не твоей программе.
Соответственно, SIGABRT - совершенно нормальное явление. Винда в таком случае говорит "Программа выполнила недопустимую операцию и будет закрыта". И это правильно - насколь мне известно, любая ОС может позволить приложению читать "чужую" память и даже писать туда, но не перераспределять её.
Т.е. если так необходимо перераспределить память под argv - необходимо не "решать" задачу в лоб, а ковырять API процесса-владельца на предмет заставить выполнять соответствующую задачу его.
Это насколько я помню основы системного программирования...

Hubbitus 04.09.2009 13:31

Борланд, не уверен что память под argv выделяется до запуска программы... Не в CRT это разве должно делаться?

Цитата:

Сообщение от Borland (Сообщение 1672705)
И это правильно - насколь мне известно, любая ОС может позволить приложению читать "чужую" память и даже писать туда,

нет. Для непривелигированных приложений это не возможно, будет ошибка segmentation failed, в виндах что-то вроде ошибки "Память по адресу 0xFFFFFF" не может быть read (меня всегда веселило такое сообщение).

Цитата:

Сообщение от Borland (Сообщение 1672705)
Т.е. если так необходимо перераспределить память под argv - необходимо не "решать" задачу в лоб, а ковырять API процесса-владельца на предмет заставить выполнять соответствующую задачу его.

Да нету процесса владельца-то (не, ну то-есть есть конечно, но стандартный шелл юзера скажем, не программный вроде exec). Хорошо, другой вопрос, как вариант решения - я копирую этот массив (не перераспределяю текущий), манипулирую с копией, а потом, просто изменяю указатель argv чтобы он указывал в новое место. Это-то изменение указателя будет легитимно?

L0rd 04.09.2009 20:03

Цитата:

Сообщение от Hubbitus (Сообщение 1672715)
Борланд, не уверен что память под argv выделяется до запуска программы... Не в CRT это разве должно делаться?

Вот как все происходит. Точка входа в любое приложение - библиотечная функция mainCRTStartup() - она считывает указатель на командную строку запускаемого приложения, выполняет всю необходимую инициализацию, формирует переменные argc и argv, а затем вызывает нашу функцию main(). Откуда взялась эта память - была ли она выделена в функции создании процесса или выделена из "кучи" процесса - мы не знаем и знать не должны.
В любом случае, функцию realloc можно вызывать только, если по заданному указателю уже была выделена память с помощью malloc/realloc - и никак иначе. А доступ на запись в переменные argc м argv просто-напросто может быть блокирован самой библиотекой CRT - хотя бы для того, чтобы пользователь не мог ничего испортить внутри самой библиотеки CRT.


Цитата:

Сообщение от Hubbitus (Сообщение 1672715)
я копирую этот массив (не перераспределяю текущий), манипулирую с копией, а потом, просто изменяю указатель argv чтобы он указывал в новое место. Это-то изменение указателя будет легитимно?

Опять же, смотря в каком контексте - если внутри программы есть код, который обрабатывает командную строчку (читая из argc и argv) - то подмена указателей проходит. Даже если, что-то опять пойдет не так - например, при завершении программы - можно просто сохранить старое значение указателя и восстановить перед выходом.

Hubbitus 05.09.2009 11:13

Цитата:

Сообщение от L0rd (Сообщение 1672764)
Вот как все происходит. Точка входа в любое приложение - библиотечная функция mainCRTStartup() - она считывает указатель на командную строку запускаемого приложения, выполняет всю необходимую инициализацию, формирует переменные argc и argv, а затем вызывает нашу функцию main().

Ну вот это уже ближе к телу, приблизительно так я себе и представлял... Теперь, подскажите еще какой библиотеки эта самая функция mainCRTStartup()? Было бы логично предположить что glibc, но я грепнул его сурсы - нету. Посмотрел также в ядре... Где искать-то ее исходник?


Цитата:

Сообщение от L0rd (Сообщение 1672764)
Опять же, смотря в каком контексте - если внутри программы есть код, который обрабатывает командную строчку (читая из argc и argv) - то подмена указателей проходит.

Собственно для этого я и собираюсь его подменить, т.к. идет сопряжение с Сишным АПИ, и мне именно в argv надо запихать свой аргумент. И я больше спрашивал не подходит ли, это я предположил что подойдет сам, я спрашиваю, можно ли, менять этот указатель? Нет ли у него опять какого-то статуса особого? Я думал что и просто перераспределить память его можно...

Цитата:

Сообщение от L0rd (Сообщение 1672764)
Даже если, что-то опять пойдет не так - например, при завершении программы - можно просто сохранить старое значение указателя и восстановить перед выходом.

Не очень понял что пойдет не так? Тут конечно может быть утечка памяти (ну я же не могу высвободить память истинного argv как выяснилось), но Вы правы, именно к концу программы, и это получается уже совершенно не критично - по завершению вся память полюбому вернется системе. Или нет? Для чего еще я должен был бы восстанавливать этот указатель в прежнее значение?

Hubbitus 05.09.2009 13:33

Хм, ну в общем поковырялся немного с дебаггером.
Нету никакого mainCRTStartup()! Упоминания его есть только в mingw компиляторе, то есть в кросс-компилере под винду. Теперь что касается реального положения вещей, раз уж все оказалось не так, как описал L0rd, выше, если вдруг кому интересно (я пишу частный случай про изначально указанный GCC-4.4.1, glibc-2.10.1-4.i586, Linux Kernel - 2.6.31 на своей системе, не будучи специалистом в этой теме совершенно не претендую на истину этого на любой другой ОС или компонентах): Соответственно main вызывается из функции __libc_start_main.

Аргументы argc и argv инициализируются вот как-то так: https://www.codeblog.org/viewsrc/gli...bp-start.h#l28 проходя через "магические" вызовы __preinit_array_start(), __preinit_array_end(), __init_array_start(), __init_array_end(), __fini_array_start(), __fini_array_end(), которые в свою очередь формируются линкером, согласно комментам. Вызываются они из __libc_csu_init, ссылка на который передается __libc_start_main как параметр init.

Возвращаясь к начальной проблеме, получается что argv выделяется при помощи alloca, а не стандартных malloc, calloc и видимо, действительно не может быть ими перераспределен.


Часовой пояс GMT +4, время: 00:10.

Powered by vBulletin® Version 3.8.5
Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.