Почему этот код для изменения строки не работает? - PullRequest
5 голосов
/ 03 июня 2009

Как вы назначаете символ в адресе памяти, на который указывает символьный указатель, в строках в стиле c? Например, в приведенном ниже примере я хочу изменить num на «123456», поэтому я попытался установить p на цифру, где находится «0», и я пытаюсь перезаписать ее с «4». Спасибо.

#include <stdio.h>
#include <stdlib.h>

int main()
{
    char* num = (char*)malloc(100);
    char* p = num;

    num = "123056";

    p = p+3;    //set pointer to where '4' should be
    p = '4';

    printf("%s\n", num );

    return 0;
}

Ответы [ 5 ]

13 голосов
/ 03 июня 2009

Прежде всего, когда вы делаете:

num = "123056";

Вы не копируете строку «123056» в область кучи, выделенную malloc(). В C присвоение указателю char * строкового литерала равнозначно установке его в качестве константы, то есть идентично:

char str[] = "123056";

Итак, то, что вы только что достигли, это то, что вы отказались от своей единственной ссылки на область кучи размером 100 байт, выделенную malloc(), поэтому ваш следующий код не выводит правильное значение; «p» по-прежнему указывает на область кучи, выделенную malloc() (поскольку num указывает на нее во время назначения), но num больше не делает.

Я предполагаю, что вы на самом деле намеревались скопировать строку "123056" в эту область кучи. Вот как это сделать:

strcpy(num, "123056");

Хотя, это лучшая практика по ряду причин:

strncpy(num, "123056", 100 - 1);  /* leave room for \0 (null) terminator */

Если вы только что сделали:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int     main(void) {
        char    *num = malloc(100);
        char    *p = num;

        strncpy(num, "123056", 100 - 1);

        p = p + 3;
        *p = '4';

        printf("%s\n", num);

       return 0;
} 

Вы бы получили правильный результат:

123456

Вы можете заключить эту операцию:

p = p + 3;
*p = '4';

... и избегайте итерации указателя, ссылаясь следующим образом:

*(p + 3) = '4';

Несколько других заметок:

  • Хотя обычная стилистическая практика, приведение возвращаемого значения от malloc() к (char *) не является необходимым. Преобразование и выравнивание типа void * гарантируется языком C.

  • ВСЕГДА проверяйте возвращаемое значение malloc(). Это будет NULL, если выделение кучи не удалось (т. Е. У вас недостаточно памяти), и в этот момент ваша программа должна завершиться.

  • В зависимости от реализации область памяти, выделенная malloc(), может содержать устаревший мусор в определенных ситуациях. Всегда полезно обнулять его после выделения:

    memset(num, 0, 100);
    
  • Никогда не забывайте free() свою кучу! В этом случае программа закроется, и ОС очистит ваш мусор, но если вы не освоитесь, у вас мгновенно возникнут утечки памяти.

Итак, вот «лучшая практика» версия:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int     main(void) {
        char    *num, *p;

        /*
         * Don't take 1-byte chars for granted - good habit to get into.
         */

        num = malloc(sizeof(char) * 100);

        if(num == NULL)
                exit(1);

        memset(num, 0, sizeof(char) * 100);

        p = num;

        strncpy(num, "123056", 100 - 1);

        *(p + 3) = '4';

        printf("%s\n", num);

        free(num);

        return 0;
}
12 голосов
/ 03 июня 2009

Этот код не будет работать, просто потому что строка:

num = "123056";

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

Вам необходимо следующее:

#include <stdio.h>
#include <stdlib.h>
int main (void) {
    char *num = malloc (100); // do not cast malloc return value.
    char *p = num;

    strcpy (num, "123056");   // populate existing block with string.

    p = p + 3;                // set pointer to where '0' is.
    *p = '4';                 // and change it to '4'.

    printf ("%s\n", num );    // output it.

    return 0;
}
1 голос
/ 03 июня 2009

В дополнение к проблеме * p, на которую указывали другие, у вас также есть проблемы с использованием памяти. У вас есть один буфер размером 100 байт с неизвестным содержимым. У вас есть еще один буфер размером 7 байт, содержащий строку «123056» и нулевой терминатор. Вы делаете это:

  1. num установлен для указания на 100-байтовый буфер
  2. p установлен для указания числа; т.е. он указывает на 100-байтовый буфер
  3. вы сбрасываете num для указания на 7-байтовый буфер; p по-прежнему указывает на 100-байтовый буфер
  4. Вы используете p для изменения 100-байтового буфера
  5. затем вы используете num для распечатки 7-байтового буфера

То есть вы не печатаете тот же буфер, который изменяете.

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

Ну, вы знаете, p это тип указателя. Он хранит адрес символа «0». Если вы назначите p значение «4». Это займет «4» в качестве адреса. Тем не менее, адрес «4» является незаконным. Вы можете использовать оператор '*', чтобы получить значение, по которому хранится адрес, сохраненный в p.

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

Просто используйте *p = '4'; ...!

...