Почему мы не можем скопировать строку в символьный указатель, КОГДА мы можем назначить строку непосредственно ей? - PullRequest
5 голосов
/ 08 февраля 2010

Этот код выдает "p = hello world":

#include "stdio.h"
#include "string.h"

int main(){
    char *p;
    p="hello world";
    printf("p is %s \n",p);
    return 0;
}

Но этот код вызывает ошибку сегментации:

#include "stdio.h"
#include "string.h"

int main() { 
    char *p;
    strcpy(p,"hello");
    printf("p is %s \n",p);
    return 0;
}

и этот код выдает "p = hello"

#include "stdio.h"
#include "string.h"
#include "stdlib.h"
int main() {
  char *p;
  p=(char*)malloc (10);
  strcpy(p,"hello");
  printf("p is %s \n",p);
  return 0;

}

Ответы [ 7 ]

12 голосов
/ 08 февраля 2010

В случае, когда p="hello world"; (1-й случай во время этого редактирования), p инициализируется, чтобы указывать на область памяти только для чтения , которая содержит строку «hello world» (строковый литерал). Эта область памяти только для чтения создается во время компиляции.

В случае, когда возникает ошибка сегментации (2-й случай во время этого редактирования), p неинициализирован, и копирование чего-либо в него приведет к непредсказуемым результатам, поскольку местоположение в памяти, на которое указывает p, не является определяется кодом.

Прежде чем вы сможете скопировать строку в p, вы должны указать память, на которую указывает p.

Вы можете выделить эту память в стеке

char buf[BUFSIZ] = ""; /* local variable */

в куче

char *buf = malloc(BUFSIZ); /* don't forget to free */

или в сегменте __DATA.

static char buf[BUFSIZ] = ""; /* global variable */

Затем можно инициализировать p, чтобы он указывал на буфер памяти.

char *p = buf;

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

Примечание: Я намеренно создал отдельный буфер и инициализировал p, чтобы указать на него, чтобы помочь мне понять.

5 голосов
/ 08 февраля 2010

Причина в том, что когда вы объявляете указатель, он фактически не указывает на что-либо полезное. strcpy требуется блок памяти для копирования строки. Он не сделает это автоматически.

Из документации (выделено мое):

Копирует строку C, указанную источником в массив, указанный пунктом назначения, включая завершающий ноль характер.

Чтобы избежать переполнения, размер массив, указанный пунктом назначения, должен быть достаточно долго, чтобы содержать один и тот же C строка в качестве источника (включая завершающий нулевой символ) , и не должен перекрываться в памяти с источник.

Вы должны сделать это правдой, поскольку это является предварительным условием функции.

Также в разделе параметров:

1019 * назначения *

Pointer to the destination array where the content is to be copied.

Вы должны убедиться, что destination является указателем на массив.

2 голосов
/ 09 февраля 2010

Обратите внимание на то, что общего у двух рабочих примеров: у них есть строка p =, которая присваивает что-то p. Нерабочий пример этого не делает.

Рассмотрим эту строку (из первого примера):

p = "hello world";

Хотя может показаться, что это «копирование строки в указатель на символ», это не так. Он копирует расположение строки в указатель на символ. Вот что хранит указатель на символ, такой как p - местоположение непрерывного блока char s.

Аналогично, рассмотрим эту строку из третьего примера:

p = malloc(10);

Это также копирование местоположения - это копирование местоположения блока из 10 неявных char с в p.

strcpy(dest, source) копирует символы из местоположения, заданного source, в местоположение, заданное dest. Должно быть понятно, что если вы никогда не установите p в правильное местоположение, то strcpy(p, "hello") не сможет сделать ничего разумного - во втором примере p является по существу случайным местом, и вы затем спросите strcpy() скопируйте что-нибудь в это место.

2 голосов
/ 08 февраля 2010

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

Если вы абсолютно хотите избежать динамического выделения памяти и , вы знаете размер исходной строки во время компиляции , вы можете получить соответствующее пространство стека следующим образом:

char buffer[6]; /* strlen( "hello" ) + 1 for zero terminator */
strcpy( buffer, "hello" );

Но это опасная дорога, ведущая к переполнению буфера .

2 голосов
/ 08 февраля 2010

Бесплатного ланча нет - вам нужно захватить память и управлять ею. Если вы просто предполагаете, что, поскольку у вас есть доступ к памяти указателя, то вы столкнетесь с неопределенным поведением (вероятно, segfault).

1 голос
/ 08 февраля 2010

Да, это раздражает. Вы можете использовать strdup, чтобы сократить его:

char *p = strdup("hello");
printf("p is %s \n",p);
1 голос
/ 08 февраля 2010

Есть две отдельные части для копирования в память. Первый - это память, занимаемая элементом, который вы хотите скопировать (который вы создаете в своем примере с помощью функции malloc ()), а второй - указатель на этот блок памяти (который вы называете p). Эти два объекта также должны быть настроены для пункта назначения, прежде чем вы сможете сделать копию. В вашем первом неудачном примере вы не настроили блок памяти для места назначения (но он был неявно установлен для источника при объявлении строки hello).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...