【问题标题】:behaviour of pointer ; char ** argv指针的行为;字符 ** argv
【发布时间】:2013-07-12 22:44:17
【问题描述】:

当我测试双指针行为时,我得到了一个我不太理解的结果。

==> 代码 1:

int main (int argc , char **argv)
{

if(*argv+1 ==NULL)
{
    printf("NULL pointer \n");
    exit(0) ;
}
else
{ 
    printf("test double pointer[] : %s \n ",*argv+1);  

}

return(0);
}

====> 结果 1

root@root:/home/aa/test# ./geip 1255
test double pointer[] : /geip 
root@root:/home/aa/test#

===> 代码 2:

int main (int argc , char **argv)
{

if(*argv+9 ==NULL)
{
    printf("NULL pointer \n");
    exit(0) ;
}
else
{ 
    printf("test double pointer[] : %s \n ",*argv+9);
}
 return(0);
 }

==> 结果 2:

root@root:/home/aa/test# ./geip 1255
test double pointer[] : 55 
root@root:/home/aa/test#

==> 结果 3:

root@root:/home/aa/test# ./geip 
test double pointer[] : ELL=/bin/bash 
root@root:/home/aa/test#

似乎 printf 从第 n 个单词(1 和 9)开始显示 我们如何解释指针的这种行为?

【问题讨论】:

  • 由于argv[0] 小于9 个字节,*argv + 9,相当于&argv[0][9] 调用未定义的行为。它碰巧不会崩溃(在您的情况下),并且存储在那里的任何内容都会被打印出来。
  • 这段代码太破了,眼睛疼。
  • 这只是一个说明,但以 root 用户身份工作存在潜在危险。
  • 这将进一步帮助您:Two Dimensional Array Implementation Using Double Pointer 特别是Mohamed 给出的图表

标签: c linux pointers argv


【解决方案1】:

你用错了。

*argv+1 将被解释为 (argv[0])+1 并且由于 argv[0] 是 "./geip" 你得到 "/geip"。

*argv+9 将被解释为 (argv[0])+9,但由于 argv[0] 的长度仅为 6,因此结果未定义。

在您的情况下, argv 可能存储为:

.  /  g  e  i  p \0  1  2  5  5 \0
0  1  2  3  4  5  6  7  8  9 10 11

这就解释了为什么+9 会让你得到“55”

但你真的应该忘记这一点,因为它永远不会有用!这是未定义的行为,永远不应该使用。

【讨论】:

  • @Tom van der Woerdt:如果我们想从第 n 个字符中读取单词,它会很有用!!
  • @stack_A 是的,但你会使用argv[0]+1 或argv[1]+2,而不是别的。
【解决方案2】:

char **argv 是指向char * 的指针(有时更简单地称为字符串)。当您执行*argv 时,您取消引用此指针。此取消引用的结果是char *,或者换句话说,它是char 的地址。当您对结果进行加法时,您的代码正在计算一个新地址。例如,*argv 是字符串中第一个字符的地址,*argv+1 是字符串中第二个字符的地址。

当您添加一个比字符串长度更长的数字时,您将退出“安全”。请记住,C 将允许您进行指针运算,使您越过字符串的末尾。在您的第二个示例中,您要求 printf 从 *argv 的开头移动 9 个字节,并将字符从那里打印到下一个 \0(或 NULL)字节。您正在有效地从程序的进程空间中读取任意内存,这解释了正在打印的内容。

【讨论】:

    【解决方案3】:

    其实问题不止一个。

    1. 请,请不要以 root 身份工作。只是不要。
    2. 您的语法 (*argv + 9) 的字面意思是:“defeference argv 并移动指针 9 个字符”,实际上,如果您从 ./geip 1255 移动 9 个字符,您将到达 55。因此,要么使用argv[i](i = 1..N 表示参数索引),要么如果你想用困难的方式来做,你必须添加括号:*(argv + i)。
    3. 尝试更好地格式化您的代码 - 它不仅对 stackoverflow 人员而且对您来说都更易读。

    例如当你运行./geip a b c 123:

    • argv[0] 是 string 持有程序名称 - ./geip
    • argv[1] 是 string 持有第一个参数 - a
    • argv[2] 是 string 持有第二个参数 - b
    • argv[3] 是 string 持有第三个参数 - c
    • argv[4] 是 string 持有第四个参数 - 123
    • argv[5] 为 NULL,因为 argc 将 bw 5(参见 cmets)
    • argv[>5] 不是一个好主意,因为没有更多的参数。所以你最好检查argc 看看有多少参数。

    【讨论】:

    • 标准保证 argv[argc] 是一个空指针。所以在这种情况下 argv[5] 保证是一个空指针。
    • +1 表示 不要以 root 身份工作。特别是,不要以 root 身份进行实验性编程!
    【解决方案4】:

    你只需做一个指针算术: **argv 是指向指针列表的指针 *argv 是列表的头部

    //char **argv is given from outthere
    char *p;
    p = *argv; // the same as "p = *argv[0]"
    for (int i = 0; i < 100) {
      printf("Next: %s\n", p+i);
    }
    

    尝试运行它并查看内存转储,从列表头部到接下来的 100 个字节。

    【讨论】:

    • 可能想用%.2X 而不是%s,这样比较不崩溃
    猜你喜欢
    • 1970-01-01
    • 2013-06-19
    • 2018-04-05
    • 2012-05-09
    • 2020-04-22
    • 2021-02-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多