поведение на показалеца; char ** argv

когато тествах поведението на двоен показалец, получих резултат, който не разбирам добре.

==> код 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
@Tom van der Woerdt: може да е полезно, ако искаме да прочетем дума от 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. моля, моля, не работете като root. Просто недей.
  2. вашият синтаксис (*argv + 9) означава буквално: "defeference 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 ще bw 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