Re: Сішка, непередбачувана поведінка
Q-bart написав:Як це пояснювати можна? Або гуглити, бо щось нічого толкового не знайшов))
Ось хороша стаття: Неочевидные особенности вещественных чисел, щоправда російською.
О, дякую! Перегляну!
Ви не увійшли. Будь ласка, увійдіть або зареєструйтесь.
Ласкаво просимо вас на україномовний форум з програмування, веб-дизайну, SEO та всього пов'язаного з інтернетом та комп'ютерами.
Будемо вдячні, якщо ви поділитись посиланням на Replace.org.ua на інших ресурсах.
Для того щоб створювати теми та надсилати повідомлення вам потрібно Зареєструватись.
Український форум програмістів → C++ → Сішка, непередбачувана поведінка
Q-bart написав:Як це пояснювати можна? Або гуглити, бо щось нічого толкового не знайшов))
Ось хороша стаття: Неочевидные особенности вещественных чисел, щоправда російською.
О, дякую! Перегляну!
І знову я тут
Сішка цікава. РОзкажіть мені будь ласка, чому якщо я виділяю памяті під 4 елементи масиву, я отримую масив з довжиною 2?
What`s the hell?
#include "stdio.h"
#include "malloc.h"
int main(int argc, char const *argv[])
{
int size = 4;
int * array = (int * )malloc(size * sizeof(int));
printf("Len of array: %li \n", sizeof(array) / sizeof(int));
return 0;
}
OUT:
Len of array: 2
Тому, що маєте 64-бітну машину систему, мали б 32-бітну, було б 1
Це якщо коротко (другу сторону цього «коротко» описав 0xDADA11C7).
Якщо довго — забудьте поширену помилку «ім'я масиву еквівалентне вказівнику на перший його елемент»
І тим більше ніколи не вважайте, що «вказівник на перший елемент масиву еквівалентний його…» ммм… designator
Бо в операторі sizeof можна використовувати лише оцей designator, яким є ім'я масиву в нотації type array[size].
Для int *array оператор sizeof дав розмір вказівника, 8 байтів для 64-бітної системи, після дялення на розмір int (4) вийшло оті 2.
Воно так не робе, натомість робе так:
int a[17]; n = sizeof(a)/sizeof(a[0]);
Ой! Не паше)
UPD.
Вірніше не паше з вказівником
Тому, що маєте 64-бітну машину систему, мали б 32-бітну, було б 1
Це якщо коротко (другу сторону цього «коротко» описав 0xDADA11C7).Якщо довго — забудьте поширену помилку «ім'я масиву еквівалентне вказівнику на перший його елемент»
І тим більше ніколи не вважайте, що «вказівник на перший елемент масиву еквівалентний його…» ммм… designator
Бо в операторі sizeof можна використовувати лише оцей designator, яким є ім'я масиву в нотації type array[size].
Для int *array оператор sizeof дав розмір вказівника, 8 байтів для 64-бітної системи, після дялення на розмір int (4) вийшло оті 2.
А як отримати мені там 4?
p.s.
Except when it is the operand of the sizeof operator or the unary & operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue. If the array object has register storage class, the behavior is undefined.
Тобто ім'я масиву автоматично приводиться до вказівника на його перший елемент практично в усіх ситуаціях, тому складається враження, що воно еквівалентне до цього вказівника і тому доволі часто пробують цю (відсутню) «еквівалентність» повернути в інший бік і запхати вказівник на я_знаю_що_там_масив під sizeof.
Якісь тут пояснення такі що навіть мене заплутали.
Вказівник на масив займає вдвічі більше ніж int, бо це просто вказівник і йому треба стільки байт скільки треба аби записати адресу. C - не python, тут масиви про свою довжину не знають, її програміст має записати десь окремо. (або не записувати, а просто після останнього елемента чимось позначити кінець, як от в сішних рядках
А як отримати мені там 4?
Тільки так, яка показав 0xDADA11C7.
Зі «справжнім» масивом.
Бо таки ж
Вірніше не паше з вказівником
бо воно показує розмір вказівника.
Оця та ваша сішка! Нащо вона здалась, був у мене python класний, треба було мені йти в той універ!
P.S. Все ок. Пішов вчитись, Треба було поскиглиити
Q-bart написав:А як отримати мені там 4?
Тільки так, яка показав 0xDADA11C7.
Зі «справжнім» масивом.
Ніколи не кажи ніколи™
Можна ще зі вказівником на справжній масив :-)
С99 і пізніше для a[не-константа]
Якось так:
#include <stdio.h>
#include <stdlib.h>
void foo(int i)
{
int a[i];
int n = sizeof(a)/sizeof(a[0]);
printf("i=%d, n=%d\n", i, n);
}
void moo(int i)
{
// тут a вказівник не на int, а на масив int-ів
int (*a)[i] = malloc(i * sizeof(int));
int n = sizeof(*a)/sizeof((*a)[0]);
printf("i=%d, n=%d\n", i, n);
if (i > 1) {
(*a)[1] = 3;
printf("(*a)[1] = %d\n", (*a)[1]);
}
free(a);
}
int main()
{
foo(12);
foo(15);
moo(12);
moo(15);
}
C не нав'язує майже нічого над те, що ви робите. Ви виділяєте пам'ять? Чудово, тепер - ваша і тільки ваша відповідальність стежити, щоб ваш код не виліз за її межі, і щоб ви її повернули, коли вона буде вам не потрібна. Ніяких чудес немає, malloc повертає вказівник і все. Більше нічого. Розміру немає, типу немає. Просто шмат пам'яті. Загубили вказівник - пам'ять тече. Переплутали, що хотіли в неї записати - втратили дані. Вилізли за межі - що завгодно, від читання 0 до падіння програми. Ніяких способів дізнатися, скільки раніше було виділено пам'яті, немає, окрім написаного вами обліку під час виділення. Так що
int * array = (int * )malloc(size * sizeof(int));
printf("Len of array: %li \n",/*єдиний спосіб дізнатися, скільки там int-ів*/ size);
Тада, розумію що тут все низькорівнево)
А що робить оце?
(int * )
Бо оце:
malloc(size * sizeof(int))
Це зрозуміло. Передаємо в функцію яку к-сть бітів треба мені виділити. А ось те що перед ним?
Перетворення типів в C позначається так:
(у_що_перетворюємо)що перетворюємо.
Наприклад, (int)5.3 == 5.
Трохи уточню, новачки в C часто пропускають цей момент з масивами. Хай ми маємо:
int a[10];//масив
int *pa=a;//вказівник
int b;//число
a майже завжди (крім sizeof) означає те саме. що й &a[0] - вказівник на початок пам'яті. І a[ b] - це те саме, що *(a+b). Тобто щоб отримати адресу b-го елементу з a, компілятор обчислює (int*)((int)a+b*sizeof(int)) (звісно, я тут розписую максимально детально, а у компілятора все це оптимізується і зайві дії викидаються). Зокрема, pa[ b]==a[ b], pa+b==a+b, p+1==a+1, інкременти і віднімання теж працюють так само. Звісно, інкрементувати a неможливо, a - вказівник на фіксоване місце в пам'яті, константа; але pa - легко.
int a[10];//масив int *pa=a;//вказівник int b;//число
a майже завжди (крім sizeof) означає те саме. що й &a[0] - вказівник на початок пам'яті.
Ще крім &, тобто &a означатиме не &&a[0], а вказівник саме на масив (як і sizeof(a) показує розмір масиву), а змінна сумісного для даного прикладу типу int (*paa)[10] = &a; при інкременті відступатиме у пам'яті на 10 інтів (розмір масиву), а не на 1.
Оце класно! Дякую, за пояснення!
Насправді, мене збила з толку зірочка у (int *) - бо конвертування типів - я знаю))
І ще одне питання)
Мені треба збирати в масив структури. Звісно ж, що масив динамічний (ми не в садіку, щоб з статичним бавитись). Поскладав кілька структур в масив, обробив дані, і пішов на нову ітерацію циклу => мені знову туди треба поскладати об'єкти (не знаю як вірно буде) структур. Але ясно ж що перед цим треба почистити той масив.
while (1==1) {
struct rib *incidental = malloc(0); // оголошуємо масив об'єктів структур (поки нуль)
/* Get all incidental ribs to used vertices */
// щось робимо ........
// бачимо що нам треба додати в масив новий елемент => додаєм до вже виділеної
// пам'яті трохи місця на один елемент. Додаємо сам елемент.........
incidental = (struct rib*)realloc(incidental, sizeof(struct rib));
incidental[i].from = i+1;
// Далі ще щось робимо. Але тут цикл дійшов до закінчення ітерації. Нам треба звільнити це все від
мотлоху.
free(incidental);
// пішли на наступну ітерацію і отримали *** Error in `./main': free(): invalid next size (fast):
// Якщо без free, то матюкається в такому випадку на realloc
}
Коли не треба йти на другу ітерацію, то все класно працює. Тому звідси роблю виснвок, що десь в мене переповнення пам'яті, як казав koala
Було б добре якби ви цикл до коду додали бо не дуже зрозуміло
В будьякому випадку, realloc не додає пам'яті а перевиділяє її. Тобто
void *p = realloc(NULL, 1);
void *p1 = realloc(p, 1); // p1 вказує на 1 байт (не на два)
Ви плутаєте проголошення і виділення пам'яті. Для змінних в купі це різні дії:
struct rib *incidental;//проголошення вказівника
incidental=(struct rib *)malloc(10*sizeof(struct rib));//виділення пам'яті, на яку тепер вказує наш вказівник
malloc(0), наскільки я пам'ятаю, implementation defined, тобто може повертати різні речі залежно від компілятора. Краще так не робити, а писати просто NULL.
Ну і крім усього іншого, malloc відносно повільний, ним краще користуватися не надто часто - тобто запитувати треба із запасом.
Втім, цей код не падає: https://ideone.com/pOi6Bi - а отже, ви викинули якусь суттєву частину, в якій і була помилка. Будь ласка, стежте, що надаєте саме код, який ілюструє ваше питання, а не іншу його частину. Телепатів тут нема.
До речі, а i у вас завжди 0? Якщо ні, то ви можете випадково переписувати якусь системну структуру даних, що й призводить до цієї помилки.