Поведение указателя; char ** argv

когда я тестировал поведение двойного указателя, я получил результат, который я плохо понимаю.

==> код 1:

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

====> результат 1

root@root:/home/aa/test# ./**** 1255
test ****** pointer[] : /****
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 ****** pointer[] : %s \n ",*argv+9);
} return(0); }

==> результат 2:

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

==> результат 3:

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

кажется, что printf отображает из n-го слова (1 и 9), как мы можем объяснить это поведение указателя?

4 ответа

Вы используете это неправильно.

*argv+1 будет интерпретироваться как (argv[0])+1 и поскольку argv[0] является "./****", вы получаете "/****".

*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",

Но вы действительно должны забыть об этом, потому что это никогда не будет полезно! Это неопределенное поведение и никогда не должно использоваться.


На самом деле существует несколько проблем.

  1. пожалуйста, не работайте как корень. Только не надо.
  2. ваш синтаксис (*argv + 9) означает буквально: "defeference argv и move pointer 9 chars", и действительно, если вы переместите 9 символов из ./**** 1255 вы прибудете на 55. Поэтому либо используйте argv[i] (i = 1..N обозначает индекс аргумента), либо если вы хотите сделать это сложным способом, вы должны добавить круглые скобки: *(argv + i).
  3. попробуйте отформатировать код лучше - он будет более читабельным не только для парней stackoverflow, но и для вас.

Например, когда вы запускаете ./**** abc 123:

  • argv[0] - имя программы, содержащее string - ./****
  • argv[1] - string содержащая первый аргумент - a
  • argv[2] - string содержащая второй аргумент - b
  • argv[3] - string содержащая третий аргумент - c
  • argv[4] - string содержащая четвертый аргумент - 123
  • argv[5] - NULL, так как argc будет bw 5 (см. комментарии)
  • argv[>5] не является хорошей идеей, потому что аргументов больше нет. Поэтому вам лучше проверить argc чтобы узнать, сколько аргументов есть.


char **argv - это указатель на char * (иногда более просто называемый строкой). Вы разыгрываете этот указатель, когда делаете *argv. Результатом этого разыменования является char * или, другими словами, это адрес char. Когда вы добавляете результат, ваш код вычисляет новый адрес. Так, например, в то время как *argv будет адресом первого символа в вашей строке, *argv+1 - это адрес второго символа в вашей строке.

Когда вы добавляете число, которое больше длины вашей строки, вы выходите из "безопасности". Помните, что C позволит вам выполнить арифметику указателя, которая приведет вас к концу вашей строки. Во втором примере вы попросите printf перейти от 9 байтов к началу *argv и напечатать символы оттуда до следующего байта \0 (или NULL). Вы эффективно читаете произвольную память из своего пространства программ, что объясняет, что печатается.


Вы просто выполняете арифметику указателя: ** 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 байт.

licensed under cc by-sa 3.0 with attribution.