Подтвердить что ты не робот

Размер массива без оператора sizeof

Я пытаюсь понять программу ниже, но мне это не понятно.

    #include<stdio.h>
    int main()
    {
        int a[]={1,2,3,4,5,6,9};
        printf("sizeof array is %d\n",sizeof(a));
        printf("size of array using logic is %d\n",((&a)[1]-a));
        printf("value of (&a)[1] is %p \n",(&a)[1]);
        printf("value of a is %p \n",a);
        printf("address of a[0] is %p\n",&a[0]);
        printf("address of a[1] is %p\n",&a[1]);
        printf("address of a[2] is %p\n",&a[2]);
        printf("address of a[3] is %p\n",&a[3]);
        printf("address of a[4] is %p\n",&a[4]);
        printf("address of a[5] is %p\n",&a[5]);
        printf("address of a[6] is %p\n",&a[6]);
    }

Выше вывод кода:

    sizeof array is 28
    size of array using logic is 7
    value of (&a)[1] is 0x7ffc4888e78c 
    value of a is 0x7ffc4888e770 
    address of a[0] is 0x7ffc4888e770
    address of a[1] is 0x7ffc4888e774
    address of a[2] is 0x7ffc4888e778
    address of a[3] is 0x7ffc4888e77c
    address of a[4] is 0x7ffc4888e780
    address of a[5] is 0x7ffc4888e784
    address of a[6] is 0x7ffc4888e788

Мне непонятно, почему ((&a)[1]-a)) во второй оператор печати возвращается 7; он должен быть 0x7ffc4888e78c - 0x7ffc4888e770, который равен 0x1c i.e 28 общий размер массива.

Для справки я также попробовал распечатать (&a)[1] и значения, которые вы можете увидеть в коде. Я также пробовал отлаживать.

4b9b3361

Ответ 1

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

Указатели похожи на математические векторы над целыми числами, если вы сделали какую-либо линейную алгебру.

p1-p2 - это расстояние между p1 и p2, целое число, необходимое для добавления к p2 для достижения p1.

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

То же самое верно, когда вы вычитаете два указателя.

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

Вторая странная вещь, происходящая здесь, заключается в том, что массивы распадаются на указатели на их первый элемент при падении шляпы. Они, однако, не являются указателями на их первый элемент, они просто легко конвертируются в них.

Итак, когда мы это делаем:

(&a)[1]

мы берем адрес a. Адрес a является указателем типа int(*)[7]. Это указатель на массив, а не указатель на первый элемент массива. Разница заключается в типе указателя. И это важно.

Затем мы используем [] в указателе. Если у вас есть указатель или массив p и значение v, p[v] определяется как *(p+v). Это приводит к юмору, если вы делаете v[p], но это не важно.

Пусть pa представляет (&a). Тогда pa[1] будет *(pa + 1).

Теперь pa является указателем на массив (а не является указателем на первый элемент массива). Итак, +1 добавляет полный размер массива (sizeof (int) * 7) к числовому значению.

So pa+1 является указателем на один конец конца a и имеет тип-указатель-массив.

Затем мы разыскиваем и получаем несуществующий массив размера 7 сразу после окончания массива a.

Затем вычитаем a.

(&a)[1]-a

Здесь происходит разложение указателя. На массивах нет операции -, но есть указатель - для указателей. Таким образом, C-язык успешно распаковывает каждый из этих массивов в указатели на их первый элемент.

Указатель на первый элемент a равен &a[0].

Указатель на первый элемент массива размера 7 сразу после окончания a равен... &a[7].

Оба этих указателя имеют тип int*. Когда вы вычитаете два int* s, вы получаете их числовое значение указателя, разделенное на sizeof(int). В этом случае это легко - 7.

Это может быть проще, если мы посмотрим на это:

(&a)[1]-(&a)[0]

или

*(&a+1)-*(&a+0)

&a является указателем на массив a типа "указатель на массив размера 7". Мы добавляем 1 к нему, получая указатель на массив потом в одном случае, и ноль в другом случае.

Затем мы возвращаемся к массивам и вычитаем. Subtraction триггеры распадаются на указатель на первый элемент, поэтому мы получаем указатель на элемент сразу после конца a и указатель на первый элемент a.

&a[7]-&a[0]

который

&*(a+7)-&*(a+0)

Теперь &* ничего не делает для вещей, которые уже являются указателями (которые они находятся в этой точке), поэтому:

(a+7)-(a+0)

Затем возникает вопрос, сколько вам нужно добавить в a+0 для достижения a+7. Ответ, что неудивительно, это 7:

(a+7) = (a+0)+7

и это то, что отображается.

Ответ 2

Если вы выполняете (&a)[1] и a до long перед вычислением, вы получите ожидаемый результат. Как прокомментировал hacce, вы в настоящее время вычисляете разницу указателей.

// These two sizes will be the same
printf("sizeof array is %ld\n",sizeof(a));
printf("size of array using logic is %ld\n",((long)(&a)[1]-(long)a));

Объяснение математики

Что происходит в этом случае, &a считается типом int(*)[7].

Затем вы ссылаетесь на (&a)[1], что соответствует *((&a)+1). На английском языке это означает "дать мне точку в памяти 1 после начала a". Поскольку &a оказывается типом int(*)[7], эта точка находится в конце массива.

Когда вы вычитаете a, указатель на начало массива, вы выполняете арифметику указателя и берете базу размером int (потому что a является массивом int). Таким образом, выражение ((&a)[1]-a) подсчитывает, сколько int находится между (&a)[1] и a.

Обзор арифметики указателя можно найти здесь.

Ответ 3

(&a)[1] - адрес ячейки памяти после массива a, т.е. 0x7ffc4888e788. (&a)[1] имеет тип int *. После преобразования a будет иметь тип int *. Это эквивалентно (&a)[0].

Стандарт говорит, что:

С11-§6.5.6/9:

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

Разность (&a)[1]-a дает количество элементов в массиве a. Обратите внимание, что a в этом выражении является адресом элемента массива a[0], после распада, и этот адрес эквивалентен адресу массива a, хотя &a и a[0] имеют другой тип.

Вы можете представить эту разницу как (&a)[1]-(&a)[0] или &a[7] - &a[0].

sizeof(a) дает размер памяти, выделенный для массива a. sizeof(a)/sizeof(a[0]) даст количество элементов массива a.

Ответ 4

Вы используете указатель int*. Все арифметические операции на нем с использованием 4 bytes (sizeof(int), если быть точным) как единица. Разница между двумя указателями выражается в этих единицах. ((&a)[1]-a)) равен sizeof(a)/sizeof(a[0]). Для вычисления размера массива в байтах вам нужно указать указатель на целочисленное значение, unsigned int:

  printf("size of array using logic is %d\n",((int)((&a)[1])-(int)a));

Ответ 5

Если вы хотите получить количество байтов, не используя sizeof -оператор, а вместо того, чтобы использовать long тип данных *, я считаю, что более идиоматичным и безопасным способом является включение обоих указателей в char *:

printf("Size of array using pointer arithmethic is %td.\n", (char*)(&a)[1] - (char*)a);

Результат:

Размер массива с использованием указателя arithmethic равен 28.

Обратите внимание, что спецификатор формата %td подходит для типа данных ptrdiff_t (определен в <stddef.h>), то есть как показано различие указателей.


*) Существуют специальные типы данных intptr_t и uintptr_t для представления указателей объектов в виде целых чисел, если вам это действительно нужно.

Ответ 6

В первую очередь спасибо всем и особую благодарность Якку за то, что он дал мне такой отличный анализ на простой указатель arthamatics.I наконец выяснил, почему это происходит, как @Yakk объяснил подробно, что очистил меня в значительной степени, но все еще имел некоторые сомнения на этом, поэтому я начал сменять код и попытался проверить артикулу указателя. Один короткий ответ - если используется & a [0], он ссылается на первый элемент в адресе массива. Если используются a или a, они относятся к базовому адресу полного массива размера 7.                Теперь, чтобы понять, мы использовали (& a) [0], которые указывают на базовый адрес массива размера 7 при увеличении до 1, который переходит в один конец конца массива a. Как объясняется - Yakk, как показано ниже: Это может быть проще, если мы посмотрим на это:

(& а) [1] - (& а) [0]

или

(& а + 1) - (& а + 0)

& a - указатель на массив a типа "указатель на массив размера 7". Мы добавляем 1 к нему, получая указатель на массив потом в одном случае, и ноль в другом случае.

Затем мы возвращаемся к массивам и вычитаем. Subtraction триггеры распадаются на указатель на первый элемент, поэтому мы получаем указатель на элемент сразу после конца a и указатель на первый элемент a.

& а [7] - & а [0]

который

& (а + 7) - & (а + 0)

Теперь & * ничего не делает для вещей, которые уже являются указателями (которые они находятся в этой точке), поэтому:

(а + 7) - (а + 0)

Затем возникает вопрос, сколько вам нужно добавить к + 0, чтобы достичь + 7. Ответ, что неудивительно, - 7:

(a + 7) = (a + 0) +7

и это то, что отображается.