2016-06-02 3 views
22

Я пытаюсь понять программу ниже, но мне это непонятно.Размер массива без оператора 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] и значения, которые вы можете видеть в коде. Я также пробовал отлаживать.

+3

'(& a) [1] -a' - указатель разница. – haccks

+2

включите предупреждения компилятора и обратите внимание, что у вас есть '% zu' для' size_t'. – sjsam

+0

@hacce почему разница указателя 7 не что-то? – Mohan

ответ

5

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

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

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 и имеет тип pointer-to-array.

Затем мы разыскиваем и получаем несуществующий массив размера 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 к нему, получая указатель на массив потом в одном случае, и ноль в другом случае.

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

&a[7]-&a[0] 

который

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

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

(a+7)-(a+0) 

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

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

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

+0

Спасибо за ясное объяснение. Я понял, проверив objdump для этого кода, а также с литовым кодом типа. – torban

15

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

// 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)); 

Объясняя Math

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

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

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

Обзор по арифметике указателя можно найти here.

+2

Ya спасибо, но без литья типа я получаю 7, который до сих пор не ясен для меня. Скажите, что он такой же, как sizeof (a)/sozeof (a [0]). Но говорить, что это похоже на это Ok, но во время отладки, как я могу ясно понять эту разницу. – torban

+0

Это часть математики указателя. Подробнее об этом можно прочитать здесь (https://www.cs.umd.edu/class/sum2003/cmsc311/Notes/BitOp/pointer.html). Я обновил ответ с более подробным описанием того, что происходит. – Carter

+0

- базовый размер int или int [7]? – sjsam

2

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

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

предпочтительнее использовать 'long' вместо' int'. – sjsam

+0

Да, я допустил ошибку. –

+0

Ya спасибо, но без литья типа я получаю 7, который до сих пор не ясен для меня. Скажите, что он такой же, как sizeof (a)/sozeof (a [0]).Но говорить, что это похоже на это хорошо, но во время отладки, как я могу ясно понять эту разницу. – torban

5

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

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

С11-§6.5.6/9:

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

Разница (&a)[1]-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.

+1

есть ли какое-либо теоретическое объяснение, почему '(& a) [1]' дает *** адрес ячейки памяти за массивом *** и '(& a) [1] -a' дает * ** количество элементов в массиве 'a' *** и значение возвращаемого значения изменяется при нажатии на' long', как упоминалось в @Carter? – Cherubim

+2

Если вы могли бы поместить небольшую заметку о различии между (& a) [x] и & [x], это будет полезно для будущей аудитории :-) – sjsam

+0

@CherubimAnand Да, есть! Я обновил свой ответ с более подробным объяснением, но он в основном сводится к арифметике указателей. Подробнее об этом можно прочитать здесь (https://www.cs.umd.edu/class/sum2003/cmsc311/Notes/BitOp/pointer.html). – Carter

2

Если вы хотите, чтобы получить число байт, без использования 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 для представления объектов указателей в виде целых чисел, если вам действительно нужно.

0

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

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

или

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

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

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

& а [7] - & а [0]

который

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

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

(a + 7) - (a + 0)

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

(а + 7) = (а + 0) +7

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

Смежные вопросы