2016-04-30 2 views
54

Вдохновленные программой, описанной в K & R раздел 5.5:' 0' оценивает ложь, " 0" оценивает истинный

void strcpy(char *s, char *t) 
{ 
    while(*s++ = *t++); 
} 

программа C

if ('\0') { printf("\'\\0\' -> true \n"); } 
else  { printf("\'\\0\' -> false\n"); } 

if ("\0") { printf("\"\\0\" -> true \n"); } 
else  { printf("\"\\0\" -> false\n"); } 

печатает

'\0' -> false 
"\0" -> true 

Почему '\0' и "\0" оценивают по-разному в C?

лязг версия 3.8.0

+32

Первый является * NUL-символ *, вторая * пустая строка *. –

+5

потому что они разные вещи, почему они оценивают одинаково? – njzk2

+3

И почему вы сравниваете строки/символы с булевыми? –

ответ

109

Напомним, как строковые литералы работать в C - "\0" является массив символов, содержащий два нулевых байта (тот, который вы просили, и неявное один в конце). При оценке для теста if он распадается на указатель на его первый символ. Этот указатель не является NULL, поэтому он считается истинным при использовании в качестве условия.

'\0' - это номер ноль, эквивалентный только 0. Это целое число, равное нулю, поэтому оно считается ложным при использовании в качестве условия.

+0

Предлагаемое изменение этого ответа неверно. Слэш - это символ побега, а не сам символ. I.e. 'echo 'int main() {printf ("% lu \ n ", sizeof (" \ 0 ")); return 0; } '| gcc -x c -; ./a.out' дает '2'. '\ 0' - целое число, которое представляет собой NULL-терминатор. – tangrs

+20

'' \ 0 "' это массив, а не указатель –

+4

@MM: это строковый литерал;) – knittl

0

'\ 0' является нулевой символ, который имеет значение . Он используется для завершения строки символов. Поэтому он считается ложным.

"\ 0" является нулевой или пустойстрока. Единственный символ в строке - это нулевой символ, который завершает строку. Поэтому это считается правдой.

+4

Это не просто «считается истинным». Строковый литерал в C - это просто указатель на его первый элемент, и это то, что оценивается как true. – lemondrop

+2

@lemondrop строковый литерал не является указателем. Строковый литерал может быть преобразован в указатель в некоторых контекстах (например, код OP) –

+3

«* Единственный символ в строке - это нулевой символ, который завершает строку *», это не совсем так. Эта «строка» ('' \ 0 "') содержит *** два символа *** '' \ 0''. – alk

3

Прежде всего, глядя на два условия, '\0' является константой целочисленного типа, которая обозначает нулевой символ C, который является таким же, как 0. В то время как "\0" является строковым литералом, который содержит 2 байта, тот, который указан, и байт нуль-терминатора неявно добавлен. Будучи строковым литералом, указатель не может быть NULL.

Во-вторых, в C, для условия оператора if, все ненулевые оцениваются как true, а нуль оценивается как false.

Согласно этому правилу, будет ясно, что '\0' является false и "\0" оценивается как true.

+0

Да, правильно, я изменил его. – fluter

+1

Ваш ответ не объясняет, почему '' \ 0 "' будет считаться «истинным». – alk

+0

@alk '' \ 0 "' будет 'true', а не' false'. – fluter

14

'\0' является номер: 0, так что оценивается как ложное (0 = ложной, !0 = TRUE).

Но "\0" является указателем на раздел только для чтения, в котором хранится фактическая строка, указатель не является NULL ergo, это правда.

+0

'' \ 0 "' не является указателем. Это строковый литерал, значение которого представляет собой массив (который неявно преобразован в указатель в большинстве, но не во всех контекстах). –

34

Прежде всего, вы должны иметь в виду, что в C,

  • Нулевой является ложным и не ноль это правда.
  • Для типов указателей NULL является ложным и не является NULL.

'\0', как уже сказано, является таким же, как целое буквального 0 и, следовательно, является ложным (см первого пункта маркированного выше, чтобы узнать, почему).

"\0" - строковый литерал, содержащий два знака \0 (тот, который вы явно добавили, а другой, который неявный и будет добавлен компилятором). Строковый литерал будет храниться где-то в постоянной памяти. Когда вы используете "\0", он преобразуется в указатель на его первый элемент. Это обычно называют «array decay». (Это - причина, почему вещи, как char* str = "string"; работ).

Итак, вы эффективно проверяете адрес первого символа строкового литерала. Так как адрес строкового литерала всегда будет не NULL, if всегда будет правдой (см. Второй пункт выше, чтобы узнать почему).


: Это "распад" массивов не всегда бывает. См Exception to array not decaying into a pointer?

+0

Вы правы, конечно, что значение 0 является ложным. Однако char не обязательно совпадает с целым числом.Int всегда подписывается, если вы явно не укажете unsigned. Независимо от того, подписан ли символ или нет, зависит от реализации! –

+2

'' \ 0'' является 'int', IIRC, хотя он _looks_ как' char'. –

+4

Кроме того, я не уверен, насколько важны «неподписанные» и «подписанные». Я имею в виду 'unsigned' или' signed', 'char' или' int', все могут представлять 0. –

0

Мы можем очистить вышеупомянутую проблему в двух разных понятия C

  1. ОБРАБОТКА если (условие) в C
  2. Разница символов & Строковые литералы в C

1. Работа if (условие) в C if (состояние)

В языке C, если условие работает на базе 0 (нуль) и без нуля.

Если результатом данного условия является нуль, то C считает, что данное условие является ложным.

Если результатом данного условия является Non-Zero, то C считает, что данное условие истинно.

2. Отличие символов & строковые литералы в C

В C, строковые литералы являются те, которые заключены в двойные кавычки («»), в то время как Символьные литералы являются те, которые заключены в одинарные котировки метки ('') и минимальная длина - один символ, а максимальная длина - два символа.

Еще один важный момент в том, что в C, если мы преобразуем '\ 0' (null) в int (Integer), тогда мы получим 0 (Zero), в то время как мы не можем преобразовать «\ 0» в int неявно или явно , Потому что «\ 0» - это строка, а «\ 0» - символ.

И в соответствии с строкой IF условия рабочей логики, если условие возвращает 0 или false, это означает, что условие является ложным; в случае, если условие возвращает ненулевое значение, это означает, что условие истинно.

Таким образом, в соответствии с пунктом 1 и 2, наконец, мы можем заключить, что

если ('\ 0') Е ("\ '\ 0 \' = ложь! \ П"); // условие становится ложным

if ("\ 0") printf ("\" \ 0 \ "! = false \ n"); // условие становится истинным

1

Прежде всего, обратите внимание, что шестнадцатеричное значение False равно 0x00, а True - любое другое значение, чем 0x00.

"\0" - это строка с символом и Null Terminator '\0' в конце. Таким образом, это указатель на символ, указывающий на массив из 2 байтов: ['\0', '\0']. В этом массиве первым является символ, а другой - нулевым терминатором.

После компиляции (без оптимизации) этот указатель на символ временно присваивается адресу в памяти, указывающему на первый байт этих двух байтов. Этот адрес может быть, например, 0x18A6 в шестнадцатеричном формате. Поэтому компилятор (большинство из них) фактически записывает эти два значения в память. Поскольку строка на самом деле является адресом первого байта этой строки, наше выражение интерпретируется как 0x18A6 != false. Итак, ясно, что 0x18A6 != 0x00 - это правда.

'\0' просто 0x00 в шестнадцатеричном формате. 0x00 != 0x00 False.

Этот ответ написан для 8-битной архитектуры данных с 16-разрядной адресацией. Надеюсь, это поможет.

0

'\ 0' - символ, равный числу ноль. «\ 0» - это строка, и мы обычно добавляем «\ 0» в конце строки. Не используйте «\ 0» или «\ 0» в условных операторах, потому что это довольно запутанно.

Следующее использование предлагается:

if (array[0] != 0) 
{ 

} 

if (p != 0) 
{ 

} 

if (p != NULL) 
{ 

} 
+0

Я бы всегда предпочитал видеть '' \ 0'' в условных выражениях, потому что это указывает на то, что вы обрабатываете «вещь» как строку, а не массив чисел. – Attie

0

Проверьте это с примерами ..

#include <stdio.h> 

int main() 
{ 
printf("string value\n"); 

//the integer zero 
printf("0.........%d\n" , 0); 

//the char zero, but chars are very small ints, so it is also an int 
//it just has some special syntax and conventions to allow it to seem 
//like a character, it's actual value is 48, this is based on the 
//ASCII standard, which you can look up on Wikipedia 
printf("'0'.......%d\n" , '0'); 

//because it is an integer, you can add it together, 
//'0'+'0' is the same as 48+48 , so it's value is 96 
printf("'0'+'0'...%d\n" , '0'+'0'); 

//the null terminator, this indicates that it is the end of the string 
//this is one of the conventions strings use, as a string is just an array 
//of characters (in C, at least), it uses this value to know where the array 
//ends, that way you don't have to lug around another variable to track 
//how long your string is. The actual integer value of '\0' is zero. 
printf("'\\0'......%d\n" , '\0'); 

//as stated, a string is just an array of characters, and arrays are tracked 
//by the memory location of their first index. This means that a string is 
//actually a pointer to the memory address that stores the first element of 
//the string. We should get some large number, a memory address 
printf("\"0\".......%d\n" , "0"); 

//a string is just an array of characters, so lets access the character 
//in position zero of the array. it should be the character zero, which 
//has an integer value of 48 
printf("\"0\"[0]....%d\n" , "0"[0]); 

//and the same thing for the empty string 
printf("\"\\0\"[0]...%d\n" , "\0"[0]); //equal to '\0' 

//we also said a string is just a pointer, so we should be able to access 
//the value it is pointing to (the first index of the array of characters) 
//by using pointers 
printf("*\"0\"......%d\n" , *"0"); 

return 0; 
} 
Смежные вопросы