LINUX.ORG.RU

Глупый вопрос по С

 


0

1

Почему подобный код работает

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


int main() {
	int *array = malloc(1 * sizeof(int));

	array[0] = 11;
	array[1] = 22;
	array[2] = 33;

	printf("%d\n", array[2]);
}

? Ведь мы выделили только один элемент размера int.

valgrind --leak-check=full ./test

нарушение памяти есть, последствий пока просто не увидел.

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

int main() {
    int i;
    int *array1 = malloc(1 * sizeof(int));
    int *array2 = malloc(1 * sizeof(int));

    array2[0] = 0;
    for(i=0;i<100;i++)
      array1[i] = i;

    printf("%d\n", array1[2]);
    printf("%d\n", array2[0]);
}

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

Кастование void pointer в int pointer ничего не решает:

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


int main() {
	int *array = (int*)malloc(1 * sizeof(int));

	array[0] = 11;
	array[1] = 22;
	array[2] = 33;

	printf("%d\n", array[2]);
}
./array  
33
flareguner
() автор топика
Ответ на: комментарий от flareguner

А, я понял, они размещаются в памяти за выделенной областью, потому и не сегфолтится. В случае выделения 3 * sizeof(int) valgrind не ругается.

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

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

Deleted
()

Если в двух словах: malloc memory alignment

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

Случайно там не оказалось ничего важного, поэтому мы не наблюдаем падения.

Это меня и ввело в заблуждение :) Всё, теперь я разобрался. Что-то питон меня избаловал, стал я забывать основы.

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

Так даже веселее:

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

int
main()
{
        int *a = malloc(sizeof(int));
        int *b = malloc(sizeof(int));
        int i;

        for (i = 0; i < 0xff; i++)
                a[i] = 0xdeadbeef;

        printf("%p %p, delta %d\n", a, b, b - a);
        printf("%x\n", b[0]);

        return 0;
}

beastie ★★★★★
()

Segmentation fault происходит только при обращении к адресу памяти, в который не отображено для текущего процесса никаких данных. Ты пытаешься писать по адресам, которые оказались валидные. А почему - просто так звезды сложились. На другом ядре/libc/компиляторе можешь получить другой результат.

m0rph ★★★★★
()

Почему подобный код работает

Потому что школота не знает про -Wall и -Werror

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

Да-да-да, протестируй сперва эти опции.

anonymous
()

привилегии на чтение/запись памяти выставляются постранично, следовательно имея например 4кб страницы памяти и сделав:

char * ptr = malloc(1);

ptr = ptr & ~(4096-1);

можно читать/писать на протяжени ptr[0] до ptr[4096-1], правда там могут быть важные данные, однако при самом чтении/записи прога гарантированно будет жить

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

А то! Если не будет, он закачает прогу себе обратно, а тебе вернет твои деньги (0 руб. 0 коп.).

anonymous
()

Почему подобный код работает

этот быдлокод не работает. Это UB. В любой момент может случится что угодно. Например, ты можешь умереть, если кому-то его покажешь ☺

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

сегодня какой-то особый праздник у быдлокодеров? А я?! 4го апреля принято писать и всем показывать, какую НЁХ они умеют? Тогда мой любимый пример:

int x = 17;
return x-- - --x;

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

А почему - просто так звезды сложились.

скорее выравнивание виновато - malloc дырки между маленькими блоками оставляет. Хотя может и звёзды.

drBatty ★★
()

Ведь мы выделили только один элемент размера int.

Ты используешь указатель, а не «выделяешь». Выделяет системный вызов. Причём в пространстве твоего процесса.

ЕМНИП про такие грабли в первую очередь твердят все руководства, начиная с апологетов.

Анонимус выше код привёл.

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

Потому что школота не знает про -Wall

facepalm

а есть опция -Wastral-fix ?

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

можно читать/писать на протяжени ptr[0] до ptr[4096-1], правда там могут быть важные данные, однако при самом чтении/записи прога гарантированно будет жить

точнее, этот быдлокод будет работать как угодно, а ядро не сможет его за это грохнуть.

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

это не быдлокод, а пример.я тебе про то и пишу, при чтении и записи по этим адресам page fault'a не будет, как поведет себя прога дальше - неизвестно

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

я тебе про то и пишу, при чтении и записи по этим адресам page fault'a не будет, как поведет себя прога дальше - неизвестно

ты не мне пишешь, а ТСу. Я это и так знаю. А вот он (возможно) поймёт тебя неправильно.

drBatty ★★
()

http://www.dedoimedo.com/computers/gdb.html читать начиная с «Not so simple example»

Если кратко там - есть пустой кусок памяти, в него и пишется, звёзды не причём. Ещё советую заменить в примере на malloc(0) :)

Longer
()

Свои темы не создаются, а тут вопрос про malloc.

Вопрос-1.
Имеется самодельный парсер (ниже минимальный код).
Для удобства написания парсера (с циклом) и легкой замены имен
полей на другие, имена полей заряжены в массив струтур s_val->mod.
После выпарсивания удобно работать с содержательными именами полей,
а не с индексами массива. Поэтому появились страшные конструкции
типа:

s_val->mod[0].val = &s_val->slon;[br]
и
*(s_val->mod[i].val) = v1;  // устанавливаем указатель на значение поля
Нет ли тут какой опасности?
Страшно, что потом я сам не разберусь, что тут написано, неговоря
о других.

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

struct s_val {
    char buf[1000];       // буфер значений

    struct mod {
        const char *key;  // имя поля
        char       **val; // указатель на указатель на значение поля
    } mod[4];

    // указатели на значения полей
    char *slon;
    char *tigr;
    char *koza;
};

//------------------ init_s_val --------------------------

void init_s_val(struct s_val *s_val)
{
    s_val->mod[0].key = "slon";
    s_val->mod[1].key = "tigr";
    s_val->mod[2].key = "koza";
    s_val->mod[3].key = NULL;     // признак конца массива

    s_val->mod[0].val = &s_val->slon;
    s_val->mod[1].val = &s_val->tigr;
    s_val->mod[2].val = &s_val->koza;

    return;
}

//------------------ main -------------------------------

int main()
{
    int i, len;
    struct s_val *s_val;
    char *v1;
    const char *str[] = {
        "slon-1",
        "tigr-1",
        NULL
    };

    s_val = (struct s_val*)malloc(sizeof(struct s_val));
    if(s_val == NULL){ printf("s_val = NULL\n"); exit(0);}
    init_s_val(s_val);

    // fork();

    // предварительное зануление значений
    s_val->buf[0] = 0;  // нулевой байт массива всегда равен нулю
    for(i=0; s_val->mod[i].key != NULL; i++){
        *(s_val->mod[i].val) = s_val->buf;
    }

    // парсер значений
    v1 = s_val->buf;  // на начало буфера значений
    for(i=0; s_val->mod[i].key != NULL; i++){
        if(str[i]==NULL) break;
        v1++;
        *(s_val->mod[i].val) = v1;  // устанавливаем указатель на значение поля
        len = strlen(str[i]);
        strncpy(v1, str[i], len);
        v1 = v1 + len;
        *v1=0;
    }

    printf("\n");
    printf("slon=\"%s\"\n", s_val->slon);
    printf("tigr=\"%s\"\n", s_val->tigr);
    printf("koza=\"%s\"\n", s_val->koza);
    printf("\n");

    exit(0);
}

Выдача:

slon="slon-1"
tigr="tigr-1"
koza=""

Вопрос-2.
Что случится, если после инициализации структуры размножить процессы
fork-ом? Память, выделенная malloc-ом, что с ней будет в дочернем
процессе?

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

1. опасности нет. 2. посмотри как работает этот код. запусти на нем valgrind. закомментируй любой из free и ещё раз запусти valgrind. подумай о произошедшем.

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

int main()
{
  char* ptr = malloc(1);
  ptr[0] = 1;
  if(fork()!=0)
  {
    sleep(1);
    printf("parent: ptr=%x\n", ptr[0]);
    ptr[0] = 2;
    printf("parent: ptr=%x\n", ptr[0]);
    sleep(2);
    free(ptr);
  }
  else
  {
    ptr[0] = 3;
    printf("child: 1.ptr=%x\n", ptr[0]);
    sleep(2);
    printf("child: 2.ptr=%x\n", ptr[0]);
    free(ptr);
  }
}

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

по коду:

  • поменяй местами первый и второй элементы в «const char *str[]»,
  • посмотри что значит конструкция вида:
    typedef struct s_val t_val;
    
  • лучше для данной задачи использовать массив структур вида:
    struct s_val
    {
      const char *key;
      char *value;
    };
    
    и для каждой из них вызывать malloc с нужным размером строки, куда будет записываться найденная строка. по умолчанию нулевой указатель. или указатель на пустую строку.
anonymous
()
Ответ на: комментарий от anonymous

Получил выдачу:

child: 1.ptr=3
parent: ptr=1
parent: ptr=2
child: 2.ptr=3
valgrind я не использую. Но и так понятно - malloc-ную память
копирует в дочерний, да еще и указатели работают, как если
выделять память в каждом процессе отдельно. Догадываюсь, что
и free будет освобождать память своего процесса.

Я и свой пример сделал с fork-ом, получил:

slon-2="slon-2"
tigr-2="tigr-2"
koza-2=""

slon-2="slon-2"
tigr-2="tigr-2"
koza-2=""

$
slon-1="slon-1"
tigr-1="tigr-1"
koza-1=""


slon-1="slon-1"
tigr-1="tigr-1"
koza-1=""
Стало быть тоже работает. А почему по два раза печатает - не пойму.
Знаю, что от библиотечных ввода-вывода всякие пакости быают, но всё
равно не пойму.

anonymous пишет:
=поменяй местами первый и второй элементы в «const char *str[]»,=
Не считается. Код минимальный, а настоящий парсер выпарсивает в любом
порядке и повторяющиеся поля.

Еще anonymous пишет:

typedef struct s_val t_val;
Но другой anonymous ранее рекомендовал не злоупотреблять типизацией,
я и не злоупотребляю. Вроде бы пользы от этого нет.

Еще anonymous пишет:
=лучше для данной задачи использовать массив структур вида:

struct s_val
{
  const char *key;
  char *value;
};
и для каждой из них вызывать malloc с нужным размером строки=

Понятно. Так я уже делал. Но уж больно много возни с malloc и free.
Поскольку всё сообщение ограниченной длины до 4 кб, а значения
полей произвольной длины, то удобно задать весь буфер 4 кб, и
в него всё войдет.
Кстати, один из anonymous-ов (сейчас не найти) рекомендовал
небольшие структуры выделять цельно, а не размазывать по частям,
и мне это нравится.

А может сделать с объединением:

enum mod_e {
    slon,
    tigr,
    koza
};
printf("slon=\"%s\"\n", s_val->value[mod_e.slon]);
Тоже, конешно, смотрится страшненько.

Повторю самый важный вывод:
fork копирует malloc-ную память в дочерний, да еще и указатели
работают, как если выделять память в каждом процессе отдельно

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

UB, об этом четко в стандарте сказано.

int a[1]; a[1]=1; это тоже UB.

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

Даёшь оператор предела в тред!

int i = 10;

так не интересно. выдаст 10 9 8 7 6 5 4 3 2 1 0

давай double сделаем!

drBatty ★★
()

На самом деле.

Запомнить надо одну вещь - ничего, никогда и никакая память не выделяется во всяких маллоках, а малок сам говно и создан для переносимости и для неосиляторов.

Тут уже кто-то писал более-менее внятное объяснение, но так уж и быть.

Давай возьмём твою портянку:

	int *array = malloc(10000000000000000);// тут ничего не выделяется.

	array[0] = 11;//тут "выделяется", причем выделяется, как уже говорили выше страница, а это в тёом случае 4096 елементов.
	array[1] = 22;//ничего не выделяется
	array[2] = 33;//и тут.
        ...//и тут тоже.
        array[4096] = 1384823974;//тут выделяется.

Потом, как уже говорили есть ещё триксы связанные с выравниванием и ущербным устройством малока - у тебя какраз таки тот случай. Судя по указателям он жрёт 16байт для фри до указателя, потом идут твои «4байта», потом ты можешь впихнуть ещё 3 инта, а дальше уже следующий малок их возможно затрёт, а возможно и нет.

Глядеть в глибц, чтобы узнать как точно работает это говно мне лень, да и ужасы там.

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

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

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

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

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

И какая разница, malloc(3) выделяет, или там ::new? Они одинаково работают, насколько я знаю.

drBatty ★★
()

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

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

А суть в том, что малокоnew работают на своём уровне и всех их ограничения, кроме ограничений ОС( эли либо софтварное ограничение кода самоей ОС, либо ограничение хардварное, на которое ОС не может игнорировать) - есть их ограничения, на которые клал более нижний уровнеь( как процессор клал на ограничение твоей ОС - он работает, пока это не противоречит его логике).

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

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

адресам, которые оказались валидные

Мало того, по этим адресам libc не хранила никакой дополнительной инфы. А то приходилось наблюдать падения при вызове free() или даже exit()

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

А самая мякотка будет, если в конце добавить free(array2)

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

Прекращай разговаривать со своеим воображением. В топике нет ни слова про сегфолт.

anonymous
()

наверно, потому-что малок выравнивает память по блокам

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

Мне оператор «slides to» нравится больше:

int x = 10;
while (x --\
            \
             \
              \
               > 0) 
     printf("%d ", x);
anonymous
()
Ответ на: комментарий от superhackkiller1997

фигня у тебя какая-то в голове.

IRL всё проще: есть уровень ядра, есть уровень приложения. Есть терминалы отсюда → туда, и обратно. Терминал «в ядро» это ::new(malloc), именно им мы(прикладные кодеры) заготавливаем память. Говорим ядру, сколько мы хотим заюзать. Ядро и заготавливает как оно нам надо. IRL на сегодня ядро заготовит сколько угодно памяти, но это не значит, что оно сразу нам даст её физически. Не даст. И не факт, что заготовит с точностью до байта, IRL больше заготавливает. Потому быдлокод в первом посте и работает. А вот сегфолт, это такой терминал обратно, из ядра. Если мы пытаемся заюзать память, которую не заготовили.

int main() {
	int *a = malloc(5);
	a[5]++;
	//a[100500]++;
	return 0;
}

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

А если не раскомментировать, сегфолта не будет, хотя a[5] тоже не заготовленно. У ядра просто нет времени заниматься такими мелочами, да и невыгодно выделять такие маленькие кусочки памяти в 5 байт. Потому оно выделяет больше. Как минимум 1 страницу, которая у меня 4К.

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

Прекращай разговаривать со своеим воображением. В топике нет ни слова про сегфолт.

прочитай первый пост.

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

Как минимум 1 страницу, которая у меня 4К.

А я себе твой тест модифицировал:

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

int main() {
	char *a = malloc(1);
	int N = 135000;
	int P = getpagesize();
	while(1){
		a[N]++;
		printf("a[%d] = %d (PS = %d)\n", N, a[N++], P);
	}
	return 0;
}

Почему-то каждый раз сдыхает четко на 135153:

a[135152] = 1 (PS = 4096)
Ошибка сегментирования (core dumped)

А размер страницы 4k… Фигня какая-то!

Anon
()

У тебя простой buffer overrun. Пропусти свою программу через valgrind, он тебе сразу покажет, что к чему.

На строку array[1] = 22:

==7699== Invalid write of size 4
==7699==    at 0x80484FF: main (overrun.c:9)
==7699==  Address 0x420902c is 0 bytes after a block of size 4 alloc'd

На строку printf(«%d\n», array[2]):

==7699== Invalid read of size 4
==7699==    at 0x8048513: main (overrun.c:12)
==7699==  Address 0x4209030 is 4 bytes after a block of size 4 alloc'd

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

А я себе твой тест модифицировал:

мой компилятор просил тебе передать, что ты быдлокодер:

mm2.c: В функции «main»:
mm2.c:11:42: предупреждение: операция над «N» может дать неопределенный результат [-Wsequence-point]
   printf("a[%d] = %d (PS = %d)\n", N, a[N++], P);
                                          ^

Почему-то каждый раз сдыхает четко на 135153:

у меня тоже. Особенность реализации malloc в текущем ядре/glibc.

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