可以在C中修改字符字符串吗?

可以在C中修改字符字符串吗?,c,string,debugging,C,String,Debugging,我花了几个小时阅读了各种C教程和与指针相关的书籍,但我真正想知道的是,一旦创建了一个char指针,是否有可能更改它 这就是我尝试过的: char *a = "This is a string"; char *b = "new string"; a[2] = b[1]; // Causes a segment fault *b[2] = b[1]; // This almost seems like it would work but the compiler throws an error.

我花了几个小时阅读了各种C教程和与指针相关的书籍,但我真正想知道的是,一旦创建了一个char指针,是否有可能更改它

这就是我尝试过的:

char *a = "This is a string";
char *b = "new string";

a[2] = b[1]; // Causes a segment fault

*b[2] = b[1]; // This almost seems like it would work but the compiler throws an error.

那么,有没有办法更改字符串中的值而不是指针地址呢?

您需要将字符串复制到另一个非只读内存缓冲区中,然后在那里进行修改。使用strncpy()复制字符串,strlen()检测字符串长度,malloc()和free()为新字符串动态分配缓冲区

例如(类似C++的伪代码):


不,您不能修改它,因为字符串可以存储在只读内存中。如果你想修改它,你可以使用一个数组来代替

char a[] = "This is a string";
或者,您也可以使用malloc分配内存,例如

char *a = malloc(100);
strcpy(a, "This is a string");
free(a); // deallocate memory once you've done

a&b的内存不是由您分配的。编译器可以自由选择只读内存位置来存储字符。因此,如果您试图改变它可能会导致seg故障。所以我建议你自己创建一个角色数组。类似于:
chara[10];strcpy(a,“你好”)

当您在源代码中编写“字符串”时,它会直接写入可执行文件,因为该值需要在编译时知道(有工具可用于将软件分离并查找其中的所有纯文本字符串)。编写
char*a=“This is a string”
时,“This is a string”的位置在可执行文件中,而
a
指向的位置在可执行文件中。可执行映像中的数据是只读的

您需要做的是(正如其他答案所指出的)在一个非只读的位置创建内存——在堆上或堆栈框架中。如果声明一个本地数组,则在堆栈上为该数组的每个元素创建空间,并将字符串文本(存储在可执行文件中)复制到堆栈中的该空间

char a[] = "This is a string";
您还可以手动复制该数据,方法是在堆上分配一些内存,然后使用
strcpy()
将字符串文本复制到该空间中

char *a = malloc(256);
strcpy(a, "This is a string");
无论何时使用
malloc()
分配空间,请记住在使用完
free()
后调用它(读取:内存泄漏)


基本上,您必须跟踪数据所在的位置。无论何时在源代码中编写字符串,该字符串都是只读的(否则可能会更改可执行文件的行为——想象一下,如果您编写
char*a=“hello”
,然后将
a[0]
更改为
'c'
,然后在其他地方编写
printf(“hello”)如果允许您更改
“hello”
的第一个字符,而编译器只存储了一次(应该),那么
printf(“hello”);
将输出
cello
!)

许多人会对char*和char[]以及C中的字符串文字之间的区别感到困惑。当您编写:

char *foo = "hello world";
…实际上,您将foo指向一个恒定的内存块(实际上,编译器在这个实例中对“helloworld”所做的操作取决于实现。)


相反,使用char[]告诉编译器您想要创建一个数组,并用内容“helloworld”填充它。foo是指向char数组第一个索引的指针。它们都是字符指针,但只有char[]将指向本地分配的可变内存块。

您的问题似乎已经得到了回答,但现在您可能想知道为什么char*a=“String”存储在只读内存中。实际上,c99标准没有对其进行定义,但大多数编译器选择这种方式来处理以下实例:

printf("Hello, World\n");
[第130页,第6.7.8节]:

宣言:

char s[] = "abc", t[3] = "abc";
定义“普通”字符数组对象s和t,其元素用字符串文字初始化。 此声明与char相同

s[] = { 'a', 'b', 'c', '\0' }, t[] = { 'a', 'b', 'c' };
数组的内容是可修改的。另一方面,宣言

char *p = "abc";

定义类型为“指针指向字符”的p,并将其初始化为指向长度为4的类型为“字符数组”的对象,该对象的元素用字符串文字初始化。如果试图使用p来修改数组的内容,则行为是未定义的。

所有这些都是很好的答案,解释了为什么不能修改字符串文字,因为它们位于只读内存中。然而,当遇到紧急情况时,有一种方法可以做到这一点。看看这个例子:

#include <sys/mman.h>
#include <unistd.h>
#include <stddef.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

int take_me_back_to_DOS_times(const void *ptr, size_t len);

int main()
{
    const *data = "Bender is always sober.";
    printf("Before: %s\n", data);
    if (take_me_back_to_DOS_times(data, sizeof(data)) != 0)
        perror("Time machine appears to be broken!");
    memcpy((char *)data + 17, "drunk!", 6);
    printf("After: %s\n", data);

    return 0;
}

int take_me_back_to_DOS_times(const void *ptr, size_t len)
{
    int pagesize;
    unsigned long long pg_off;
    void *page;

    pagesize = sysconf(_SC_PAGE_SIZE);
    if (pagesize < 0)
        return -1;
    pg_off = (unsigned long long)ptr % (unsigned long long)pagesize;
    page = ((char *)ptr - pg_off);
    if (mprotect(page, len + pg_off, PROT_READ | PROT_WRITE | PROT_EXEC) == -1)
        return -1;
    return 0;
}
#包括
#包括
#包括
#包括
#包括
#包括
int take_me_back_to DOS_times(const void*ptr,size_t len);
int main()
{
const*data=“本德总是清醒的。”;
printf(“之前:%s\n”,数据);
如果(返回到待办事项时间(数据,大小(数据))!=0)
佩罗尔(“时间机器似乎坏了!”);
memcpy((char*)数据+17,“喝醉了!”,6);
printf(“在:%s\n之后”,数据);
返回0;
}
int take_me_back_to DOS_times(const void*ptr,size_t len)
{
int页面大小;
未签名的长PGU关闭;
作废*页;
pagesize=sysconf(_SC_PAGE_SIZE);
如果(页面大小<0)
返回-1;
pg_off=(无符号长)ptr%(无符号长)页面大小;
页面=((字符*)ptr-pg_关闭);
如果(保护(第页,len+pg_关闭,保护读取|保护写入|保护执行)=-1)
返回-1;
返回0;
}
我写这篇文章是作为的一部分,你可能会觉得有趣(我希望:)


希望能有帮助。祝你好运

您也可以使用
strdup

   The strdup() function returns a pointer to a new string which is a duplicate of the string  s.
   Memory for the new string is obtained with malloc(3), and can be freed with free(3).
例如:

char *a = strdup("stack overflow");

字符数组的问题是我将字符数组的指针传递给一个函数,这样我就可以在那里操作一个字符串,然后再将它发送出去。很遗憾,我不得不使用malloc。不,您仍然可以使用堆栈上分配的对象。例如,如果有一个函数void f(char*p);然后从main()可以传递f(a)。这将把第一个字符的地址传递给函数
   The strdup() function returns a pointer to a new string which is a duplicate of the string  s.
   Memory for the new string is obtained with malloc(3), and can be freed with free(3).
char *a = strdup("stack overflow");