Мне трудно понять, в чем разница между этими двумя фрагментами кода:
// out is of type char* of size N*D
// N, D are of type intfor (int i=0; i!=N; i++){
if (i % 1000 == 0){
std::cout << "i=" << i << std::endl;
}
for (int j=0; j!=D; j++) {
out[i*D + j] = 5;
}
}
Этот код работает нормально, даже для очень больших наборов данных (N = 100000, D = 30000). Из того, что я понимаю об арифметике указателей, это должно дать тот же результат:
for (int i=0; i!=N; i++){
if (i % 1000 == 0){
std::cout << "i=" << i << std::endl;
}
char* out2 = &out[i*D];
for (int j=0; j!=D; j++) {
out2[j] = 5;
}
}
Однако последний не работает (он зависает с индексом 143886 — я думаю, что это происходит с ошибками, но я не уверен на 100%, так как я не привык к разработке под Windows) для очень большого набора данных, и я боюсь, что Я упускаю что-то очевидное о том, как работает указатель арифметики. Может ли это быть связано с продвижением char *?
РЕДАКТИРОВАТЬ: Теперь мы установили, что проблема заключалась в переполнении индекса (т.е. (i * D + j)> = 2 ^ 32), поэтому использование uint64_t вместо int32_t устранило проблему. Что мне до сих пор неясно, так это то, почему первый вышеупомянутый случай будет проходить, а другой — с ошибками.
N * D
3e9; это не вписывается в 32 бит int
,
При использовании N в качестве размера массива, зачем использовать int?
имеет ли отрицательное значение массива какое-либо логическое значение?
что значит «не работает»?
просто думайте об указателях как об адресах в памяти, а не как о «объектах».
char*
void*
int*
все они являются указателями на адреса памяти и, таким образом, в точности совпадают, когда определены или передаются в функцию.
char * a;
int* b = (char*)a;
void* c = (void*)b;
a == b == c;
Разница заключается в том, что при доступе к a, a [i] извлекаемое значение является следующим размером (* a) байтов с адреса a.
И при использовании ++ для продвижения указателя адрес, на который установлен указатель, продвигается
sizeof(pointer_type) bytes.
Пример:
char* a = 1;
a++;
А сейчас 2.
((int*)a)++;
А сейчас 6.
Еще одна вещь:
char* a = 10;
char* b = a + 10;
&(a[10]) == b
потому что в конце
a[10] == *((char*)(a + 10))
поэтому в вашем примере не должно быть проблем с размерами массивов, потому что два примера одинаковы.
РЕДАКТИРОВАТЬ
Теперь обратите внимание, что нет отрицательного адреса в памяти, поэтому доступ к массиву со отрицательным значением со знаком преобразует значение в положительное.
int a = -5;
char* data;
data[a] == data[MAX_INT - 5]
По этой причине может случиться так, что (при использовании значений знака в качестве размеров массива!) Ваши два примера на самом деле не получат одинаковый результат.
Версия 1
for (int i=0; i!=N; i++) // i starts at 0 and increments until N. Note: If you ever skip N, it will loop forever. You should do < N or <= N instead
{
if (i % 1000 == 0) // if i is a multiple of 1000
{
std::cout << "i=" << i << std::endl; // print i
}
for (int j=0; j!=D; j++) // same as with i, only j is going to D (same problem, should be < or <=)
{
out[i*D + j] = 5; // this is a way of faking a 2D array by making a large 1D array and doing the math yourself to offset the placement
}
}
Версия 2
for (int i=0; i!=N; i++) // same as before
{
if (i % 1000 == 0) // same as before
{
std::cout << "i=" << i << std::endl; // same as before
}
char* out2 = &out[i*D]; // store the location of out[i*D]
for (int j=0; j!=D; j++)
{
out2[j] = 5; // set out[i*D+j] = 5;
}
}
Они делают то же самое, но если out
недостаточно велик, они оба будут вести себя неопределенным образом (и, вероятно, вылетать).