2013-12-14 7 views
2
#include<stdio.h> 

#define SIZE 3 

int main() 
{ 
    char intArrayOne[SIZE] = {'A', 'B', 'C'}; 

    char (*ptrToAnOneDimArray)[SIZE] = &intArrayOne; 

    int i = 0; 
    for(i=0 ; i<SIZE ; i++) 
    { 
     printf("%c ", (*ptrToAnOneDimArray)[i]); 
    } 
} 

Output 
A B C 

Когда мы должны использовать «ptrToAnOneDimArray» - виды использования в C/C++? Пожалуйста, дайте мне пример в реальном мире.Почему мы должны использовать указатель-к-массивы в C/C++?

Можно ли избежать таких сложных и нечетких способов использования указателей?

+1

«Нужна» - такое смешное слово. Я не могу придумать, когда я, возможно, использовал его. –

ответ

3

Например, если вы хотите реализовать динамический многомерный массив:

int (*ptrArr)[WIDTH] = malloc(sizeof ptrArr[0] * HEIGHT); 

является гораздо лучше чем

int **ptrPtr = malloc(sizeof ptrPtr[0] * HEIGHT); 
for (size_t i = 0; i < HEIGHT; i++) { 
    ptrPtr[i] = malloc(sizeof ptrPtr[0][i] * WIDTH); 
} 

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

+0

Хотя это правда, я думаю, что лучше обернуть массив в структуре, чтобы сделать более четкий код. –

+1

@ C.R. Нет. Или это * не более * стилистическое предпочтение, но это, конечно, не «очищает» код. Массив должен быть массивом, а не структурой. –

+0

Массив внутри структуры («std :: array» бедного человека) очень похож на массив, но более семантически понятен. –

0

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

Теперь у вас есть программное обеспечение, которое должно обрабатывать все эти типы модулей. У вас есть 3 процедуры (упрощенные здесь) для инициализации, чтения, записи каждого типа модуля (HW0 - модуль A, HW1 - модуль B).

void HW0_init() { printf("HW0_init\n"); } 
void HW0_read() { printf("HW0_read\n"); } 
void HW0_write(){ printf("HW0_write\n"); } 

void HW1_init() { printf("HW1_init\n"); } 
void HW1_read() { printf("HW1_read\n"); } 
void HW1_write(){ printf("HW1_write\n"); } 

Теперь представьте, что вы хотите, чтобы инициализировать Ваш модуль и прочитать н от него, так что вы делаете:

int hw_id = 1; 

// want to init hardware 
switch(hw_id) 
{ 
    case 0: HW0_init(); break; 
    case 1: HW1_init(); break; 
    // ... 
} 

// now I want to read 
switch(hw_id) 
{ 
    case 0: HW0_read(); break; 
    case 1: HW1_read(); break; 
    // ... 
} 

Это может быть сделано по-разному с помощью указателей на массивы. Если объявлять массивы указателей на ваши функции следующим образом:

// as many arrays as you have modules 
void (*hw0[3])() = { HW0_init, HW0_read, HW0_write }; 
void (*hw1[3])() = { HW1_init, HW1_read, HW1_write }; 

ваш код может быть упрощена следующим образом:

enum HW_ACTION 
{ 
    HW_INIT = 0, 
    HW_READ = 1, 
    HW_WRITE = 2 
}; 

// pointer to array of pointers to funcs taking nothing 
// and returning nothing 
void (*(*f)[3])(void); 

// detect hardware and set 'f' 
f = &hw1; 

(*f)[HW_INIT](); // same as HW1_init(); <=> hw1[HW_INIT](); 
(*f)[HW_READ](); // same as HW1_read(); <=> hw1[HW_READ](); 

Тот же эффект - «легкий код».

Вы можете рассматривать его как poor's man virtual methods для пользователей C, не имеющих компилятор C++, где обычно создать базовый абстрактный класс с init, read, write методов и их реализации для каждого вида модуля.

Реальная жизнь здесь http://en.wikipedia.org/wiki/Virtual_method_table.

-1

Указатели-указатели (отсюда указатели на массивы по прокси) действительно полезны. Если у вас есть функция/метод, и он принимает аргумент «указатель на значение», вы можете изменить значение внутри вашей функции, и это значение остается в области действия после того, как вы оставите функцию - пройдя по ссылке, конечно. Однако вы не можете изменить адрес, на который указывает ваш указатель - например, сделайте указатель, который вы передали в указатель NULL, или укажите его другим значением в другом месте в памяти. Если вы используете указатель на указатель на значение, вы можете изменить значение «среднего» указателя внутри вашей функции. Я думаю, что библиотека C-коннекторов MySQL является примером того, где это используется.

В вашем примере вы могли бы передать ptrToAnOneDimArray в функцию и сделать * ptrToAnOneDimArray быть указателем NULL или указатель на некоторые другие данные, а не intArrayOne - в intArrayOne имеет фиксированный размер компилятором (в стеке), то вы могли бы динамически обновлять * ptrToAnOneDimArray из стека, чтобы быть массивом malloc() 'd в куче.

#include <stdio.h> 
#include <stdlib.h> 
#define SIZE 3 

void display(char* data) { 
    int i = 0; 
    for(i=0 ; i<SIZE ; i++) { 
     printf("%c ", data[i]); 
    } 
} 

void changeMyArgument(char** pointerToPointer) { 

    *pointerToPointer = (char*) malloc(SIZE * sizeof(char)); 

    /* now we use array notation for a change */ 
    (*pointerToPointer)[0] = 'X'; 
    (*pointerToPointer)[1] = 'Y'; 
    (*pointerToPointer)[2] = 'Z'; 
} 

int main() { 

    /* intArrayOne is implicitly char* */ 
    char intArrayOne[SIZE] = {'A', 'B', 'C'}; 
    char* arraysArePointers = intArrayOne; 

    /* ptrToAnOneDimArray is implicitly char** */ 
    char** ptrToAnOneDimArray; 
    ptrToAnOneDimArray = &arraysArePointers; 

    display(*ptrToAnOneDimArray); 

    changeMyArgument(ptrToAnOneDimArray); 

    display(*ptrToAnOneDimArray); 

} 
+0

В качестве примера укажите код. – anonymous

+0

'вы не можете изменить адрес, указатель которого указатель на' - это любопытно – Artur

+0

@Artur нет, если вы передадите указатель * по значению * в функцию. – abasterfield