LINUX.ORG.RU

[C] Массивы и указатели

 


0

1

Возник такой вопрос - можно ли инициализировать указатели на массивы массивами или явно придётся выписывать type cast?

Пример:

// char c[];
char (*p)[3] = c;

Ругается:

warning: initialization from incompatible pointer type


Ответ на: комментарий от anonymous

Шило на мысло, приведение типа справа или слева...

abacaba
() автор топика

> можно ли инициализировать указатели на массивы массивами

Сколько угодно:

int a[5][5];
int (*p)[] = a;

LamerOk ★★★★★
()
Ответ на: комментарий от hippi90

> char *b[10];

Это не указатель на массив.

abacaba
() автор топика
Ответ на: комментарий от LamerOk

Да так нормально, ругается имеенно на массивы с неполным типом:

   
int a[] = {0,1,2,3,4};
int (*p)[] = a;

Если для 'a' указать рамерность предупреждения не будет, интересно что ему не нравится.

abacaba
() автор топика
Ответ на: комментарий от hippi90
a = malloc(10);
a = "Hello\n";

Всего пара строк кода, а уже потекли.

andreyu ★★★★★
()
Ответ на: комментарий от LamerOk

Ладно ладно, не всё проверил и не всё изначально написал. Более полный вопрос - почему тут

int foo() {
	int a[] = {0,1,2,3,4};
	int (*p)[] = &a;
}
всё проходит нормально без предупреждений, а тут -
int foo(int a[]) {
	int (*p)[] = &a;
}
- есть сообщение?

abacaba
() автор топика
Ответ на: комментарий от abacaba

Потому что в действительности всё совсем не так, как на самом деле.

int a[] = ... // объявление массива

int foo(int a[]) тождественно int foo(int *a)

LamerOk ★★★★★
()
Ответ на: комментарий от LamerOk

Всё-таки тут какая-то неконсистентность есть, ведь по сути-то передаётся указатель на массив, а не на какой-нибудь одиночный int. Теперь чтобы его таки преобразовать к указателю на массив приходится писать касты, или через void* ...

abacaba
() автор топика

Я бы лучше каждый указатель инициализировал через malloc (calloc) - так удобнее.

[code]int *pointer;

pointer = malloc(sizeof(int)*10);

[/code]

И по pointer[0-9] обращаться...

И можно вместо sizeof(int) написать sizeof(int*****) и потом до хрена раз каждый раз запускать malloc ;) никто не мешает.

BattleCoder ★★★★★
()
Ответ на: комментарий от abacaba

> Всё-таки тут какая-то неконсистентность есть, ведь по сути-то передаётся указатель на массив

Неконсистентность у тебя в голове, потому что ты путаешь указатель, массив и указатель на массив. Любой массив может выступать в качестве указателя на объекты своего типа.

Прочитай уже K&R, главу 5, наконец.

LamerOk ★★★★★
()
Ответ на: комментарий от LamerOk

Я-то как раз ничего не путаю, и массив таки отличается от указателя на элемент везде - кроме аргумента функции, в чём и есть неконсистентность.

abacaba
() автор топика
Ответ на: комментарий от abacaba

> Я-то как раз ничего не путаю

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

Ну и вот таких конструкций:

int (*p)[] = &a;


ты бы не городил, а писал бы по человечески:

int *p = a;



LamerOk ★★★★★
()
Ответ на: комментарий от LamerOk

Никакой путаницы нет, просто я не знал (забыл) об этой подлости с массивами как аргументами функции.

int *p = a;

Это - не по человечески, это для любителей пописать на ассемблере. По человечески когда тип объекта (переменной) отражает её суть, и указатель на int и указатель на массив int-ов с этой точки зрения должны иметь разный тип.

abacaba
() автор топика
Ответ на: комментарий от abacaba

указатель на int и указатель на массив int-ов с этой точки зрения должны иметь разный тип.

Это как это? Указатель - он и в африке указатель и от типа данных, на которые он указывает, указатель не зависит.

По теме: указатели инициализировать по-человечески надо. Например:

#include <stdio.h>
main(){
	char *ptr[3] = {"does", "it", "work?"};
	int i;
	for(i=0; i < 3; i++)
		printf("ptr[%d] = %s\n", i, ptr[i]);
}

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от Eddy_Em

> Это как это? Указатель - он и в африке указатель и от типа данных, на которые он указывает, указатель не зависит.

Значит можно везде использовать (void*) раз от типа указуемых данных указатель не зависит.

abacaba
() автор топика
Ответ на: комментарий от abacaba

Тип данных нужно знать только компилятору - чтобы правильно смещения вычислять при всяких ptr++.

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от abacaba

> Никакой путаницы нет

указатель на int и указатель на массив int-ов с этой точки зрения должны иметь разный тип.



Это и есть путаница. Я же сказал - ты путаешь указатель и указатель на массив.


я не знал (забыл) об этой подлости с массивами


А на том ли языке ты пишешь?

LamerOk ★★★★★
()
Ответ на: комментарий от LamerOk

> Я же сказал - ты путаешь указатель и указатель на массив.

А мне почему-то кажется что путаешь ты.

abacaba
() автор топика
Ответ на: комментарий от LamerOk

Это конечно не так. То что я показал - работает, исключение - аргументы функций, исключительно из-за того что по странной прихоти авторов языка С тип «массив» при передаче в качестве аргумента функции теряется.

Изначально проблема в этом и состояла: я думал что указателю на массив нормально присваивать значение нельзя, а оказывается нормально можно (int (*p)[] = &a;), только если массив не берётся из аргумента (что и случилось в реальности у меня).

abacaba
() автор топика
Ответ на: комментарий от Eddy_Em

О массивах указателей я вообще нигде не говорил.

abacaba
() автор топика
Ответ на: комментарий от abacaba

> То что я показал - работает

4.2

Пруфы:
http://www.linux.org.ru/forum/development/6295102
http://www.linux.org.ru/jump-message.jsp?msgid=6295102&cid=6295642

исключение - аргументы функций, исключительно из-за того что по странной прихоти авторов языка С тип «массив» при передаче в качестве аргумента функции теряется.


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

Ты, часом, паскалем в детстве не болел?

я думал что указателю на массив нормально присваивать значение нельзя, а оказывается нормально можно (int (*p)[] = &a;),


Вот это твоё «нормально» и выдает тебя с головой, как человека не в теме.

Читай K&R, а не отмазывайся на форуме.

LamerOk ★★★★★
()
Ответ на: комментарий от abacaba

>это для любителей пописать на ассемблере
добро пожаловать в Си :)
поздравляю с открытием))

xydo ★★
()
Ответ на: комментарий от LamerOk

> То что я показал - работает

4.2

Специально в скобках написал для нежелающих перечитывать.

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

Я странные прихоти и не обязан понимать, на то они и странные, хотя сомневаюсь что аргументы изначально были именно такие.

Вот это твоё «нормально» и выдает тебя с головой, как человека не в теме.

Спасибо, в вашей «теме» к счастью и не надо быть, а надо использовать декларативные возможности языка, какие-бы скудные они не были.

abacaba
() автор топика
Ответ на: комментарий от abacaba

> Я странные прихоти и не обязан понимать, на то они и странные,

Тогда зачем ты создал тему на форуме? Если ты не хочешь понимать язык - не понимай. Никто ведь тебя не заставляет.

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


И в третий раз я тебе говорю - читай K&R. Там всё написано, включая аргументы, которые были именно такие.

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


Да нет никаких «декларативных возможностей». Размерность массива учитывается только в рамках того пространства имён, где он был объявлен.

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

LamerOk ★★★★★
()
Ответ на: комментарий от LamerOk

> Да нет никаких «декларативных возможностей». Размерность массива учитывается только в рамках того пространства имён, где он был объявлен.

Вообще никакой объект нельзя использовать вне пределов где он был объявлен, разве только создать новый - указатель на него. И в случае указателя на массив _все_ размерности (даже будь они не константные) сохраняются при доступе к массиву.

abacaba
() автор топика
Ответ на: комментарий от abacaba

> Вообще никакой объект нельзя использовать вне пределов где он был объявлен,

Да ну?

разве только создать новый - указатель на него.


Т.е. - можно. Молодец, возьми с полки пирожок. Еще пара таких пассажей, и тебя можно будет воспроизвести в капитаны.

в случае указателя на массив _все_ размерности (даже будь они не константные) сохраняются при доступе к массиву.


Пруф или левтолстой. ;)

LamerOk ★★★★★
()
Ответ на: комментарий от LamerOk
/* Два эквивалентных с точки зрения использования примера */

/* Во-первых, даже в стандартном способе передачи теряется только одна
 * размерность: */
void foo1(size_t n, size_t m, int arr[n][m]) {
	arr[i][j]; /* Здесь компилятор скорее всего не сможет проверить
		    * выход за границу для 'i', но вполне в состоянии
		    * для 'j' */
}

/* Однако можно сохранить всё наверняка */
void foo2(size_t n, size_t m, int (*arr)[n][m]) {
	(*arr)[i][j]; /* Здесь уже можно делать и статический анализ, и
		       * динамический проверки - все размерности
		       * присутствуют и передаются свыше */
}

abacaba
() автор топика
Ответ на: комментарий от abacaba

Здесь компилятор скорее всего .., но вполне в состоянии

Компилятор «скорее всего вполне в состоянии» сделать за тебя домашку по химии. Но это не значит, что он будет её делать. Компилятор - пацан конкретный и за базар отвечает по понятиям.

/* Во-первых, даже в стандартном способе передачи теряется только одна * размерность: */

Да ну?

/* Однако можно сохранить всё наверняка */

Да ну?

#include <stdio.h>

void foo1(size_t n, size_t m, int a[n][m])
{
	printf("Size of(a[0]): %d\n", (int)sizeof(a[0])/sizeof(a[0][0]));
}

void foo2(size_t n, size_t m, int (*a)[n][m])
{
	printf("Size of(a[0]): %d\n", (int)sizeof(a[0])/sizeof(a[0][0]));
	printf("Size of(a[0][0]): %d\n", (int)sizeof(a[0][0])/sizeof(a[0][0][0]));
}

int main(void)
{
	int a[1][2];
	int (*p)[][2] = &a;
	foo1(3, 4, a);
	foo2(3, 4, p);
	return 0;
}

LamerOk ★★★★★
()
Ответ на: комментарий от LamerOk

Компилятор - пацан конкретный и за базар отвечает по понятиям.

Да я уже вижу что ты чоткий пацан, пиши везде void*!

   int a[1][2];
   ...
   foo1(3, 4, a);

Это ты решил показать, что умеешь писать неправильные - но зато чисто по-пацански - программы?

abacaba
() автор топика
Ответ на: комментарий от anonymous

> cdecl> explain int (*p)[] declare p as pointer to array of int cdecl> explain int *p[] declare p as array of pointer to int

А ты, дибил-очевидность, вначале читать научись а потом пиши.

abacaba
() автор топика
Ответ на: комментарий от LamerOk

Я в примере показал то о чем говорил - как сохранить тип массива с помощью указателя. То что программы можно писать неправильно, и, в частности, неправильно приводить указатели - я и не отрицал.

abacaba
() автор топика

В любом случае вопрос для себя я разъяснил ещё вчера, больше спорить и кому-то объяснять о чём там речь - не хочу, всем спасибо.

abacaba
() автор топика
Ответ на: комментарий от abacaba

> Я в примере показал то о чем говорил - как сохранить тип массива с помощью указателя.

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

LamerOk ★★★★★
()
Ответ на: комментарий от LongLiveUbuntu

Нет. Сравни хотя-бы sizeof(a) для int*a и для int a[5] к примеру - это более чем достаточная разница, чтобы считать их различными типами данных. И да, в случае указателя на массив (int(*a)[5]), sizeof(*a) работает правильно - т.е. как для массива.

Это не единственная разница, для int*a я не могу в общем случае обращаться, например, к a[3], т.к. сам тип не гарантирует что такой элемент существует - валидность указателя этого не гарантирует. В случае массива такая гарантия есть, так же как и в случае обращения через указатель на массив - (*a)[3].

Впрочем, всё это и так должно быть очевидно каждому, кто пишет на С достаточно вдумчиво...

abacaba
() автор топика
Ответ на: комментарий от abacaba

тип не гарантирует что такой элемент существует - валидность указателя этого не гарантирует. В случае массива такая гарантия есть, так же как и в случае обращения через указатель на массив - (*a)[3].

#include <stdio.h>

int main(void)
{
	int a[] = {0,1,2,3,4};
	int (*p)[5] = &a;
	printf("Access to address out of range: %d\n", a[6]);
	printf("Access to address out of range: %d\n", (*p)[6]);
	return 0;
}
:~$ gcc test.c -o test -Wall -Wextra -std=gnu99
:~$ ./test
Access to address out of range: 45627232
Access to address out of range: 45627232
:~$

Впрочем, всё это и так должно быть очевидно каждому, кто пишет на С. ©

LamerOk ★★★★★
()
Ответ на: комментарий от LamerOk

Эхехе...

Ладно зайдём с другой стороны: представь что в gcc-5.0.0 появилась возможность генерирования рантайм-проверок доступа к элементам массива. Как ты думешь - для какого типа компилятор сможет добавить проверку (в том числе для приведённого тобой примера), для int* или для int[5]?

А на сегодня достаточно что я знаю о гарантии наличия элемента видя тип, а не гадаю что скрывается за T* пришедшим в данном лексическом контексте вообще неведомо откуда.

abacaba
() автор топика
Ответ на: комментарий от abacaba

> представь что в gcc-5.0.0 появилась возможность генерирования рантайм-проверок доступа к элементам массива.

Мой юный друк, такая возможность уже есть 100 лет в обед. Найти её в руководстве по gcc предоставляется тебе в качестве упражнения.


> Как ты думешь - для какого типа компилятор сможет добавить проверку


Во время компиляции - ни для какого не сможет, ибо стандарт. Я знаю, тебе трудно в это поверить, но жестокая действительность такова, что компиляторы реализуют опираясь на стандарт, а не на твои фантазии.

А проверки времени выполнения уже сделаны, выражаясь твоим языком, «для обеих типов».

> я знаю о гарантии наличия элемента видя тип


Прости за прямоту, но ты, видимо, слегка туповат. Тебе весь топик объясняют, что T name[] и T *name - это один и тот же тип. Твоё func(size_t size, T array[size]) ничем не отличается от func(T *p, size_t size). «Гарантии» у тебя в обеих случаях одни и те же - надежда на корректное использование вызывающим кодом.

Подчеркну еще раз (может с N+1-ого раза дойдет) - замена одного _синтаксиса_ на другой тебе ни-че-го не даст, кроме трудночитаемости кода.

LamerOk ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.