арифметика указателей в C ++ с использованием char *

Мне трудно понять, в чем разница между этими двумя фрагментами кода:

// 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 устранило проблему. Что мне до сих пор неясно, так это то, почему первый вышеупомянутый случай будет проходить, а другой — с ошибками.

2

Решение

N * D 3e9; это не вписывается в 32 бит int,

4

Другие решения

При использовании 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

Версия 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 недостаточно велик, они оба будут вести себя неопределенным образом (и, вероятно, вылетать).

-1
По вопросам рекламы [email protected]