c - argv 中指向字符串的指针是否可修改?

标签 c pointers language-lawyer argv

这个问题在这里已经有了答案:





Is argv[n] writable?

(4 个回答)


5年前关闭。




最近(2016 年 1 月,以防问题持续很长时间)我们遇到了问题 Are the strings in argv modifiable? .
在评论区给this回答,我们(@2501 和我)争论它是否真的是 字符串 (示例字符是 **argv )可修改或 指向字符串的指针 (示例指针是 *argv )。

适当的标准引用来自 C11 标准草案 N1570,§5.1.2.2.1/2:

The parameters argc and argv and the strings pointed to by the argv array shall be modifiable by the program, and retain their last-stored values between program startup and program termination.



指向由 argv 指向的字符串的指针也是如此。可修改?

最佳答案

正如问题中引用的 OP 一样,C11 标准明确指出 argcargv变量,以及 argv 指向的字符串数组,可修改。这些指针是否可修改,是手头的问题。该标准似乎没有以一种或另一种方式明确说明它。

关于标准中的措辞,有两个关键点需要注意:

  • 如果指针应该是不可变的,标准本可以通过要求将 main 声明为 int main(int argc, char *const argv[]) 来说明这一点。 , 如 haccks mentioned在这个问题的另一个答案中。

    事实上,标准中没有任何地方是 constargv 相关的提及似乎是故意的。即缺少const似乎不是可选的,而是由标准规定的。
  • 标准电话argv始终是一个数组。修改数组是指修改其成员。因此,标准中的措辞显然是指修改argv中的成员。数组,当它指出 argv是可修改的。

    另一方面,C 中的数组参数(基于 C11 草案 N1570,第 6.7.6.3p7 节)“应调整为‘限定类型的指针’”。因此,下面的代码,
    int foo(int x[2], int y[2])
    {
        if (x[0] > y[0])
            x = y;
        return x[1];
    }
    

    是有效的 C11,因为 xy调整为 int *xint *y , 分别。 (这也在 C11 草案 N1570 的第 6.3.2.1p3 节中重申:“......数组......被转换为一个类型为'pointer to type'的表达式,它指向数组的初始元素......”。 )
    显然,如果 x 就不是这样了。和 y被声明为局部或全局数组,而不是函数参数。

  • 就语言律师主义而言,我会说标准并没有以一种或另一种方式说明它,尽管它暗示指针也应该是可修改的。因此,作为对 OP 的回答:两者 .

    在实践中,argv 中的指针有很长的传统。数组可修改。许多库都有初始化函数,这些函数采用指向 argc 的指针。和指向 argv 的指针数组,其中一些确实修改了 argv 中的指针数组(删除特定于库的选项);例如 GTK+ gtk_init() MPI_Init() (尽管至少 OpenMPI 明确声明它不会检查或修改它们)。查找参数声明 (int *argc, char ***argv) ;这样做的唯一原因——假设意图是从 main() 调用的使用 (&argc, &argv) -- 是修改指针,从命令行参数中解析和删除库特定的命令行参数,修改两者argc以及 argv 中的指针如所须。

    (我最初指出 POSIX 中的 getopt() 设施依赖于可修改的指针——该功能可追溯到 1980 年,被大多数 Unix 风格采用,并于 1997 年在 POSIX.2 中标准化——但这是不正确的,正如乔纳森Leffler 在评论中指出:POSIX getopt() 不会修改实际的指针;只有 GNU getopt() 会,而且只有在没有设置 POSIXLY_CORRECT 环境变量时才会修改。GNU getopt_long() 和 BSD getopt_long() 都会修改指针,除非 POSIXLY_CORRECT 被设置,但与 getopt() 相比,它们更年轻,更不广泛。)

    在Unix领域,修改argv[]指向的字符串的内容被认为是“可移植的”。数组,并使修改后的字符串在进程列表中可见。 DJB 的 daemontools 软件包 readproctitle 中的一个示例说明了这一点是如何有用的。 . (请注意,字符串必须就地修改,不能扩展,以便更改在进程列表中可见。)

    所有这些都表明了一个非常悠久的传统,基本上几乎从 C 作为一种编程语言诞生以来,并且肯定在 C 的标准化之前,处理 argc , argv ,指针在argv数组,以及这些指针指向的字符串的内容,作为可修改的。

    因为 C 标准的意图不是定义新行为,而是将跨实现的现有行为编码(以提高可移植性和可靠性等),所以似乎可以安全地假设部分标准编写者无意中遗漏了在 argv 中明确指定指针数组为可修改的。其他任何事情都会打破传统,并明确违反 POSIX 标准(该标准也旨在促进跨系统的可移植性,并扩展未包含在 ISO C 标准中的 C 功能)。

    关于c - argv 中指向字符串的指针是否可修改?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35105918/

    相关文章:

    c - 让 C 在 _Generic 中生成错误

    arrays - 在参数声明中指定数组的大小有什么意义?

    c++ - 使用 C++ 方式对结构和数组进行别名处理

    c++ - 转换到子对象进行测试 - 此代码是否符合标准?

    c - C中的结构数组初始化

    c - 为什么二维不等于一维

    c - 对于 (*ptr)[],为什么 printf ("%p",(void*)ptr+1) 有效但 printf ("%p",ptr+1) 无效?

    c++ - const char* 类型变量的应对内容导致意外结果

    c - 指针作为第二个参数而不是返回指针?

    c - 多个相同的原型(prototype)是否合法?