В чем разница между указателем и массивом в следующем контексте? - PullRequest
4 голосов
/ 26 июня 2009
#include <cstring>
int main()
    {
    char *pName = new char[10];
    char dummy[] = "dummy";
    strcpy(pName + 0,dummy);//how this is different from -->this works
    strcpy(pName[0],dummy);//this one...--> error C2664: 'strcpy' : 
                           //cannot convert parameter 1 
                           //from 'char' to 'char *'

    }

Ответы [ 6 ]

12 голосов
/ 26 июня 2009
  • pName [0] - первый элемент в массиве символов ( один символ)
  • pName - это ярлык для & pName [0] (указатель на первый элемент вашего массива)

Причина, по которой вы получаете ошибку, состоит в том, что strcpy ожидает указатель на char (char *), а не значение char (что и есть в pName [0])

3 голосов
/ 26 июня 2009

При работе с указателями и массивами в C или C ++ это действительно помогает распознать их как очень четкие конструкции (я думаю, что одной из лучших книг, объясняющих это различие, является книга под названием «Секреты Deep C», если я правильно помню). Что мутит воду, так это тот факт, что существует одностороннее бесшумное преобразование из имен массивов в указатели (несоответствие в обработке имен переменных в языке), но очень важно не интерпретировать существование этого явления распада как подразумевающего эквивалентность.

Чтобы помочь нам рассуждать об этом, давайте введем идею «ячейки памяти». Мы моделируем «ячейку памяти» как имеющую два атрибута:

a) value
b) address

Затем мы можем смоделировать простую переменную C ++ как имеющую два атрибута (нам не нужны типы на этом низком уровне абстракции):

c) name  
d) memory cell

Как и большинство моделей, у него есть некоторые недостатки (не касается массива с более чем одним элементом, но этого достаточно для наших целей).

Так, например:

// non-array variable: name 'i', and memory cell: value=3, address=0x0A
int i = 3;

// non-array variable: name 'p', and memory cell: value=0x0A, address=0x0B
int *p = &i;

// array variable: name 'a', and memory cell: vale=4, address=0x0C     
int a[1] = { 4 };

// non-array variable: name 'b', and memory cell: value=0x0C, address = 0x0D
int (*b)[1] = &a;

// non-array variable: name 's', and memory cell: value=0x0C, address = 0x0E
int *s = &a[0];


// non-array variable: name 't', and memory cell: value=0x0C, address = 0x0F
int *t = a; // Here is the key difference! read on...

Теперь вот основное отличие между переменной массива и переменной C ++, не являющейся массивом (указателем):

Когда оценивается имя переменной в C ++, оно всегда вычисляется по значению своей ячейки памяти с одним исключением: если переменная называет переменную массива.
Если переменная является именем массива, она оценивается по адресу ячейки памяти.
Вышеприведенные две строки заслуживают повторного прочтения.

Вот несколько примеров, которые помогут прояснить последствия (см. Вышеупомянутые переменные):

int k = i;  // the 'i' name evaluates to the value of its cell, so 'k' is set to 3

int *q = p; // 'p' evaluates to the value of its cell, so 'q' is set to 0x0A

int *r = a; // 'a' evaluates to the *address* of its cell, so 'r' is set to 0x0C

int (*c)[1] = b; // 'c' is set to 0x0D

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

Так, например, не делайте этого:

// myproj_file1.cpp
int array[100] = { 0 }; // here 'array' evaluates to the *address* of the first memory cell

// myproj_file2.cpp
extern int* array; // here 'array' evaluates to the *value* of the first memory cell 
            // Assuming the linker links the two
            // what it does if you read the assembly, is something like this: 
            // extern int* array = (int*) array[0];
            // but it doesn't have to, it can do anything, since the behavior is undefined

Надеюсь, это поможет. Если вам все еще кажется, что дальнейшие разъяснения могут помочь, пожалуйста, задайте дополнительный вопрос, и не стесняйтесь получить копию (библиотеку?) Этой книги "Deep C Secrets":)

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

0 голосов
/ 26 июня 2009

pName - указатель на вновь выделенную память. char *pName = new char[10];

dummy также является массивом / указателем. char dummy[] = "dummy";

pName является указателем и указывает на базовый адрес, даже если вы добавите (pName + 0), все еще указывает на то же место в памяти, потому что вы добавляете только 0. strcpy(pName + 0,dummy);

strcpy использует переменную указателя и ваше передаваемое значение в первом аргументе, поэтому вы получаете ошибку strcpy(pName[0],dummy)

0 голосов
/ 26 июня 2009

Массив - это просто указатель, автоматически (обычно) назначаемый автоматически выделенному блоку памяти. Принимая ваш пример, вы можете объявить пустышку равным образом:

char    dummy[] = "dummy";
char    *dummy = "dummy";

И затем вы можете использовать либо синтаксис массива, либо синтаксис указателя для доступа к данным:

char    ch = dummy[0];   // get the first element of the array
char    ch = *dummy;     // get the data pointed to by dummy

И [], и * могут использоваться для повторного обращения к указателям и массивам, поэтому следующие значения эквивалентны:

array[N];
*(ptr + N);

Учитывая вторую форму, (ptr + N) по-прежнему является указателем, чуть дальше по массиву. Вот почему это синтаксически правильно в вашем примере. ptr[N] - разыменование указателя и символ (в данном контексте).

0 голосов
/ 26 июня 2009

Технически, strcpy(pName[0], dummy); не правильно. Даже если для этого была выделена память.

Это потому, что pName[0] имеет тип 'char', а pName + 0 имеет тип char *. Они оба ссылаются на одну и ту же память, но по-разному.

Компилятор может затем превратить strcpy(pName[0], dummy); в strcpy((char*) pName[0], dummy);, что является опасным неявным приведением. Если ваш компилятор наполовину приличный, вы получите предупреждение или ошибку (как вы видите с вашей «ошибкой C2664»).

0 голосов
/ 26 июня 2009

Разницы нет. Они оба вылетят, так как вы не выделили место для pName. :) [РЕДАКТИРОВАТЬ: больше не сбой - вопрос был отредактирован]

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

(РЕДАКТИРОВАТЬ: если вы действительно имели в виду & pName [0], как указал Брайан Бонди.)

...