Re: Вопрос по С

JERRY

В чем заключается разница между
char a[100];
и
int b[100];
Разница в том смысле, что a и b, как указатели на заранее зарезервированную область памяти, работают по разному?

peter1dav

У них же длина разная ... если мне не изменяют мои познания в С

1234554321

в размерах инт и чар?

1234554321

изменяют. может быть разной

peter1dav

Т.е. разной?
Имеешь в виду различные реализации Си?
И соответственно разные аппаратные платформы?

1234554321

угу

JERRY

Дело не в длине, уверяю. Считайте, что выхода за пределы массива 100% не может быть. Но в случае с char он тем не менее происходит.

1234554321

код в студию

peter1dav

Ну на пне враде как char это 1 байт, а инт толи 2 толи 4... скорее 2.

JERRY

Платформа Linux, компилятор gcc.

peter1dav

Платформа линукс еще ничего не говорит..... он же может быть на разных архитектурах стоять

peter1dav

И вообще... а - это указательна массив из 100 Char, а б - это указатель на массив из 100 int .
А у них длина как правило, а может и всегда разная !
Поэтому... а+1 и в+1 - это разные вещи... т.к. б+1 может быть равно а+2.ж
З.Ы. надеюсь не наврал

JERRY

Примерно так


char arr[1000];
void copy_arr(void* arr1, void* arr2, long size) {
long count;
unsigned long from = (unsigned long*) arr2;
unsigned long to = (unsigned long*) arr1
count = size / sizeof(long);
далее тупое копирование, алгоритм 100% рабочий, я его просто скопировал.
}
void do_something_bad(void* arr1) {
if (condition) {
copy_arr(arr, arr1, 1000);
}
}

JERRY

Дело не в длине, я размер заведомо достаточный ставил, все-равно для char падало. Единственное, причина не обязательно в С как таковом: ошибка была не в прикладной программе. Просто вдруг я чего то важного про С не знаю.

peter1dav

Ты ж спросил в чем разница... а не почему падает

1234554321

мне кажется дело в преобразовании в unsigned

peter1dav

Слушай... а это наормальноЮ, что в одном месте unsigned long а в другом просто long?

peter1dav

А в каком месте падает то?

rid2000

скорее 2.

Ты чего? Это было до революции...
Сейчас все на 32 разрядах(старье не трогать)...
Скоро, должны 64 разрядки пойти

1234554321

спасибо любимой Микрософт за Int_16, int_32, int_64

JERRY

в unsigned* преобразуются void*. А проблема в том, что при замене char на long все работает, а для char'а - page_fault (размер массива одинаковый). Причем адресная арифметика есть только в функции копирования, остальное только преобразования типов.

dberezhnoy

Используй вместо copy_arr стандартную функцию memcpy(void* to, void* from, unsigned int size).

peter1dav

Ну... я просто не помню.. на нормальных языках уже года 2 не писал..
все 1С с его

Для СчетчикЦикла По Условие Цикл
//Делаем что то
КонецЦикла;


1234554321

а ты компилируешь на no_opt? с компилятора станется прооптимизировать...

rid2000

Int_16, int_32, int_64

Это эмуляция...
Сам сайзоф(инт) = маш.слово...

1234554321

я знаю

rid2000

Я что-то код не понимаю...
Или там очень много ошибок...

peter1dav

Так вылетает то в "тупом" алгоритме копирования...?
Покажи его что ль

abrek

Всё-таки реальный фрагмент кода, который себя неправильно ведёт, поможет больше.

oleg701

unsigned long from

А там не unsigned long * from должно стоять?

peter1dav

unsigned long * from

А что такое From?
Dtlm (unsigned long *) - это просто приедение типов, т.к. в параметрах Void*

dberezhnoy

Один зоркий человек нашелся

rid2000



#include <fogoten.h> :)
template <class T>
void copy_arr( T *arr1, T *arr2, long size)
{
// memcopy or memcpy, poriadok arr ne pomnu
memcpy(arr1,arr2,size);
}

rid2000

count = size / sizeof(long);

JERRY

Не могу показать реальный код. См. ниже.
Падает при копировании в самом конце массива (причем не глючного, а из которого копируется) на границе двух страниц (дело происходит в ядре, а не в user mode). Функцию менять бессмысленно, поскольку а) эта работает для других типов б) она самая что ни на есть стандартная, я ее просто скопировал из другого места. Я просто думал, что дело в том, что я не знаю что-то о С. Все таки char[] это строка, может с ней какие то особые действия выполняются.

JERRY

считай это опечаткой

abrek

В ядре Linux есть memcpy, самому писать копирование блока вроде и незачем.
Кроме особенностей оптимизатора, что-то не приходит больше в голову других причин, почему может не работать.

dberezhnoy

1. У тебя точно написано

unsigned long* from = (unsigned long*) arr2;
unsigned long* to = (unsigned long*) arr1

а не

unsigned long from = (unsigned long*) arr2;
unsigned long to = (unsigned long*) arr1

2. Тебе точно надо изобретать велосипед? memcpy тебя не устраивает?
3. Как ты дальше исползуешь count?
4. char[] - это не строка. char[10] ничем сакральным кроме выделенной длинны в памяти не отличается от int[10].

JERRY

Функция копирования верная, я ее не менял, если интересно, то выглядит так


void copy_page(void* _to, void* _from, long count) {
unsigned long* to = (unsigned long*) _to;
unsigned long* from = (unsigned long*) _from;
count /= (sizeof(unsigned long*)*4)
do {
unsigned long a, b, c, d;
a = from[0];
b = from[1];
c = from[2];
d = from[3];
count--;
from += 4;
to[0] = a;
to[1] = b;
to[2] = c;
to[3] = d;
to += 4;
} while (count);
}


Я повторяю проблему. Все работает, если заменить char на long (с соответствующим изменением длины массива). С char падает. Тут дело или в каких то особенностях С или в чем то еще, а в чем именно даже предположить не могу.

abrek

Тебе надо понять, что происходит, или сделать, чтоб работало?

peter1dav

а зачем такой изврат?

JERRY

Что происходит. Чтобы работало нужно заменить char на long. Меня такая фигня очень смущает, если честно. Ведь это же не С++, где все что угодно может быть, а предсказуемый С. Но факт фактом char не работает вообще никак даже с намного большей длиной массива - возникает page fault. А его не должно быть в принципе, поскольку память заранее зарезрвирована.

JERRY

Это не от меня зависит. Так надо.

natali20

Приведи вариант с char (который не работает)

1234554321

так всё-таки, если скомпилировать без оптимизации, падает?

abrek

что-то ты избегаешь ответа на вопрос, чем не подходит memcpy, ну да ладно
что можно сделать:
1. откомпилировать этот кусок без оптимизации, останется ли проблема?
2. поискать отличия в ассемблерном коде, сгенерированном компилятором для char и для int
3. попробовать воспроизвести проблему на искусственном примере в user level, тогда ты сможешь показать полный фрагмент кода какому-нибудь гуру

JERRY

Он точно такой же только массив объявлен с char: char arr[1000]. Вполне возможно, что глюк в компиляторе или еще где, так что меня интересует только одно: не встречался ли кто то с похожей ситуацией, когда причина бага была в массиве char и исчезала, если использовать для массива другой тип.

JERRY

Я не могу сейчас ничего скомпилировать. Технических возможностей нет.
2 .10 : memcpy использовать не могу - я не должен испортить стек вызовами функций или локальными переменными. В примере это не так для простоты. Насчет посмотреть код в ассемблере - может и поможет, но я того ассемблера вообще не знаю, это не Intel и тулзы для просмотра не факт, что есть. Отключить оптимизацию - попробую, если она есть.

Dasar

на современном железе char[100] ровно в 4 раза меньше, чем int[100]
твоя функция copy_arr, копирует count * sizeof(long) байт = 4*count байт
Для того, чтобы версия с char работала также, как версия с int. надо увеличить размер массива char-ов в 4 раза по сравнению с int-овой версией (если плохо чувствуешь, что такое память, то увелить лучше в 10, не ошибешься).
т.е. если ты пишешь int a[1000] и все работает, то в версии с char-ом должно быть char a[4 * 1000];, чтобы тоже все работало.

Dasar

восколько раз ты делаешь массив с char-ами больше, чем с int-ами?

abrek

> я не должен испортить стек вызовами функций или локальными переменными
а unsigned long a, b, c, d разве не мешают?
> тулзы для просмотра не факт, что есть
генерировать: gcc -S
просматривать: less, vim, etc.

abrek

> не встречался ли кто то с похожей ситуацией
вообще встречался, оптимизатор char * и int * может по-разному обрабатывать
но там была проблема с aliasing, к твоему случаю никак не подходит вроде

JERRY

К сожалению, причина не в этом. Считай, что копируется ровно столько байтов (или даже меньше сколько в массиве char.

Dasar

к сожалению, по коду я этого не вижу

JERRY

Не портить стек в идеале, а не работает этот пример. -S учту, забыл об этом.
На самом деле, если не жалко свой Linux попробовать воспроизвести ошибку очень просто. Даже писать ничего почти не нужно - у меня падало при копировании 0-й страницы в свой буфер, причем, что прикольно, если считать страницу с диска в тот же буфер, то все было ОК. Так что проблема, возможно, действительно в aliasинге.

JERRY

Поверь мне на слово. Размер массива я проверил в первую очередь и даже увеличил его в несколько раз (в 8 но это не помоголо, поскольку размер не зависел от long, а был заранее зафиксирован. Т.е. мне нужно было 1000 байт, я написал char arr[8000].

abrek

> падало при копировании 0-й страницы в свой буфер
не понял
то есть _from == (void *) 0 ?
там же guard page, что тогда удивительного

freezer

еще раз, покажи функцию целиком, и при каком вызове она работает, а при каком - нет. желательно все константы и операторы указать, сорца мы не своруем

JERRY

Нет не 0. 0-й страницы по виртуальному адресу - это начало 3-го гигабайта для Intel. На самом деле может была и не нулевая, а первая - это не важно. Кроме того, при long все работало.

1234554321

походу Савкин прав, и надо смотреть асм...

freezer

а ты проверял что ли?.. у меня ща и винда под рукой есть и линух, можно просто проверить, если действительно ошибок не видно

1234554321

нет, там же сверхсекретный код
P.S. я исходил из предположения, что всё так, как описано...

freezer

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

MICH51

Народ, а сколько места в Линухе отводится под указатель? 4 или 2 байта?

1234554321

вот бы некоторым всё опошлить

JERRY

Я напишу код, если ты готов пожертвовать своим Линуксом. А так смысла нет, нужно кое-что поменять в ядре, так просто не проверить. И вопрос только в том фича ли это С или gcc или баг того, чем я свой код компилирую.

JERRY

8 или 4. Зависит от разрядности.

freezer

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

rfgbnfy

Я напишу код, если ты готов пожертвовать своим Линуксом.

Давай код . Завтра проверю . Причём рискну не своим линуксом - а сервером (своего в принципе нет ). Естественно после консультации с нашими программерами и админом ..............

JERRY

Дай мне файл mm/memory.c из исходников. Я там напишу все, что нужно. Там эта пресловутая функция копирования и находится.

JERRY

Если Atilla файл не даст, я завтра напишу тут, что и как.

rfgbnfy

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

freezer

ага, нашел... copy_page_range?

JERRY

range это не то, видимо. Там конкретно одна страница должна копироваться. Ищи там from[0] в начале файла.

freezer

ни одного файла memory.c со строкой from[0] не нашлось

JERRY

Блин, облажался. Copy не в этом файле, а где не помню. Где-то в районе copy_user_highpage. Но я и так напишу. wait a minute.

JERRY



//long buffer[PAGE_SIZE/sizeof(long)];
char buffer[PAGE_SIZE];
void copy_page(void* _to, void* _from, long count) {
unsigned long* to = (unsigned long*) _to;
unsigned long* from = (unsigned long*) _from;
int flag;
count /= (sizeof(unsigned long*)*4);
local_irq_save(flag);
do {
unsigned long a, b, c, d;
a = from[0]; b = from[1];
c = from[2]; d = from[3];
count--;
from += 4;
to[0] = a;
to[1] = b;
to[2] = c;
to[3] = d;
to += 4;
} while (count);
local_irq_restore(flag);
}
void do_func(void* data, int mode) {
// Nothing
}
void do_work(void* data, int mode) {
if (mode == 1) {
copy_page(buffer, data, PAGE_SIZE);
do_func(buffer, mode);
} else {
do_func(data, mode);
}
}
void my_func(void) {
struct page* page;
long i = 100; // any number you want
page = &mem_map[0];
while (i) {
do_work(page_address(page 1);
i--;
page++;
}
}


Вставить это можно в тот же memory.c. Если жертвовать Линуксом, то my_func нужно вставить в init/main.c в функцию init перед execve(..)'ями. Разница между работающим и неработающим вариантами в первой строчке. C char не работает , хотя у тебя может и заработает.

dberezhnoy

count /= (sizeof(unsigned long*)*4);
Вот тут сразу видно не правильно. Навекрное имелось виду count /= (sizeof(unsigned long)*4); Тем более count / (sizeof(unsigned long)*4) - не всегда целое число, если размер дан в char-ах.

freezer

что такое mem_map?

freezer

просто чтобы все копировалось, count должно быть кратно 16. Но если это не так, то просто не вся страница скопируется, выход не должен произойти

JERRY

Это опечатка. Кроме того, размер ulong и указателя одинаковый там, где возникала ошибка. Число будет делиться по определению. Размер массива всегда степень двойки - 4096 байт.

JERRY

mem_map - глобальная переменная. Она определена в самом начале memory.c, можешь посмотреть. По типу это массив struct page'ей.

freezer

вот это под виндой работает в обоих вариантах:


//char buffer[PAGE_SIZE];
long buffer[PAGE_SIZE/sizeof(long)];
void copy_page(void* _to, void* _from, long count)
{
unsigned long* to = (unsigned long*) _to;
unsigned long* from = (unsigned long*) _from;
int flag;
count /= (sizeof(unsigned long*)*4);
do {
unsigned long a, b, c, d;
a = from[0]; b = from[1]; c = from[2]; d = from[3];
count--;
from += 4;
to[0] = a;
to[1] = b;
to[2] = c;
to[3] = d;
to += 4;
} while (count);
}
void do_work(void* data, int mode)
{
if (mode == 1)
{
copy_page(buffer, data, PAGE_SIZE);
}
}
void my_func(void)
{
double data[10000];
long i = 100;
do_work(data, 1);
}


ЗЫ под линухом gcc это тоже все съела и все нормально работало, не упало...
траблы могут быть с этим массивом mem_map, его размером и содержимым. А вообще, все это имхо нафиг не нужно, т.к. есть memcpy, которую умный компилятор (icl, cl etc) может превратить в нормальные SIMD-инструкции.
Оставить комментарий
Имя или ник:
Комментарий: