'\0' оценивает false, \0 оценивает true

Вдохновленный программой, описанной в разделе 5.5 K&R:

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


person Rahn    schedule 30.04.2016    source источник
comment
Первый — нулевой символ, второй — пустая строка.   -  person David C. Rankin    schedule 30.04.2016
comment
поскольку это разные вещи, почему они должны оцениваться одинаково?   -  person njzk2    schedule 01.05.2016
comment
И почему вы сравниваете строки/символы с логическими значениями?   -  person Lightness Races in Orbit    schedule 01.05.2016
comment
Вот почему слабая типизация - плохая плохая идея....   -  person Johan    schedule 01.05.2016
comment
@LightnessRacesinOrbit Программа, описанная в K&R, которая копирует содержимое char[] в другой: while((dest[i++] = source[i++]));. Точно не помню, завтра проверю.   -  person Rahn    schedule 01.05.2016
comment
по той же причине ложь есть истина.   -  person Matija Nalis    schedule 30.01.2017
comment
Есть языки, в которых ложно (C среди них нет) и те, в которых '\0' истинно (вероятно, потому, что они не отличают символы от строк), но я был бы удивлен, если бы существовал язык, в котором \0 является ложным.   -  person Carsten S    schedule 26.04.2017


Ответы (10)


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

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

person user253751    schedule 30.04.2016
comment
Предлагаемое редактирование этого ответа неверно. Косая черта — это escape-символ, а не сам по себе символ. т.е. echo 'int main() { printf("%lu\n", sizeof("\0")); return 0; }' | gcc -x c -; ./a.out дает 2. \0 — это целое число, представляющее ограничитель NULL. - person tangrs; 30.04.2016
comment
@M.M: это строковый литерал;) - person knittl; 30.04.2016
comment
Да, строковые литералы — это массивы char - person M.M; 30.04.2016
comment
Этот "\0" является указателем на массив символов не подходит для ответа 28++, так как он просто неверен по двум причинам. "\0" s не указатель, а массив, и если бы этот массив по каким-либо причинам распался на указатель, он бы указывал не на массив, а на его 1-й элемент. - person alk; 30.04.2016
comment
@alk Но мы говорим здесь о постоянной строке. Кроме того, любой указатель на массив всегда является указателем на его первый элемент. - person Mr Lister; 30.04.2016
comment
@MrLister Указатель на массив и указатель на элемент массива — это два разных зверя. - person Spikatrix; 30.04.2016
comment
@MrLister char (*ptr)[] = &"test"; будет указателем на строковый литерал, а char* ptr = "test"; будет указателем на адрес первого символа строкового литерала "test". - person Spikatrix; 30.04.2016
comment
@MrLister: любой указатель на массив всегда также является указателем на его первый элемент. правильно по значению, неправильно по типу. - person alk; 30.04.2016
comment
@CoolGuy: Учитывая char (*p1)[] = &"lo"; char *p2 = &"Hello";, будет ли какая-либо гарантия, что (*p1) идентифицирует первый символ массива, а не, например, четвертый символ массива, идентифицируемый p2? - person supercat; 01.05.2016
comment
@supercat Я полагаю, вы имели в виду char *p2 = "Hello";? Во всяком случае, я не уверен в этом. Думаю, может, когда вы используете -fwritable-strings. - person Spikatrix; 01.05.2016
comment
@supercat: Нет, я в это не верю. - person Lightness Races in Orbit; 01.05.2016

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

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

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

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

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


: Такое «распад» массивов происходит не всегда. См. Исключение для массива, не распадающегося на указатель?

person Spikatrix    schedule 30.04.2016
comment
Вы, конечно, правы в том, что значение 0 является ложным. Однако char не обязательно совпадает с целым числом. Целое число всегда подписано, если вы явно не укажете без знака. Является ли символ подписанным или нет, зависит от реализации! - person Mr Lister; 30.04.2016
comment
'\0' — это int, IIRC, хотя выглядит как char. - person Spikatrix; 30.04.2016
comment
Кроме того, я не уверен, как unsigned и signed здесь уместны. Я имею в виду unsigned или signed, char или int, все они могут представлять 0. - person Spikatrix; 30.04.2016
comment
Что показывает проблему с (ИМХО слишком распространенным) сокращенным тестом для 0/NULL. Если бы ОП написал, например. если (\0 == NULL), было бы довольно очевидно, в чем проблема. - person jamesqf; 30.04.2016
comment
Просто примечание: '\0' будет char в C++, хотя в C это int. - person Ruslan; 01.05.2016

'\0' — это число: 0, поэтому оно оценивается как ложное (0 = ложное, !0 = истинное).

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

person FedeWar    schedule 30.04.2016
comment
"\0" не является указателем. Это строковый литерал, значением которого является массив (который неявно преобразуется в указатель в большинстве, но не во всех контекстах). - person Keith Thompson; 07.05.2016

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

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

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

person fluter    schedule 30.04.2016
comment
Ваш ответ не объясняет, почему "\0" будет считаться true. - person alk; 30.04.2016
comment
@alk "\0" будет true, а не false. - person fluter; 30.04.2016
comment
Я сказал, что это строковый литерал, который не может быть NULL, поэтому он true. - person fluter; 30.04.2016
comment
Я знаю это. Но из вашего ответа это не очевидно. Не очевидно для тех, кто не знает, что строка при определенных условиях распадается на указатель на ее 1-й элемент, как, вероятно, не знает OP. - person alk; 30.04.2016
comment
Будучи строковым литералом, указатель не может быть NULL. -- Было бы полезно объяснить, на какой указатель вы ссылаетесь (хотя другие ответы уже охватывают это). - person Keith Thompson; 07.05.2016

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

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

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

'\0' — это просто 0x00 в шестнадцатеричном формате. 0x00 != 0x00 — Ложь.

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

person Bora    schedule 06.05.2016

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

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

person msc    schedule 30.04.2016
comment
Это не просто так считается правдой. Строковый литерал в C — это просто указатель на его первый элемент, и именно он оценивается как истинный. - person Lemon Drop; 30.04.2016
comment
@lemondrop строковый литерал не является указателем. Строковый литерал может быть преобразован в указатель в некоторых контекстах (например, в коде OP) - person M.M; 30.04.2016
comment
Единственным символом в строке является нулевой символ, который завершает строку, это не совсем так. Эта строка ("\0") содержит два '\0' символа. - person alk; 30.04.2016
comment
Таким образом, это считается правдой. И откуда это So извлекает какие-либо доказательства, почему литерал "\0" оценивается как true, не очевидно из вашего ответа. - person alk; 30.04.2016
comment
@cat sizeof "" не равно sizeof "\0". - person alk; 30.04.2016
comment
@cat: Без обид, все верно, но ты, кажется, ускользаешь... :-) Все, что я пытался понять, это Единственный персонаж в ..., что просто неправильно . - person alk; 30.04.2016
comment
@cat, если в качестве операнда == задан массив, он преобразуется в указатель. Массив отличается от указателя точно так же, как 5 отличается от 5.0 в коде 5 == 6.0. Вы можете убедиться в этом, сравнив sizeof "\0" с размером указателя. - person M.M; 30.04.2016
comment
@cat: бывают случаи, когда полезно иметь API, которому потребуется, чтобы несколько строк констант брали один указатель, а затем интерпретировали часть до первого нулевого байта как первую, а часть оттуда до следующего нулевого байта как вторую и т. д. Шаблон не очень распространен, но значение "FOO\0" "BAR\0" "23 skidoo" определено [закрывающие и повторно открывающие кавычки перед последним нулевым байтом более читабельны, чем \00023 skidoo, и ИМХО чище включать такие кавычки последовательно, чем только там, где нужный. - person supercat; 01.05.2016

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

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

if (array[0] != 0)
{

}

if (p != 0)
{

}

if (p != NULL)
{

}
person G.Mather    schedule 07.05.2016
comment
Я бы всегда предпочитал использовать '\0' в условных выражениях, потому что это указывает на то, что вы обрабатываете «вещь» как строку, а не массив чисел. - person Attie; 21.03.2017

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

#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; 
}
person Krishna    schedule 26.05.2016

Простая вещь: ПО крайней мере, 0 (int) и 0.0 (float или double) имеют значение FALSE в C.

'\0' равно целому числу 0.

«\0» — это массив символов. Неважно, что ВНУТРИ массива, сколько там символов или что это за символы.

Таким образом, '\0' оценивается как 0, как 77-77 оценивается как 0. И 0 является ложным.

int x; x = '\0'; printf("X has a value : %d"); Вывод:


х имеет значение: 0

И код:

if(0){printf("true");}

else{printf("false");}

Выход:


ЛОЖЬ

person Bhartendu Kumar    schedule 16.07.2019

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

  1. Работа if(условие) в C
  2. Разница символьных и строковых литералов в C

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

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

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

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

2. Разница между символьными и строковыми литералами в C

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

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

И в соответствии с рабочей логикой строки ЕСЛИ условие, если условие возвращает 0 или ложь, это означает, что условие ложно; если условие возвращает ненулевое значение, это означает, что условие истинно.

Итак, по пунктам 1 и 2 окончательно можно сделать вывод, что

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

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

person Ravi    schedule 02.05.2016