Как изменить значение указателя между функциями - PullRequest
1 голос
/ 23 сентября 2019

Я новичок в C, и я не понимаю, как изменить значение char * между функциями.

Например:

#include <stdio.h>
void testing(char *s) {
        s = "hi";
} // testing

int main() {
        char *s = "hello";
        testing(s);
        printf("%s\n", s);
} // main

В этом случае значениеЗначение s не изменяется, и печатается "hello".

Как изменить s, не изменяя тип возвращаемого значения или параметры какой-либо функции?

Ответы [ 3 ]

4 голосов
/ 23 сентября 2019

C вызовы функций строго передаются по значению.Это означает, что когда вы передаете s в testing, создается локальная копия.Поэтому присвоение новому значению s в функции не повлияет на значение в main.

. Поскольку значение указателя копируется, вы можете получить доступ к памяти, на которую он указывает.Если s указывает на память, которая не предназначена только для чтения, вы можете скопировать содержимое одного буфера в другой.Например, вместо s = "hi";,

strcpy(s, "hi");

В main оператор char *s = "hello";, вероятно, будет s указывать на буфер только для чтения.Чтобы сделать его доступным для записи буфером, объявите вместо него s как массив:

 char s[] = "hello";

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

#include <stdio.h>
void testing(char **s) {
        *s = "hi";
} // testing

int main() {
        char *s = "hello";
        testing(&s);
        printf("%s\n", s);
} // main

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

#include <stdio.h>
char *testing(char *s) {
        s = "hi";
        return s;
} // testing

int main() {
        char *s = "hello";
        s = testing(s);
        printf("%s\n", s);
} // main
3 голосов
/ 23 сентября 2019

В main, char *s инициализируется строковым литералом "hello".Это означает, что s будет указывать на анонимный массив 6 char, содержащий значения { 'h', 'e', 'l', 'l', o', '\0' }.Тем не менее, содержимое этого массива не может быть изменено с точки зрения стандарта, поэтому любой код, который записывает в этот массив, демонстрирует неопределенное поведение .

В вызове testing,параметр char *s рассматривается как локальная переменная внутри функции.Любое изменение значения указателя не повлияет на переменную char *s в main.В вызовах функций C все параметры передаются по значению .

В пределах testing вы изменяете s, чтобы указывать на другой строковый литерал "hi".Подобно инициализации в main, testing s теперь будет указывать на анонимный массив 3 char, содержащий значения { 'h', 'i', '\0' }.Содержимое этого анонимного массива не подлежит изменению.Как упоминалось выше, это не влияет на указатель main s.

Вы заявили, что не хотите изменять параметры или возвращать тип какой-либо функции.Для этого testing потребуется перезаписать содержимое массива, на которое указывает его параметр s.Поскольку текущий код в main имеет s, указывающий на немодифицируемый массив, вам необходимо изменить его, чтобы он указывал на модифицируемый массив.Это можно сделать, изменив main s в массив вместо указателя:

int main() {
        char s[] = "hello"; // N.B. s[] is 6 chars long including the null terminator
        testing(s);
        printf("%s\n", s);
} // main

Затем вы можете изменить testing, чтобы перезаписать содержимое массива, на которое указывает его параметрs:

#include <string.h>

void testing(char *s) {
        strcpy(s, "hi");
} // testing

Я использовал стандартную библиотечную функцию strcpy в вышеприведенной версии testing.

Вы должны убедиться, что вы делаетене пишите слишком много char s в массив, на который указывает s.

Длина массива s в main была автоматически установлена ​​на 6 инициализатором "hello" (5 символов плюс aнулевой символ-терминатор).Вы можете установить длину массива в явном виде, если вам нужно перезаписать более 6 char с, включая нулевой терминатор:

    char s[100] = "hello";  // s[100] has space for 99 characters plus a null terminator.
2 голосов
/ 23 сентября 2019

Функции получают только копии значений аргументов:

void f(int x)          // x is a new variable that is initialized to the argument value.
{
    x = x + 3;         // Adds 3 to the local x.
    printf("%d\n", x); // Prints 8.
}

int main(void)
{
    int x = 5;
    f(x);              // Passes 5, not x, to f.
    printf("%d\n", x); // Prints 5, not 8.
}

Вы можете получить значение из функции, вернув его:

int f(int x)           // Function is changed to return int.
{
    x = x + 3;         // Adds 3 to the local x.
    printf("%d\n", x); // Prints 8.
    return x;          // Returns new value.
}

int main(void)
{
    int x = 5;
    x = f(x);          // Set x from the value returned by the function.
    printf("%d\n", x); // Prints 8.
}

Вы также можете получить значение изфункция с помощью функции изменить значение объекта.Для этого вы должны предоставить адрес объекта функции:

void f(int *p)          // p is a pointer to an int.
{
    *p = *p + 3;        // Adds 3 to the pointed-to int.
    printf("%d\n", *p); // Prints 8.
}

int main(void)
{
    int x = 5;
    f(&x);             // Passes address of x to f.
    printf("%d\n", x); // Prints 8.
}

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

void f(char **p)       // p is a pointer to a pointer to a char.
{
    *p = "hi"          // Sets *p to point to (first character of) "hi".
    printf("%s\n", x); // Prints "hi".
}

int main(void)
{
    char *s = "hello"; // Sets s to point to (first character of) "hello".
    f(&s);             // Passes address of s to f.
    printf("%s\n", x); // Prints "hi".
}
...