【问题标题】:Why ++str operation causes error if array is char *str[] but not in case of char *argv[] (one of the argument to the main function)?如果数组是 char *str[] 而不是 char *argv[] (主函数的参数之一),为什么 ++str 操作会导致错误?
【发布时间】:2025-11-27 05:50:02
【问题描述】:

我编写了一小段代码来了解如何遍历 char 指针数组和 2D 字符数组。我用过 1]2] 在打印 o/p 只跟踪哪个代码块打印什么时,请忽略它们。

打印 o/p 的类似代码用于遍历 3 个不同(类型)的数组:-

a) 字符指针数组:char *argv[]

b) 字符指针数组:char *str[]

c) 二维字符数组:char strarr[][7]

我执行的基本操作如下: if char *foo[] then

A) foo 指向 foo 的第一个元素(字符串)(对此表示怀疑)

B) *++foo 指向 foo 的下一个元素的第一个字符

C) **++foo 给出 foo 的下一个元素的第一个字符

D) ++*foo 指向 foo 元素中的下一个字符

E) **++*foo 给出 foo 元素内的下一个字符

只有在 char *argv[] 的情况下才允许操作 A、B、C 和 D。 char *str[] 只允许操作 C & D;而二维数组 char strarr[][7] 不允许所有 A-D。

我明白为什么 char *str[] 允许 C 和 D 操作,而 char strarr[][7] 则不允许: 由于 char *str[] 是一个数组指针,并且它的元素(即地址到字符串的值)的值的增加是允许的,而在 char strarr[]2[7] 是元素的二维数组的情况下,它不可能增加其元素的地址。

纠正我的任何错误假设。

但我无法理解为什么 **++str、*++str 或 ++str 是不允许的,而 **++argv *++argv 或 ++argv 是允许的?

argv 和 str 都是 char 指针数组,那么为什么会有这种不同的行为呢? (str 指向 str[0] 那么为什么 ++str 会导致错误而不是指向 str[1]?) char *argv[] 是某种特殊类型的数组还是以其他方式/方法对其执行的操作?

当我编写 **++str、*++str 或 ++str 时,编译器会给出以下错误:

gcc -Wall -c "TestProg.c"(在目录中: /home/crownedeagle/Downloads/CDAC C-CAT/C - K&R 解决方案)TestProg.c: 在函数'main'中:TestProg.c:61:33: error: lvalue required as 递增操作数 printf("\n1]\n**++str = %c", **++str); ^

代码如下:

//Pointer of Array and Array Pointer Experimentation

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

int main(int argc, char *argv[])
{
    int i;
    if (argc == 7)
    {
        printf("\n");
        for (i = 0; i < argc; i++)
            printf("%s\t\t%p\n", argv[i], argv[i]);

        printf("\n0]\n**argv = %c", **argv);
        printf("\n*argv = %s", *argv);

        printf("\n1]\nargv = %p", argv);
        printf("\n*argv = %s", *argv);
        printf("\n**++argv = %c", **++argv);
        printf("\nargv = %p", argv);
        printf("\n*argv = %s", *argv);
        printf("\n**++argv = %c", **++argv);
        printf("\nargv = %p", argv);
        printf("\n*argv = %s", *argv);      

        printf("\n2]\nargv = %p", argv);
        printf("\n&(argv[0]) = %p", &(argv[0]));
        printf("\n*argv = %p", *argv);
        printf("\n&(**argv) = %p", &(**argv));

        printf("\n*++*argv = %c", *++*argv);
        printf("\n*argv = %s", *argv);

        printf("\nargv = %p", argv);
        printf("\n&(argv[0]) = %p", &(argv[0]));
        printf("\n*argv = %p", *argv);
        printf("\n&(**argv) = %p", &(**argv));

        printf("\nargv[0] = %s", argv[0]);

        printf("\n\n");

        for (i = 0; i < argc; i++)
            printf("%s\t\t%p\n", argv[i], argv[i]);
    }

    char *str[] = {"ABCDEF", "HIJKLM", "OPQRST", "VWXYZA", "123456"};

    printf("\n");
    for (i = 0; i < 5; i++)
            printf("%s\t\t%p\n", str[i], str[i]);

    printf("\n0]\n**str = %c", **str);
    printf("\n*str = %s", *str);

    //printf("\n1]\n**++str = %c", **++str);
    //printf("\n*str = %s", *str);

    printf("\n1]\nstr = %p", str);
    printf("\n&(str[0]) = %p", &(str[0]));
    printf("\n*str = %p", *str);
    printf("\n&(**str) = %p", &(**str));

    printf("\n*++*str = %c", *++*str);
    printf("\n*str = %s", *str);

    printf("\nstr = %p", str);
    printf("\n&(str[0]) = %p", &(str[0]));
    printf("\n*str = %p", *str);
    printf("\n&(**str) = %p", &(**str));

    printf("\nstr[0] = %s", str[0]);

    printf("\n\n");

    for (i = 0; i < 5; i++)
            printf("%s\t\t%p\n", str[i], str[i]);

    char strarr[][7] = {"ABCDEF", "HIJKLM", "OPQRST", "VWXYZA", "123456"};

    printf("\n");
    for (i = 0; i < 5; i++)
            printf("%s\t\t%p\n", strarr[i], strarr[i]);

    printf("\n0]\n**strarr = %c", **strarr);
    printf("\n*strarr = %s", *strarr);

    //printf("\n1]\n**++strarr = %c", **++strarr);
    //printf("\n*strarr = %s", *strarr);

    printf("\n1]\nstrarr = %p", strarr);
    printf("\n&(strarr[0]) = %p", &(strarr[0]));
    printf("\n*strarr = %p", *strarr);
    printf("\n&(**strarr) = %p", &(**strarr));

    //printf("\n*++*strarr = %c", *++*strarr);
    printf("\n*strarr = %s", *strarr);

    printf("\nstrarr = %p", strarr);
    printf("\n&(strarr[0]) = %p", &(strarr[0]));
    printf("\n*strarr = %p", *strarr);
    printf("\n&(**strarr) = %p", &(**strarr));

    printf("\nstrarr[0] = %s", strarr[0]);

    printf("\n\n"); 

    for (i = 0; i < 5; i++)
            printf("%s\t\t%p\n", strarr[i], strarr[i]);

    return 0;   
}

代码输出为:

crownedeagle@EagleNest:~/C - K&R Solutions$ ./TestProg abcdef hijklm opqrst vwxyza 123456 789012

    ./TestProg    0x7ffd61c027c7
    abcdef        0x7ffd61c027d2
    hijklm        0x7ffd61c027d9
    opqrst        0x7ffd61c027e0
    vwxyza        0x7ffd61c027e7
    123456        0x7ffd61c027ee
    789012        0x7ffd61c027f5

    0]
    **argv = .
    *argv = ./TestProg
    1]
    argv = 0x7ffd61c01c98
    *argv = ./TestProg
    **++argv = a
    argv = 0x7ffd61c01ca0
    *argv = abcdef
    **++argv = h
    argv = 0x7ffd61c01ca8
    *argv = hijklm
    2]
    argv = 0x7ffd61c01ca8
    &(argv[0]) = 0x7ffd61c01ca8
    *argv = 0x7ffd61c027d9
    &(**argv) = 0x7ffd61c027d9
    *++*argv = i
    *argv = ijklm
    argv = 0x7ffd61c01ca8
    &(argv[0]) = 0x7ffd61c01ca8
    *argv = 0x7ffd61c027da
    &(**argv) = 0x7ffd61c027da
    argv[0] = ijklm

    ijklm         0x7ffd61c027da
    opqrst        0x7ffd61c027e0
    vwxyza        0x7ffd61c027e7
    123456        0x7ffd61c027ee
    789012        0x7ffd61c027f5
    (null)        (nil)
    XDG_VTNR=7    0x7ffd61c027fc

    ABCDEF        0x400dfa
    HIJKLM        0x400e01
    OPQRST        0x400e08
    VWXYZA        0x400e0f
    123456        0x400e16

    0]
    **str = A
    *str = ABCDEF
    1]
    str = 0x7ffd61c01b80
    &(str[0]) = 0x7ffd61c01b80
    *str = 0x400dfa
    &(**str) = 0x400dfa
    *++*str = B
    *str = BCDEF
    str = 0x7ffd61c01b80
    &(str[0]) = 0x7ffd61c01b80
    *str = 0x400dfb
    &(**str) = 0x400dfb
    str[0] = BCDEF

    BCDEF         0x400dfb
    HIJKLM        0x400e01
    OPQRST        0x400e08
    VWXYZA        0x400e0f
    123456        0x400e16

    ABCDEF        0x7ffd61c01b50
    HIJKLM        0x7ffd61c01b57
    OPQRST        0x7ffd61c01b5e
    VWXYZA        0x7ffd61c01b65
    123456        0x7ffd61c01b6c

    0]
    **strarr = A
    *strarr = ABCDEF
    1]
    strarr = 0x7ffd61c01b50
    &(strarr[0]) = 0x7ffd61c01b50
    *strarr = 0x7ffd61c01b50
    &(**strarr) = 0x7ffd61c01b50
    *strarr = ABCDEF
    strarr = 0x7ffd61c01b50
    &(strarr[0]) = 0x7ffd61c01b50
    *strarr = 0x7ffd61c01b50
    &(**strarr) = 0x7ffd61c01b50
    strarr[0] = ABCDEF

    ABCDEF        0x7ffd61c01b50
    HIJKLM        0x7ffd61c01b57
    OPQRST        0x7ffd61c01b5e
    VWXYZA        0x7ffd61c01b65
    123456        0x7ffd61c01b6c

【问题讨论】:

  • 如果我理解这个问题,我相信这与数组作为参数传递给函数“衰减”到指针这一事实有关。 Why do arrays in C decay to pointers?
  • 关于这种说法:printf("\n&amp;(str[0]) = %p", &amp;(str[0]));这是试图获取地址的地址。编译器输出:`71:28: 警告:格式“%p”需要“void *”类型的参数,但参数 2 的类型为“char **”[-Wformat=]
  • 关于这种声明:printf("\n&amp;(strarr[0]) = %p", &amp;(strarr[0])); 这是试图获取地址的地址编译器输出::94:29: warning: format ‘%p’ expects argument of type ‘void *’, but argument 2 has type ‘char (*)[7]’ [-Wformat=]
  • 对于 OP 发布的代码,编译器会输出 15 个警告(如上述 2 个 cmets 所示)。编译时,始终启用警告,然后修复这些警告。 (对于gcc,至少使用:-Wall -Wextra -Wconversion -pedantic -std=gnu11)注意:其他编译器使用不同的选项来产生相同的结果

标签: c arrays string


【解决方案1】:

在这个函数声明中

int main(int argc, char *argv[])

编译器将具有未知大小类型数组的参数调整为指向元素类型的指针。也就是这个声明等价于

int main(int argc, char **argv )

所以在函数中你正在处理一个指针,你可以应用增量或赋值运算符。

例如这些函数声明

void f( int a[100] );
void f( int a[10] );
void f( int a[] );

声明同一个函数,所有声明都被编译器调整为声明

void f( int *a );

来自 C 标准(6.7.6.3 函数声明符(包括原型))

7 应调整参数声明为“类型数组” to ‘‘qualified pointer to type’’,类型限定符(如果有的话) 是在数组类型派生的 [ 和 ] 中指定的那些。 如果关键字 static 也出现在数组的 [ 和 ] 中 类型推导,然后对于函数的每次调用, 相应的实际参数应提供对第一个的访问 数组的元素至少与指定的元素一样多 大小表达式。

在此声明中

char *str[] = {"ABCDEF", "HIJKLM", "OPQRST", "VWXYZA", "123456"};

声明了一个char * [5] 类型的数组。数组是不可修改的左值。所以你可能不适用于数组增量或赋值运算符。

【讨论】:

  • 在答案的最后一行中,char * [5] 部分中 [ 和 ] 中的值应该是什么?它是字符串长度,应该是 6?还是 7 个?
  • @CrownedEagle 是 char * 类型的大括号中的初始值设定项的数量。所以数组的类型是 char *[5]
  • 好的。。谢谢。。让我再看一遍你的答案,试着理解它。
【解决方案2】:

泌尿运算符的执行顺序是从右到左所以:

* & + - ! ~ ++expr --expr

表示先执行 ++,然后执行 **。

关于:

     printf("\n1]\n**++str = %c", **++str);

所以,++str 导致 str[1] 指针

通过 '*' 取消引用一次会导致 str[1] 指向的字符串

通过 '*' 进行的第二次取消引用会导致垃圾为字符串,其中:str[1] 点不是指针

【讨论】:

  • 由于我使用的是 %c 格式说明符,第二次取消引用不应该给出 str[1] 的第一个字符吗?
最近更新 更多