C: путаница указателей

#c #pointers #lvalue #post-increment

#c #указатели #значение lvalue #последующее увеличение

Вопрос:

Я понимаю, что это часть базового материала, но я застрял :- (Может кто-нибудь, пожалуйста, помочь мне?

Программа 1:

 #include <stdio.h>
#include <stdlib.h> 

int main()
{
 int a=1,b=2,c;
 c=(a b)  ;
}
  

Почему вывод является ошибкой? требуется значение lvalue?

Программа 2:

 #include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
 char *p1="name";
 char *p2;

 p2=(char*)malloc(20);
 memset(p2,0,20);

 while(*p2  =*p1  );
 printf("%sn",p2);

}
  

Почему на выходе получается пустая строка? И если я изменю порядок приращения на обратный, то есть: while( *p2= *p1); почему возникает ошибка lvalue?

Комментарии:

1. «kiss», избегайте однострочников, таких как while(*p2 = *p1 ); Лучше разбить эту строку на более читаемую многострочную с четким «цикл до», «inc» и копировать часть. Вместо того, чтобы делать все это сразу.

2. @Johan: Если все будет сделано так прямолинейно, это не будет частью каверзного вопроса в интервью 🙂

3. вы правы в вопросе (и в том, чтобы узнать что-то новое), но более простой код, как правило, работает лучше в долгосрочной перспективе 😉

Ответ №1:

Что касается первого вопроса, (a b) означает «увеличить значение a b на 1″.

Однако вы не можете увеличивать значение a b , поскольку это не переменная. Чего бы вы ожидали от следующего кода?

 int a = 1, b = 2;
printf("a = %d, b = %d, a b = %dn", a, b, a b);
(a b)  ;
printf("a = %d, b = %d, a b = %dn", a, b, a b);
  

Очевидно, что первый printf должен выводить

a = 1, b = 2, a b = 3

Но как насчет второго?

a = ?, b = ?, a b = 4

Неясно, какими должны быть a или b, если мы увеличиваем сумму.

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

Более простым способом копирования строки было бы использовать strcpy , вот так:

 strcpy(p2, p1);
  

Обратите внимание, это безопасно только потому, что вы знаете, что размер строки в p1 не больше размера p2 .Если вы не уверены в размере строки (например, если вы получаете строку из пользовательского ввода), вам нужно быть осторожным, как описано в Википедии.

Что касается того, почему while( *p2= *p1); не работает, в то время как while(*p2 =*p1 ); работает:

Postfix- имеет более высокий приоритет, чем * . Это означает, *p2 означает *(p2 ) . Итак

 *(p2  ) = something;
  

то же самое, что

 *p2 = something;
p2  = 1;
  

Между тем, *p2 означает (*p2) , или «на что бы p2 ни указывало, увеличенное на единицу».

Опять же, вы получите проблему, если скажете:

  int a = 5, *p2 = amp;a;
   *p2 = 10;
 printf("a = %dn", a);
  

Что бы вы ожидали, что это напечатает? Во всяком случае, он должен вывести 9, потому что вы сообщаете об этом компилятору *p2 1 = 10 .

Однако вы не можете ожидать, что C-компилятор решит это уравнение, поэтому, чтобы сохранить язык простым и эффективным, такого рода вещи запрещены.

Комментарии:

1. очень хорошее объяснение первой части. Гораздо более интуитивно, чем просто говорить о rvalues и lvalues . 1 🙂

2. Спасибо за ответ, можете ли вы также помочь мне со следующей частью первого вопроса? И если я изменю порядок приращения на обратный, то есть: while( *p2= *p1); почему ошибка lvalue required?

3. @kingsmasher1: Я пытался объяснить это, посмотрим, имеет ли это смысл.

Ответ №2:

 c=(a b)  ;
  

a b не является значением lvalue — как бы вы хотели присвоить что-либо результату добавления (rvalue) — и операторы / — присваивают новое значение.

 while(*p2  =*p1  );
  

Ваш p2 указывает на в конце строки. Вам нужно сохранить исходный адрес, на который указывает p2, перед вашим циклом:

 char *p3 = p2;
while(*p2  =*p1  )
    ;
printf("%sn",p3);
  

Комментарии:

1. Что касается первого вопроса, тогда в чем смысл приращений post? Если мы это сделаем, a это допустимо, верно? Тогда почему бы и нет, (a b)

Ответ №3:

 c=(a b)  ;
  

Оператор не работает с временными переменными. (a b) образует временный.

 while(*p2  =*p1  );
  

Вы увеличиваете p2 здесь. После цикла p2 больше не указывает на начало блока памяти, возвращенного последним malloc() вызовом.

Ответ №4:

Если вы посмотрите на цикл while:

while(*p2 = *p1 );

помните, что выражение в C является «истинным», если оно ненулевое, и false, если оно равно нулю. Даже если цикл не имеет тела, поток управления по-прежнему:

проверить условие -> выполнить инструкцию в теле -> проверить условие

и здесь «проверить условие» означает «оценить оператор и посмотреть, не равно ли оно нулю».

Цикл while продолжает работать до тех пор, пока p1 не укажет на ноль, после чего управление переходит к printf.

например

 int main() 
{
    int array1[] = {2, 3, 1, 0, 3, 5};
    int array2[20];
    int * p2 = (int *)memset(array2, 0, 20*sizeof(int));
    int * p1 = array1;

    while(*p2   = *p1  ) {
        printf("In while: p1 is %dn", *p1);
    }
    printf("Out of while: %dn",*p2);
    return 0;

}
  

выдает:

 In while: p1 is 3
In while: p1 is 1
In while: p1 is 0
Out of while: 0