Указатель функции также указывает в память, единственная разница - то, что существует исполняемый код в той ячейке памяти вместо данных.
На многих платформах, при попытке выполнить данные (например, регулярная память) Вы разрушите или вызовете исключение. Это известно как Предотвращение Выполнения Данных - меры безопасности для предотвращения приложений, непреднамеренно выполняющих изворотливый код, который может быть помещен туда вредоносным программным обеспечением.
Верно, но это небольшая разница. По сути, первое:
char amessage[] = "now is the time";
Определяет массив, члены которого находятся в пространстве стека текущей области, тогда как:
char *pmessage = "now is the time";
Определяет указатель, который живет в пространстве стека текущей области, но который ссылается на память в другом месте (в этом, "сейчас время "хранится в другом месте памяти, обычно в таблице строк).
Также обратите внимание, что, поскольку данные, принадлежащие второму определению (явный указатель), не хранятся в пространстве стека текущей области видимости, они точно не определены где он будет храниться и не должен изменяться.
Изменить: Как указали Марк, GMan и Павел, существует также разница, когда оператор адресации используется для любой из этих переменных. Например, & pmessage возвращает указатель типа char **, или указатель на указатель на символы, тогда как & amessage возвращает указатель типа char (*) [16] или указатель на массив из 16 символов (который, как и char **, должен быть разыменован дважды как точки litb вне).
Массив - это константный указатель. Вы не можете обновить его значение и указать в другом месте. Хотя для указки можно сделать.
For this line: char amessage [] = «сейчас самое время»;
компилятор будет оценивать использование сообщения в качестве указателя на начало массива, содержащего символы «сейчас самое время». Компилятор выделяет память для «сейчас самое время» и инициализирует ее строкой «сейчас самое время». Вы знаете, где хранится это сообщение, потому что сообщение всегда ссылается на начало этого сообщения. Сообщение не может получить новое значение - это не переменная, это имя строки «сейчас самое время».
Эта строка: char * pmessage = "now is the time";
объявляет переменную pmessage, которая инициализирована (с заданным начальным значением) начального адреса строки "now is the time". В отличие от amessage, pmessage может быть присвоено новое значение. В этом случае, как и в предыдущем случае, компилятор также сохраняет «сейчас самое время» в другом месте памяти. Например, это приведет к тому, что pmessage будет указывать на букву «i», которая начинается со слова «это время». pmessage = pmessage + 4;
Первая форма ( сообщение
) определяет переменную (массив), которая содержит копию строки «сейчас время»
.
Вторая форма ( pmessage
) определяет переменную (указатель), которая находится в другом месте, чем любая копия строки «сейчас время»
.
Попробуйте вывод этой программы:
#include <inttypes.h>
#include <stdio.h>
int main (int argc, char *argv [])
{
char amessage [] = "now is the time";
char *pmessage = "now is the time";
printf("&amessage : %#016"PRIxPTR"\n", (uintptr_t)&amessage);
printf("&amessage[0]: %#016"PRIxPTR"\n", (uintptr_t)&amessage[0]);
printf("&pmessage : %#016"PRIxPTR"\n", (uintptr_t)&pmessage);
printf("&pmessage[0]: %#016"PRIxPTR"\n", (uintptr_t)&pmessage[0]);
printf("&\"now is the time\": %#016"PRIxPTR"\n",
(uintptr_t)&"now is the time");
return 0;
}
Вы увидите, что хотя & amessage
равно & amessage [0]
, это неверно для & pmessage
и & pсообщение [0]
. Фактически, вы увидите, что строка, хранящаяся в amessage
, живет в стеке, а строка, на которую указывает pmessage
, живет где-то еще.
Последний printf показывает адрес строковый литерал. Если ваш компилятор выполняет «объединение строк», то будет только одна копия строки «сейчас самое время» - и вы увидите, что ее адрес не совпадает с адресом сообщения amessage
. Это потому, что сообщение
получает копию строки при инициализации.
В конце концов, суть в том, что сообщение
сохраняет строку в своем собственная память (в этом примере в стеке), тогда как pmessage
указывает на строку, которая хранится в другом месте.
Второй выделяет строку в некоторой секции ELF, доступной только для чтения. Попробуйте выполнить следующее:
#include <stdio.h>
int main(char argc, char** argv) {
char amessage[] = "now is the time";
char *pmessage = "now is the time";
amessage[3] = 'S';
printf("%s\n",amessage);
pmessage[3] = 'S';
printf("%s\n",pmessage);
}
, и вы получите ошибку сегментации при втором назначении (pmessage [3] = 'S').
Если массив определен так, что его размер доступен во время объявления, sizeof (p) / sizeof (type-of-array)
вернет количество элементов в массиве.
Я не могу добавить что-то полезное к другим ответам, но замечу, что в Deep C Secrets Питер ван дер Линден подробно описывает этот пример. Если вы задаете такие вопросы, я думаю, вам понравится эта книга.
PS Вы можете присвоить новое значение pmessage
. Вы не можете присвоить новое значение сообщению
; он неизменный .
Вместе с памятью для строки "now" "время" распределяется в двух разных местах, вы также должны помнить, что имя массива действует как значение указателя в отличие от переменной указателя , которой является pmessage. Основное отличие состоит в том, что переменная-указатель может быть изменена так, чтобы указывать где-то еще, а массив не может.
char arr[] = "now is the time";
char *pchar = "later is the time";
char arr2[] = "Another String";
pchar = arr2; //Ok, pchar now points at "Another String"
arr = arr2; //Compiler Error! The array name can be used as a pointer VALUE
//not a pointer VARIABLE
Массив содержит элементы. На них указывает указатель.
Первый - это краткая форма выражения
char amessage[16];
amessage[0] = 'n';
amessage[1] = 'o';
...
amessage[15] = '\0';
, то есть это массив, содержащий все символы. Специальная инициализация инициализирует его за вас и автоматически определяет его размер. Элементы массива можно изменять - в нем можно перезаписывать символы.
Вторая форма - это указатель, который просто указывает на символы. Он хранит символы не напрямую. Поскольку массив является строковым литералом, вы не можете взять указатель и записать туда, куда он указывает
char *pmessage = "now is the time";
*pmessage = 'p'; /* undefined behavior! */
Этот код, вероятно, выйдет из строя на вашем ящике. Но он может делать что угодно, потому что его поведение не определено.
Вот гипотетическая карта памяти, показывающая результаты двух объявлений:
0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07
0x00008000: 'n' 'o' 'w' ' ' 'i' 's' ' ' 't'
0x00008008: 'h' 'e' ' ' 't' 'i' 'm' 'e' '\0'
...
amessage:
0x00500000: 'n' 'o' 'w' ' ' 'i' 's' ' ' 't'
0x00500008: 'h' 'e' ' ' 't' 'i' 'm' 'e' '\0'
pmessage:
0x00500010: 0x00 0x00 0x80 0x00
Строковый литерал «сейчас - время» хранится как 16-элементный массив символов по адресу памяти 0x00008000 . Эта память может быть недоступна для записи; лучше предположить, что это не так. Никогда не следует пытаться изменить содержимое строкового литерала.
Объявление
char amessage[] = "now is the time";
выделяет массив из 16 элементов char по адресу памяти 0x00500000 и копирует в него содержимое строкового литерала. Эта память доступна для записи; вы можете изменить содержимое сообщения по своему усмотрению:
strcpy(amessage, "the time is now");
Объявление
char *pmessage = "now is the time";
выделяет единственный указатель на char по адресу памяти 0x00500010 и копирует адрес строкового литерала к нему.
Поскольку pmessage указывает на строковый литерал, его не следует использовать в качестве аргумента для функций, которым необходимо изменить содержимое строки:
strcpy(amessage, pmessage); /* OKAY */
strcpy(pmessage, amessage); /* NOT OKAY */
strtok(amessage, " "); /* OKAY */
strtok(pmessage, " "); /* NOT OKAY */
scanf("%15s", amessage); /* OKAY */
scanf("%15s", pmessage); /* NOT OKAY */
и так далее. Если вы изменили pmessage так, чтобы он указывал на amessage:
pmessage = amessage;
, то его можно использовать везде, где можно использовать сообщение.