增加字符指针

6
以下代码会在第二行导致段错误:
 char *tester = "hello";
 char a = (*tester)++;  // breaks here
 printf("Char is %c\n", a);

以下代码可以正常运行:
 char *tester = "hello";
 a = *tester;
 a++;
 printf("Char is %c\n", a);

 // prints i

为什么不能一次完成?

3
tester 是一个字符指针(char *)。(*tester) 是一个常量字符。你正在尝试对一个常量字符进行递增操作。 - Russley Shaw
7个回答

11

有可能是您在增加错误的东西。 (*tester)++ 增加了指向 tester 的数据。我认为您希望执行的是 *(++tester),它将增加指针并解引用它。


3
Nit: *(tester++) 的值是在增加指针之前指向的内容,它在逻辑上等同于 a = *tester; tester++。如果要获取第二个字符,应该写成 *(++tester) - John Bode
我想将'h'递增到'i'。 - Corry Chapman
@JohnBode 您说得很对。我已经更正了我的答案。 - Taelsin

6
char *tester = "hello";

这里的tester指向一个字符串常量,它存储在只读内存中。任何写入修改都会导致未定义的行为。

如@Taelsin所指出的那样,似乎你想增加指针本身,而不是它所指向的内容。你需要*(++tester)。

在OP澄清后:

如果您想将H递增到I,则(*tester + 1)可以完成工作。


谢谢。不,我实际上想要取消引用'h'并将其递增到'i'。我猜我不明白为什么它是一个字符串常量。你能详细解释一下吗? - Corry Chapman
Tester是一个指向字符串常量的指针。例如,char tester[]="Hello"与char *tester = "Hello"是不同的。 - SandBag_1996
1
@CorryChapman 在 C 语言中,像 "hello" 这样的字符串字面量被存储在只读内存中。你不能直接修改该字符串的内容。这就是为什么在足够高的警告级别下,将该字符串字面量分配给一个非 const 指针(就像你所做的那样)会发出警告。如果你想要修改内容,你必须先将字符串复制到可变数组中:char tester[] = "hello"; - emlai
1
@zenith 详细说明:在 C 语言中,试图写入字符串字面量的地址(例如 "hello")是未定义行为。C 语言没有定义存储 "hello" 的内存类型。它可能存储在只读内存中,也可能存储在可写内存中。这可能会导致错误。这是未定义行为。"Hello" 被称为字符串字面量。 - chux - Reinstate Monica

3
如果您想增加字符,可以将其加1而不是使用增量运算符:
char *tester = "hello";
char a = (*tester) + 1; // instead of (*tester)++
printf("Char is %c\n", a);

希望这是你正在寻找的内容。
解释:
在另一个答案中,zenith 解释了为什么不能使用增量运算符:
像 "hello" 这样的字符串字面值是在只读内存中初始化的。不可能增加它的值。这就是为什么如果要更改它,必须先复制该值。 char a = (*tester) + 1; 获取第一个字符的值,将其加1并保存到 a 中。

谢谢。我看到它可以工作,但不清楚为什么不能一次完成。这是一个语法问题,而不是寻找另一种方法来完成它。 - Corry Chapman
1
@CorryChapman 因为 i++i = i+1 是相同的,所以 i 被修改了,而 a = i+1 不会改变 i。 为什么这很重要? i 是字符串字面值的一个字符,改变它会导致未定义的行为。 - 2501

2

char a = (*tester)++;并没有像你想象的那样增加指针,而是增加了解引用指针,也就是指针所指向的值。

实际上,你正在尝试更改一个字符串字面量,它可能驻留在只读内存中,从而导致未定义的行为。

请参阅n1570,6.4.5字符串字面量:

7如果程序尝试修改这些数组,则未指定这些数组是否不同,只要它们的元素具有适当的值。如果程序尝试修改此类数组,则行为未定义。

要增加指针,请使用char a = *(tester++);,或者仅使用char a = *tester++;,因为它具有运算符优先级。


1
我猜你想做类似于这样的事情。
const char *tmp = "Hello world. This is a great test";
   int count = strlen(tmp);
   while(count>0)
   {
      printf("%s\n",tmp);
      tmp++;
      count--;
   }

0
 char *tester = "hello";

你在这里试图直接创建一个指向字符串的指针。这将导致意外行为(因为它是只读空间)。

相反,我建议你使用字符数组


0

我认为没有一个答案完全回答了op的问题:op想要将'h'解引用为'i',并且他还想知道为什么segmentFault来自他的第一个代码片段。

综合以上答案/评论,我在此列出完整的答案:

A. op的第一个困惑来自于char s[]char *s之间的概念差异:

char *s="hello";是将字符串字面量"hello"放置在只读内存中,并使s成为指向该字符串字面量的指针,从而使任何写操作都非法。而如果定义:

char s[]="hello";是将字符串字面量放置在只读内存中,并将该字符串复制到堆栈上的另一个分配内存中,以便您可以直接在堆栈内存上读取/写入(但仍未触及只读内存)。

*tester 解引用会给你一个常量字符,它是位于只读内存中的字符串字面量的第一个字母,这就是为什么尝试执行 char a = (*tester)++ 会失败的原因,因为这是在只读内存上进行指针操作,行为未定义。

B. 第二个代码片段为什么能正常工作?

a = *tester; 意味着你声明了另一个变量 char a,然后在堆栈内存上分配一些空间用于 char 并将其初始化为 'h';这相当于以下内容:

char a;
a = 'h';

当然你可以对 a 进行递增。
这是一个显式打印地址的示例:运行链接
#include <stdio.h>

int main()
{
    char * tester = "hello";
    printf("tester first  letter address=%p\n",  tester);       // print out address should be something like 0x400648
    printf("tester second letter address=%p\n", (tester+1));    // print out address should be something like 0x400649

    char a;
    a = *tester;
    printf("a address=%p\n", &a);                               // print out address should be something like 0x7ffe0bc6aab7
    a++;        
    printf("a address=%p\n", &a);                               // print out address should be something like 0x7ffe0bc6aab7

    return 0;
}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接