LINUX.ORG.RU

Помогите разобраться с malloc СИ

 ,


0

3

Здравствуйте.

Есть функция в которой есть две переменные для массивов - buff и trans_rate:

void rate_coin(char *coin)
 {
    FILE *rat_coi;
    char *buff = (char*) malloc(128 * sizeof(char));
    snprintf(buff, (int)strlen(coin) + 59, "%s%s%s", "curl https://api.coinmarketcap.com/v1/ticker/", coin, "/ 2>/dev/null"); 
    printf("Coinmarketcap: %s\n", buff);
    rat_coi = popen(buff, "r"); 
    if(rat_coi == NULL) error_log("rat_coi!");

    char trans_rate[128] = {0,};
    //char *trans_rate = (char*) malloc(128 * sizeof(char));
    int count = 0;
    strcat(trans_rate, "RATE COIN\n");
    memset(buff, 0, 128);
    while(fgets(buff, 126, rat_coi) != NULL)
     {
       count++;
       if(count > 8) break;
       if(count == 4 || count == 7 || count == 8)
        {
          char *pch = strtok(buff, " ,\"");
          while(pch != NULL)
           {
             char *ptr = strchr(pch, ':');
             if(ptr!=NULL) *ptr = ' ';
             strcat(trans_rate, pch);
             pch = strtok(NULL, " ,\"");
           }
        }
     }

    trans_rate[strlen(trans_rate) - 1] = 0;
    SendMessage(glob_chat_id, trans_rate);
    free(buff);
    printf("%s\n\n", trans_rate);
    //free(trans_rate);
 }

Для переменной buff делаю malloc, использую её в функции snprintf(buff, ...). После этого очищаю buff с помощью memset(buff,...), использую её же в функции while(fgets(buff,...) и в конце освобождаю память - free(buff);.

Для переменной trans_rate выделяю нужное кол-во байт и использую её.

Всё работает хорошо.

Если же для переменной trans_rate тоже делать malloc (строчка закомментирована) вместо char trans_rate[128] = {0,};, то прога начинает вести себя непредсказуемо, как будто перекрываются массивы (в printf попадают непонятные символы):

h�"�WRATE COIN

Объясните пожалуйста, почему в данном случае для buff malloc работает, а для trans_rate нет?


char *trans_rate = (char*) malloc(128 * sizeof(char));
strcat(trans_rate, "RATE COIN\n");
 

Ну вообще-то в первом аргументе strcat должна быть корректная C-строка. Читай

И кстати, твой sizeof(char) всегда равен 1

Crocodoom ★★★★★
()

Когда написано

char *trans_rate = (char*) malloc(128 * sizeof(char));
trans_rate уже может что-то содержать, видимо это и есть таинственные символы. Можно проверить, сразу сделав printf. Потом strcat добавляет к этим символам RATE COIN.

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

А откуда оно там взялось?

«Строка» в Си — это последовательность байт в памяти, завершаемая нулевым байтом.

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

Если хочешь выделять обнулённые данные, используй calloc()

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

Если хочешь выделять обнулённые данные, используй calloc()

Спасибо, как раз прочитал про calloc(), использовал его и всё работает.)

Ну а в целом, скажите как будет правильней поступать, использовать динамическое выделение памяти или тупо везде использовать char trans_rate[128] = {0,}; ?

Это ведь крохотный объём (для современных компов), чтоб переживать из-за него?

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

А откуда оно там взялось?

Оно там было ещё до того, как эта память была выделена. Поэтому обычно делают memset, чтобы забить всё нулями (или чем угодно ещё), либо используют calloc, который делает это сам, либо, в случае со строками, ставят нуль, где надо, а потом пишут нужные данные, затирая всё, что было и не вылезая за размеры буфера. Что из этого использовать — решать тебе.


"%s%s%s", "curl https://api.coinmarketcap.com/v1/ticker/", coin, "/ 2>/dev/null"

Зачем так сложно? Ведь можно проще: "curl https://api.coinmarketcap.com/v1/ticker/%s/ 2>/dev/null", coin

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

Зачем так сложно? Ведь можно проще:

Да, чего-то перемудрил.) Спасибо, сделал как Вы предложили.

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

как будет правильней поступать

Здесь нет «правильно»/«не правильно», есть лишь задача и способы её решения. Выделять на стеке быстрее, но если увлечься, получишь переполнение. Если выделять в куче — надо следить за очисткой (каждому malloc/calloc/whateveralloc должен соответствовать free!), иначе получишь утечку памяти. Как я понял, задача не предполагает особых требований, поэтому поступай, как нравится

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

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

Спасибо.

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

Когда предварительная инициализация данных не нужна. Например, выделяем буфер под какие-то данные, которые придут по сети либо от пользователя: в этом случае нам наплевать, что там было, ибо чтения до записи не происходит. В итоге экономим сколько-то тактов, что на тяжёлых задачах даёт прирост производительности

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

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

То есть, Вы имеете ввиду, что даже если там и есть мусор, то он затрётся новыми данными? Или я не правильно понял?

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

Просто замени первое вхождение strcat на strcpy.

Да, так работает с malloc. Я понял причину, спасибо!

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

даже если там и есть мусор, то он затрётся новыми данными?

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

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

Совершенно верно

Благодарствую!

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

надо забивать чем-то

То есть будет равносильно:

char *trans_rate = (char*) calloc(128, sizeof(char));

и

char *trans_rate = (char*) malloc(128 * sizeof(char));
memset(trans_rate, 0, 128);
stD
() автор топика
Ответ на: комментарий от stD

При выделении маленьких кусков памяти — да. При выделении больших кусков память запрашивается непосредственно у ядра, а оно сразу даёт обнулённые страницы. Т.е. оверхеда на memset() быть не должно.

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

В твоем случае достаточно

char *trans_rate = (char*) malloc(128 * sizeof(char));
*trans_rate = 0;
Ну или, как я уже говорил, в первом вхождении strcat не использовать.

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

При выделении маленьких кусков памяти — да. При выделении больших кусков память запрашивается непосредственно у ядра

А каковы эти размеры (маленький, большой) памяти и кто устанавливает эти границы?

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

Ну или, как я уже говорил, в первом вхождении strcat не использовать.

Да, strcat заменил на strcpy.

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

Ведро.

Кстати, где-то с C99 нет нужды malloc'ом выделять небольшой буфер. Если нужно выделить, скажем, char buf[N];, где N в районе килобайта-другого, можно так и написать:

    // получаем значение N
…
    char buf[N];
…

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

А каковы эти размеры (маленький, большой) памяти и кто устанавливает эти границы?

Implementation-specific.

Просто так совпало, что:

  • Операционная система обязана чистить страницы перед их выдачей приложениям. (Требование безопасности. Чтобы, например, процесс, подконтрольный злоумышленнику, не прочитал твои приватные ключи и пароли просто выделяя память и ища там нужную ему информацию в мусоре.)
  • Встретив запрос на «достаточно большой» блок реализация кучи просто запрашивает новую память у ядра.
Deleted
()
Ответ на: комментарий от Softwayer

К слову, strcat вообще увлекаться не стоит

Можно использовать strlcat().

В случае интенсивной работы со строками лучше задействовать какую-нибудь специализированную библиотеку: http://www.and.org/vstr/comparison

Deleted
()

char *buff = (char*) malloc(128 * sizeof(char));
snprintf(buff, (int)strlen(coin) + 59, «%s%s%s», «curl https://api.coinmarketcap.com/v1/ticker/", coin, »/ 2>/dev/null");

char* buff = (char*)malloc(128);
snprintf(buff, 128, "curl https://api.coinmarketcap.com/v1/ticker/%s/ 2>/dev/null", coin);



Дальше читать не стал, здоровья лишнего нет.

andreyu ★★★★★
()

Выброси Си лично для себя на помойку и учи нормальные языки погроммирования.
Форматирование у тебя в коде какое-то чересчур вольное.

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

При выделении маленьких кусков памяти — да.

Вот это не факт. Если маленький кусок выдаётся со свежей страницы, взятой у ядра, то аллокатору ничто не мешает не делать memset.

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

Форматирование у тебя в коде какое-то чересчур вольное

Да, есть такое дело), но я исправлюсь.

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

учи нормальные языки погроммирования

Бугога! Если С — не нормальный, то что же? Ассемблер что ли?

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

Бугога! Если С — не нормальный, то что же?

Видимо имеются ввиду языки типа ява и питон, на которых, кстати, в обозримом будущем будут писать только роботы-программисты. Ну это так, размышления.

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

С++. Вот же только недавно выяснили, что альтернативы ему нет. На нём можно и как на Си писать, для любителей извращений, и как на путоне — ляпота.

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

А на них и сейчас не люди, а роботы пишут.

anonymous
()

Для переменной buff делаю malloc, использую её в функции snprintf(buff, ...).

Это делаете кривовато. Если кто подсунет coin="?бла-бла-128-char-shell-code" то может сломать. И таки да, в «%s%s%s...» есть смысл, когда выходная строка формируется из нескольких, где каждую можно изменить отдельно, скажем при конфигурировании перед трансляцией.

#define CURL_PRG "curl"
#define COINMARKETCAP_URL "https://api.coinmarketcap.com/v1/ticker/"

size_t buff_sz = strlen(CURL_PRG) + 1 + strlen(COINMARKETCAP_URL) + strlen(coin) + strlen("/ 2>/dev/null") + 1;
char *buff = malloc(buff_sz);
snprintf(buff, buff_sz, "%s %s%s%s", CURL_PRG, COINMARKETCAP_URL, coin, "/ 2>/dev/null"); 

После этого очищаю buff с помощью memset(buff,...), использую её же в функции while(fgets(buff,...)

memset тут не нужен.

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

Это делаете кривовато. Если кто подсунет coin="?бла-бла-128-char-shell-code" то может сломать

Спасибо.

stD
() автор топика

глава из K&R про массивы и указатели

так же см приложение про r- и l-value

в закоментированной строчке ты адрессу( даже не по адрессу а буквально синониму адресса) пытаешься присвоить указатель который на память которая выделяется при результативном исполнении малока (которого нет ибо тебе ещё компилятор по рукам даёт за попытку тыкание пальцев в розетку)

---троль тоньше а?

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

Но ведь браузер, из которого ты это написал, написан на C++, а не на C! И GCC, которым ты конпелируешь свою сишечку, тоже переписали на C++! Я уже молчу про шланг, который изначально на C++ и уже обгоняет GCC, хотя намного моложе его.

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

Ты читать не умеешь? Я же сказал: мне С за глаза хватает! А что там на чем делали, насрать.

Я хоть и исхожу на говно от пхытона, portage ведь пользую!!!

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

Но ведь браузер, из которого ты это написал, написан на C++, а не на C!

Чисто в познавательных целях, а на чём написаны драйвера для клавиатуры, на которой Вы печатаете?

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

Вычитал сейчас, что после освобождения памяти - free(buff), нужно обнулить указатель - *buf = 0;.

Это действительно необходимо делать?

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

Вычитал сейчас, что после освобождения памяти - free(buff), нужно обнулить указатель - *buf = 0;.

Это действительно необходимо делать?

*buf = 0 — так ты обнуляешь не указатель, а ячейку памяти, на которую он указывает.

Так делать точно нельзя. Ты память при помощи free() отдал: всё, она не твоя. Читать-писать её больше не надо.

Указатель обнуляется так: buf = 0 (без звёздочки!). Делается это как раз для того, чтобы случайно не обратиться к памяти, которую ты отдал. Если дальше по коду ты случайно обратишься к *buf, забыв, что уже сделал free(), то:

  • Если ты не обнулил указатель, твоя память может разрушить структуры данных с непредсказуемыми результатами.
  • Если ты обнулил указатель, программа сразу упадёт, не успев накуролесить. (нулевая страница памяти всегда недоступна прикладному процессу, и ядро его убъёт)

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

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

На нём можно и как на Си писать, — но ­зачем, если уже есть Си? И для любителей извращений писать как на путоне — но зачем, если для извращенцев уже придумали путон?

Пофиксил, не благодари.

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

Парень, вот правда, возьми какой-нибудь серьёзный учебник и прочитай, если хочешь кодить.

Мне 40 лет и я бородат) (это правда), но речь не об этом. Я читаю книги, правда, ибо понимаю, что черпать знания лучше всего в них. Однако в процессе писанины программы невольно обращаешься к различным интернет-источникам (думаю многие так поступают). Вот и про обнуление прочёл на, вроде бы, популярном ресурсе - http://cppstudio.com/post/9088/ (ближе к концу статьи) , но как выясняется не слишком компетентном))).

Я это говорю не чтобы повыпендриваться, а ведь тебе же самому будет проще.

Я Вас прекрасно понимаю и поддерживаю.

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

Я читаю книги, правда, ибо понимаю, что черпать знания лучше всего в них

lol. см Ершов Программирование вторая грамотность

также годно Кавало Шартье История чтения в западном мире от Античности до наших дней

зы. знания лучше всего черпать из годного черпателю источника - лучший наставник лучше лучшей книги в разрезе темы в которой наставник реальне.

однако относительная дороговизна квалифицированных наставников относительно книг(после внедрения книгопечатанья )

---так что если вокруг тебя лучшим источником знания является книга - ээээ попробуй вместе с этой(-ими) книгой переместистся в место где и наставники наставникуют - форум оно конечно выше вероятность тут наствникования — как и личный опыт

ззы. ты троль всёж тоньше как тебе выше указали ужо.

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

Не обязательно забивать все нулями. Хватит trans_rate[0] = 0.

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

В самую точку. Драйвера для «железок» писать, прошивки микроконтроллеров, перекладывать байты — это задачи для Си. Описывать же сложные сущности, при этом сохраняя возможность выжимать максимум из hardware, на Си устанешь. Для какой железяки вы пишете прошивку? Судя по коду, зачем-то дергается curl: такое нормально выглядит только для bash, в Си сложно писать http-запросы или нет библиотек для этого? Скорость выполнения кода упрется в скорость выполнения сетевого запроса, какой прок от производительности Си? На питоне этот код напишется левой пяткой и уже работал бы.

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