LINUX.ORG.RU

ANSI C: указатель на массив ф-ций


0

0

Помогите чайнику.

Изучаю указатели по книге Кернигана и Ричи "Язык программирования С". Есть такой код.

int array[3] = {0xa, 0xb, 0xc};
int (*daytab)[3];

т.е. определил daytab как указатель на массив из 3-х элементов типа int -- если я правильно понимаю.

Почему же нельзя сделать такое присваивание:

/* здесь warning: assignment from incompatible pointer type */
daytab = array;

Догадываюсь, что array нужно как-то к чему-то привести :) Но не пойму к чему.

Заранее благодарю за помощь!

anonymous

int *daytab;

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

Не так. Элемент массива имеет тип int. Имя массива -- синоним указателя на первый элемент, т.е. имеет тип int *. Так что писать нужно вот так:

int array[] = { 1, 2, 3 };
int *arrayp = array;

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

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

> Явно объявлять переменную типа "указателя на массив такой-то длины" нет никакого смысла, да и не по стандарту это, как видно.

плохо стандарт читаете type *p и type (*p)[N] - семантически разные вещи:

    int i, j;
    int (*parr)[4];
    int (*p)[4];
    int arr[2][4] = { {1, 2, 3, 4}, {5, 6, 7, 8} };

    parr = arr;
    for (p = arr, i = 0; i < 2; i++, p++) {
        for (j = 0; j < 4; j++) {
            printf("%d ", *(*p + j));
        }

        putchar('\n');
    }

А теперь попрубуйте заменить (*p)[4] на **p или *p и посмотрите, что
скажет вам компилятор. 

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

чтобы топикстартер не запутался:

daytab = &array;

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

> плохо стандарт читаете type *p и type (*p)[N] - семантически разные вещи

Спасибо, Капитан! Смысла в объявлении указателя на массив (**p) для доступа к этому массиву нет никакого, кроме случаев, когда массив создан на динамической памяти и исходный указатель нужно изменить (из функции, например.)

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

>Смысла в объявлении указателя на массив (**p) для доступа к этому массиву нет никакого

Действительно - кому нужна эта ф-ция int main(int argc, char **argv) :)

koTuk
()

Короче так.
Массив целых чисел:
int a[3];
Динамический массив целых чисел или указатель на целое число/массив целых чисел:
int* b; // вариант №1
int c[]; // вариант №2 (интересно, нахрена 2 варианта?)
Указатель на массив, элементы которого - динамические массивы (т.е. например d[0]==b, d[1]==c и т.д.):
int** d; // вариант №1
int* e[]; // вариант №2
Указатель на массив строк:
char** args; // вариант №1
char* args2[]; // вариант #2
Указатель на функцию, принимающую 2 целых аргумента и возвращающую целый результат:
int (*g)(int x, int y);

Den_Zurin
()
Ответ на: комментарий от koTuk

> Действительно - кому нужна эта ф-ция int main(int argc, char **argv) :)

И что ты хотел этим сказать? При чем здесь char **argv? Это массив указателей на массивы, что совершенно не коррелирует с обсуждаемым вопросом.

Для танкистов поясню: если нужно передать массив в функцию, либо создать динамический массив, то указатель для работы с ним нужно объявлять как type *array а не type (*array)[]. Замени type на char *, и ты получишь тот же самый char **argv (можно переписать как char *argv[]), как и пишут в объявлении main. А вовсе не char *(*argv)[].

spy_
()
Ответ на: комментарий от Den_Zurin

> int* b; // вариант №1
> int c[]; // вариант №2 (интересно, нахрена 2 варианта?)


Элементарно следует из взаимозаменяемости указателей и массивов, которая была введена для удобства.

P.S. Неправильно вы, батенька, пишете. Нужно int *var, а не int* var, ибо символ * является "атрибутом" переменной, а не типа, и потому его следует писать слитно с именем переменной, а не типа. :)

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

>При чем здесь char **argv? Это массив указателей на массивы

:)) Что за высер, где это тебя так учили ? Для справки - массив и указатель это разные вещи.

koTuk
()
Ответ на: комментарий от spy_

> Элементарно следует из взаимозаменяемости указателей и массивов, которая была введена для удобства.

они не взаимозаменяемы. Это миф. Они только в некоторых случаях взаимозаменяемы.

int **p это вовсе не то же самое что и int (*p)[1]

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

> Для справки - массив и указатель это разные вещи.

Разные, но семантически имя массива _работает_как_указатель_на_его_первый_элемент_ (хотя никакого указателя фактически нет, т.е. нет ячейки памяти с адресом). В чем я не прав? В том, что char **argv -- это массив указателей на массивы? Так я прав. argv является массивом из указателей типа char *, второй asterisk нужен именно для того, чтобы подчеркнуть это. Вообще, я для наглядности пишу char *argv[], что явно показывает argv[] как массив указателей на строки; это равнозначно char **argv, т.к. массив неявно передается в функцию как указатель на первый элемент.

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

> char **argv -- это массив указателей на массивы?

Уточню: массив указателей на массивы char'ов, т.е. C-style строк.

spy_
()
Ответ на: комментарий от dilmah

> int **p это вовсе не то же самое что и int (*p)[1]

Разумеется не то же самое, я и не спорю.

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

> В чем я не прав?

ты был не прав в фразе:

> Явно объявлять переменную типа "указателя на массив такой-то длины" нет никакого смысла, да и не по стандарту это, как видно.

А по теме ответ дал anonymous (*) (25.11.2008 10:03:00)

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

В общем про main это была шутка - я просто взял (++p) без контекста, ты ее не оценил :) 
>имя массива _работает_как_указатель_на_его_первый_элемент_
не совсем так, вернее совсем не так :) оно неявно приводится к 
указателю на первый элемент в некоторых случаях. Для примера взять хотя бы этот

bash-3.2# cat 3.c
#include <stdio.h>

int main(void) {
    int a[] = {0,1};
    int *b;

    b = a;
    printf("%d, %d\n", a[0], a[1]);
    printf("%d, %d\n", b[0], b[1]);    
    printf("%d\n", sizeof(a));
    printf("%d\n", sizeof(b));    
    return 0;
}
bash-3.2# gcc -std=c99 3.c && ./a.out
0, 1
0, 1
8
4

>В чем я не прав? В том, что char **argv -- это массив указателей на массивы?

Думаю исходя из вышеизложенного ты понял свою ошибку :)

koTuk
()
Ответ на: комментарий от spy_

> Вообще, я для наглядности пишу char *argv[], что явно показывает

кроме того, это единственно верный с точки зрения стандарта способ описания параметров ф-ции main()

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

> Явно объявлять переменную типа "указателя на массив такой-то длины" нет никакого смысла, да и не по стандарту это, как видно.

> А по теме ответ дал anonymous (*) (25.11.2008 10:03:00)


Я, как автор топика, в замешательстве :( Может быть кто возьметься разжевать. Исходные данные:

unsigned int array[3] = {0xa, 0xb, 0xc};
unsigned int (*daytab)[3];
daytab = &array; /* верно! */

daytab определен как указатель на массив из 3-х элементов, т.е. переменная daytab содержит в себе 32-битное число (если рассматривать 32bit платформу), указывающее на массив из трех величин типа int. Тогда почему нельзя просто присвоить:

daytab = array;

(ведь array и так есть указатель на первый элемент).

Либо мое понимание сущности daytab неверно, и это на самом деле указатель на ячейку памяти, содержащую указатель на массив??

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

> и это на самом деле указатель на ячейку памяти, содержащую указатель на массив??

нет.

array и &array это фактически одно и то же, просто &array сразу имеет правильный тип. (А типы эти, как отметили выше, фактически отличаются размером типа на который они указывают, а сами их значения указывают на одно место в памяти). Размер объектов на который указывает указатель влияет на то как с этим указателем производится арифметика.

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

>> Вообще, я для наглядности пишу char *argv[], что явно показывает

>кроме того, это единственно верный с точки зрения стандарта способ описания параметров ф-ции main()

Вот и анонимные знатоки сдандартов подтянулись :) О каком стандарте идет речь ? Если брать ISO/IEC 9899:1999 (E)

5.1.2.2.1 Program startup
1 The function called at program startup is named main. The implementation declares no prototype for this function. It shall be defined with a return type of int and with no parameters:
int main(void) { /* ... */ }
or with two parameters (referred to here as argc and argv, though any names may be used, as they are local to the function in which they are declared):
int main(int argc, char *argv[]) { /* ... */ }
__or equivalent__;9) or in some other implementation-defined manner.
.......
9) Thus, int can be replaced by a typedef name defined as int, or the type of argv can be written as char ** argv, and so on.

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

Ничего нового ты мне не открыл. "Работает как указатель на первый элемент массива" и "неявно приводится к типу указателя на первый элемент" -- одна и та же мысль, просто по-разному сформулированная. Если массив объявлен как int a[], по первый элемент можно получить двумя путями -- a[0] и *a, т.е. имя массива здесь неявно используется как указатель. Мы на самом деле и вовсе не спорим, а говорим об одном и том же разными словами. :)

Разумеется, в твоем примере sizeof(a) != sizeof(b), т.к. первое -- массив из двух четерехбайтных int'ов, а второе -- ячейка с адресом памяти длиной в машинное слово. Это очевидно, но не имеет отношения к вопросу. Я же говорил, что имя переменной массива работает как указатель (файтически только на чтение, т.к. не имеет под собой никакой реальной ячейки с адресом, -- в примере int a[] имя a -- это синоним &a[0]), а не _является_ указателем.

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

>Ничего нового ты мне не открыл.
Возможно - решать тебе :)

>"Работает как указатель на первый элемент массива" и "неявно приводится к типу указателя на первый элемент" -- одна и та же мысль
мысли читать не умею, но на мой взгляд

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

>в твоем примере sizeof(a) != sizeof(b), т.к. первое -- массив из двух четерехбайтных int'ов, а второе -- ячейка с адресом памяти длиной в машинное слово. Это очевидно, но не имеет отношения к вопросу

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

>char **argv -- это массив указателей на массивы?

>Уточню: массив указателей на массивы char'ов, т.е. C-style строк.

char **argv ниразу не массив указателей на _массивы_, это указатель на указатель с соответствующей адресной арифметикой, в ф-ции main он эквивалентен записи char* argv[] потому что массивы в ф-ции передаются в виде адреса первого элемента и вообще ты тут путаешь указатель на тип char c указателем на массив элементов с типом char, char* argv[] - это массив указателей на тип char.

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

Переменную char *ptr можно использовать и как указатель на char (*ptr = 'a'), и как массив char'ов (ptr[3] = 'a').

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

> Переменную char *ptr можно использовать и как указатель на char (*ptr = 'a'), и как массив char'ов (ptr[3] = 'a').

Как массив только в том случае, если ptr указывает на первый байт области памяти, достаточной для хранения требуемого массива. Т.е. char *ptr и char ptr[3] -- разные вещи, но в конструкциях *ptr = 'a' и ptr[0] = 'a' они _семантически_ (_не_логически_, не путать) равнозначны.

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