поведение указателя ; символ ** аргумент

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

==> код 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), как мы можем объяснить такое поведение указателя?


person stack_A    schedule 12.07.2013    source источник
comment
Поскольку argv[0] короче 9 байт, *argv + 9, что эквивалентно &argv[0][9], вызывает неопределенное поведение. Бывает, что не вылетает (в вашем случае), и печатается все, что там хранится.   -  person Daniel Fischer    schedule 12.07.2013
comment
Этот код настолько сломан, что у меня болят глаза.   -  person ams    schedule 12.07.2013
comment
Это просто примечание, но работа в качестве пользователя root потенциально опасна.   -  person awesoon    schedule 12.07.2013
comment
Это поможет вам в дальнейшем: Реализация двумерного массива с использованием двойного указателя Специально диаграмма, предоставленная Мохамедом   -  person Grijesh Chauhan    schedule 12.07.2013


Ответы (4)


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

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

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

person Tom van der Woerdt    schedule 12.07.2013
comment
@ Том ван дер Вурдт: это может быть полезно, если мы хотим прочитать слово из n-го символа !! - person stack_A; 12.07.2013
comment
@stack_A Да, но вы бы использовали argv[0]+1 или argv[1]+2, а не что-то еще. - person Tom van der Woerdt; 12.07.2013

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

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

person tuckermi    schedule 12.07.2013

На самом деле проблем больше, чем одна.

  1. пожалуйста, пожалуйста, не работайте под рутом. Просто не надо.
  2. ваш синтаксис (*argv + 9) буквально означает: «защитить argv и переместить указатель на 9 символов», и действительно, если вы переместите 9 символов из ./geip 1255, вы попадете в 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 будет иметь значение 5 (см. комментарии)
  • argv[>5] не лучшая идея, потому что аргументов больше нет. Так что вам лучше проверить argc, чтобы увидеть, сколько аргументов есть.
person Jan Spurny    schedule 12.07.2013
comment
Стандарт гарантирует, что argv[argc] является нулевым указателем. Таким образом, в этом случае argv[5] гарантированно будет нулевым указателем. - person DaV; 12.07.2013
comment
+1 за Не работать с правами root. Особенно не занимайтесь экспериментальным программированием под root! - person Jonathan Leffler; 12.07.2013

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

person Yury Schkatula    schedule 12.07.2013
comment
Возможно, вы захотите использовать %.2X вместо %s, это менее аварийно - person Tom van der Woerdt; 12.07.2013