Comparing character arrays and string literals in C++ - c++

I have a character array and I'm trying to figure out if it matches a string literal, for example:
char value[] = "yes";
if(value == "yes") {
// code block
} else {
// code block
}
This resulted in the following error: comparison with string literal results in unspecified behavior. I also tried something like:
char value[] = "yes";
if(strcmp(value, "yes")) {
// code block
} else {
// code block
}
This didn't yield any compiler errors but it is not behaving as expected.

Check the documentation for strcmp. Hint: it doesn't return a boolean value.
ETA: == doesn't work in general because cstr1 == cstr2 compares pointers, so that comparison will only be true if cstr1 and cstr2 point to the same memory location, even if they happen to both refer to strings that are lexicographically equal. What you tried (comparing a cstring to a literal, e.g. cstr == "yes") especially won't work, because the standard doesn't require it to. In a reasonable implementation I doubt it would explode, but cstr == "yes" is unlikely to ever succeed, because cstr is unlikely to refer to the address that the string constant "yes" lives in.

std::strcmp returns 0 if strings are equal.

strcmp returns a tri-state value to indicate what the relative order of the two strings are. When making a call like strcmp(a, b), the function returns
a value < 0 when a < b
0 when a == b
a value > 0 when a > b

As the question is tagged with c++, in addition to David Seilers excellent explanation on why strcmp() did not work in your case, I want to point out, that strcmp() does not work on character arrays in general, only on null-terminated character arrays (Source).
In your case, you are assigning a string literal to a character array, which will result in a null-terminated character array automatically, so no problem here. But, if you slice your character array out of e. g. a buffer, it may not be null-terminated. In such cases, it is dangerous to use strcmp() as it will traverse the memory until it finds a null byte ('\0') to form a string.
Another solution to your problem would be (using C++ std::string):
char value[] = "yes";
if (std::string{value} == "yes")) {
// code block
} else {
// code block
}
This will also only work for null-terminated character arrays. If your character array is not null-terminated, tell the std::string constructor how long your character array is:
char value[3] = "yes";
if (std::string{value, 3} == "yes")) {
// code block
} else {
// code block
}

Related

How to handle POST requests in c++ CGI [duplicate]

This question already has answers here:
How do I properly compare strings in C?
(10 answers)
Closed 1 year ago.
int main (int argc, **argv)
{
if (argv[1] == "-hello")
printf("True\n");
else
printf("False\n");
}
# ./myProg -hello
False
Why? I realize strcmp(argv[1], "-hello") == 0 returns true... but why can't I use the equality operator to compare two C strings?
Because argv[1] (for instance) is actually a pointer to the string. So all you're doing is comparing pointers.
You can't compare strings in C with ==, because the C compiler does not really have a clue about strings beyond a string-literal.
The compiler sees a comparison with a char* on either side, so it does a pointer comparison (which compares the addresses stored in the pointers)
In C because, in most contexts, an array "decays into a pointer to its first element".
So, when you have the array "foobar" and use it in most contexts, it decays into a pointer:
if (name == "foobar") /* ... */; /* comparing name with a pointer */
What you want it to compare the contents of the array with something. You can do that manually
if ('p' == *("foobar")) /* ... */; /* false: 'p' != 'f' */
if ('m' == *("foobar"+1)) /* ... */; /* false: 'm' != 'o' */
if ('g' == *("foobar"+2)) /* ... */; /* false: 'g' != 'o' */
or automatically
if (strcmp(name, "foobar")) /* name is not "foobar" */;
Because there is no such thing as a C string.
In C, a string is usually an array of char, or a pointer to char (which is nearly the same). Comparing a pointer/array to a const array won't give the expected results.
UPDATE: what I meant by 'no C string' is, there is no string in C. What's usually referred to as a 'C string' is language independent (as 'Pascal string' is), it's the representation of strings as a null-terminated linear array of characters.
In C, string values (including string literals) are represented as arrays of char followed by a 0 terminator, and you cannot use the == operator to compare array contents; the language simply doesn't define the operation.
Except when it is the operand of either the sizeof or & operators, or when it is a string literal being used to initialize another array in a declaration, an expression with type "N-element array of T" will have its type implicitly converted (decay) to type "pointer to T", and the value of the expression will be the address of the first element of the array.
So when you write
if (argv[1] == "-hello")
the compiler implicitly converts the expression "-hello" from type "7-element array of char" to "pointer to char" (argv[1] is already a pointer type), and the value of the expression is the address of the character '-'. So what == winds up comparing are two pointer values, which are (most likely) never going to be equal since "-hello" and argv[1] (most likely) occupy different regions in memory.
This is why you have to use library functions like strcmp() to compare string values.
Because C strings dont exist as such. They are char arrays ending in a \0.
The equality operator == will test that the pointer to the first element of the array are the same. It wont compare lexicographically.
On the other hand "-hello" == "-hello" may return non zero, but that doesn't mean that the == operator compares lexicographycally. That's due to other facts.
If you want to compare lexicographycally, you can always
#define STR_EQ(s1,s2) \
strcmp(s1,s2) == 0
Reading harder I see that you tagged as c++. So you could
std::string arg1 ( argv[1] );
if (arg1 == "-hello"){
// yeahh!!!
}
else{
//awwwww
}
Strings are not native types in C. What you are comparing in that example are two pointers. One to your first argument, and the other is a static character array with the contents of "-hello".
You really want to use strncmp or something similar.
When you're using ==, you're comparing pointers. That is, it will return true if the two operands refer to the same string in memory. Therefore, it's unsuitable for use in comparing strings lexicographically.
Because C strings are array of characters. Arrays are simply pointers to the first element in the array, and when you compare two pointers using == it compares the memory address they point to, not the values that they point to.

how to initialize static char array with NULL(or 0) in c++

I attempted to initialize char array with NULL like this syntax.
char str[5] = NULL;
But it returned error..
How can I initialize char array with NULL or 0 in C++?
Concretely, I want to print "Hello" in this example code.
#include <iostream>
int main()
{
char str[5] = NULL;
if(!str)
std::cout << "Hello" << std::endl;
return 0;
}
This code will return error because of incorrect initialization. Then, what initializing sentence should I replace sentence with?
An array can not be null. Null is state of a pointer, and an array is not a pointer.
In the expression !str, the array will decay to the address of the first element. The type of this decayed address is a pointer, but you cannot modify the decayed pointer. Since the array is never stored in the address 0 (except maybe in some special case on an embedded system where an array might actually be stored at that memory location), !str will never be true.
What you can do, is initialize all of the sub-objects of the array to zero. This can be achieved using the value-initialization syntax:
char str[5]{};
As I explained earlier, !str is still not meaningful, and is always false. One sensible thing that you might do is check whether the array contains an empty string. An empty string contains nothing except the terminator character (the array can have elements after the terminator, but those are not part of the string).
Simplest way to check whether a string is empty is to check whether the first character is the terminator (value-initialized character will be a terminator, so a value initialized character array does indeed contain the empty string):
if (!str[0]) // true if the string is empty
//...
char str[5] = {'\0'};
if (str[0] != '\0')
//...
If you now put some characters into str it will print str up to the last '\0'.
Every string literal ends with '\0', you must make sure your array ends with '\0' too, if not, data will be read beyond your array (until '\0' is encountered) and possibly beyond your application's memory space in which case your app will crash.
You should use std::string or QString however if you need a string.
You can't initialise a char array with NULL, arrays can never be NULL. You seem to be mixing up pointers and arrays. A pointer could be initialised with NULL.
You can initialise the chars of your array with 0, that would be
char str[5] = {0};
but I don't think that's what you're asking.
#include <stdio.h>
using namespace std;
int main() {
char str[5];
for(int i=0;i<5;i++){
str[i]=NULL;
}
printf("success");
return 0;
}
Hope this helps.

What does `(c = *str) != 0` mean?

int equiv (char, char);
int nmatches(char *str, char comp) {
char c;
int n=0;
while ((c = *str) != 0) {
if (equiv(c,comp) != 0) n++;
str++;
}
return (n);
}
What does "(c = *str) != 0" actually mean?
Can someone please explain it to me or help give me the correct terms to search for an explanation myself?
This expression has two parts:
c = *str - this is a simple assignment of c from dereferencing a pointer,
val != 0 - this is a comparison to zero.
This works, because assignment is an expression, i.e. it has a value. The value of the assignment is the same as the value being assigned, in this case, the char pointed to by the pointer. So basically, you have a loop that traces a null-terminated string to the end, assigning each individual char to c as it goes.
Note that the != 0 part is redundant in C, because the control expression of a while loop is implicitly compared to zero:
while ((c = *str)) {
...
}
The second pair of parentheses is optional from the syntax perspective, but it's kept in assignments like that in order to indicate that the assignment is intentional. In other words, it tells the readers of your code that you really meant to write an assignment c = *str, and not a comparison c == *str, which is a lot more common inside loop control blocks. The second pair of parentheses also suppresses the compiler warning.
Confusingly,
while ((c = *str) != 0) {
is a tautology of the considerably easier to read
while (c = *str) {
This also has the effect of assigning the character at *str to c, and the loop will terminate once *str is \0; i.e. when the end of the string has been reached.
Assignments within conditionals such as the above can be confusing on first glance, (cf. the behaviour of the very different c == *str), but they are such a useful part of C and C++, you need to get used to them.
(c = *str) is an expression and that has a value in itself. It is an assignment, the value of an assignment is the assigned value. So the value of (c = *str) is the value of *str.
The code basically checks, whether the value of *str, which just has been assigned to c is not 0. In case it isn't, then it will call the function equiv with that value.
Once the 0 is assigned, this is the end of the string. The function has to stop reading from the memory, which it does.
It's looping over every character in the string str, assigning them to c and then seeing if c is equal to 0 which would indicate the end of the string.
Although really the code should use '\0' as that is more obviously a NUL character.
We are going through the str in the while loop and extract every char symbol in it until it is equal to zero - the main rule of the end of char string.
Here is 'for' loop equivalent:
for (int i = 0; i < strlen(str); ++i )
std::cout << str[i];
It is just sloppily written code. The intention is to copy a character from the string str into c and then check if it was the null terminator.
The idiomatic way to check for the null terminator in C is an explicit check against '\0':
if(c != '\0')
This is so-called self-documenting code, since the de facto standard way to write the null terminator in C is by using the octal escape sequence \0.
Another mistake is to use assignment inside conditions. This was recognized as bad practice back in the 1980s and since then every compiler gives a warning against such code, "possibly incorrect assignment" or similar. This is bad practice because assignment includes a side effect and expressions with side effects should be kept as simple as possible. But it is also bad practice because it is easy to mix up = and ==.
The code could easily be rewritten as something more readable and safe:
c = *str;
while (c != '\0')
{
if(equiv(c, comp) != 0)
{
n++;
}
str++;
c = *str;
}
You don't need char c since you already have the pointer char *str, also you can replace != 0 with != '\0' for better readability (if not compatibility)
while (*str != '\0')
{
if (equiv((*str),comp)
!= 0)
{ n++; }
str++;
}
To understand what the code does, you can read it like this
while ( <str> pointed-to value is-not <end_of_string> )
{
if (function <equiv> with parameters( <str> pointed-to value, <comp> )
returned non-zero integer value)
then { increment <n> by 1 }
increment pointer <str> by 1 x sizeof(char) so it points to next adjacent char
}

difference beŠµween compare array of char and pointer to char string with char string

#include <iostream>
int main() {
char* a = "test";
char b[] = "test";
if ( a == "test" ) // work
std::cout << "1";
if ( b == "test" ) // don't
std::cout << "2";
}
What exactly happend in both variants? Just memmory adress compare?
In both cases you are not comparing the actual strings (use strcmp for this), but addresses:
In the first case, you are comparing the address stored in a - the start address of a string literal "test" - with the start address of a (conceptually) different string literal, that happens to have the same content. However, if there are multiple identical string literals in your code, the compiler is allowed to store them all in the same place to save memory and as a result, the comparison yields true (although this is not guaranteed to happen every time).
In the second case however, you are comparing the address of the first element of b with that of the string literal. Here, b is a local array that contains a copy of the string "test" but resides at a completely different memory region, so this comparison fails (and will always fail)
Note: Unless you have a very good reason not to, you should of course - as mentioned by PaulEvans - use std::string instead of an char array to store strings. This will give you all the nice value semantic properties and operator overloads you'd expect.
In both cases you're comparing pointers not strings so it's blind luck any of them worked.
The best way to to compare strings is with std::string. Something like:
std::string c = "test";
if (c == "test")
std::cout << "c really is \"test\"!\n";

Compare equality of char[] in C

I have two variables:
char charTime[] = "TIME";
char buf[] = "SOMETHINGELSE";
I want to check if these two are equal... using charTime == buf doesn't work.
What should I use, and can someone explain why using == doesn't work?
Would this action be different in C and C++?
char charTime[] = "TIME"; char buf[] = "SOMETHINGELSE";
C++ and C (remove std:: for C):
bool equal = (std::strcmp(charTime, buf) == 0);
But the true C++ way:
std::string charTime = "TIME", buf = "SOMETHINGELSE";
bool equal = (charTime == buf);
Using == does not work because it tries to compare the addresses of the first character of each array (obviously, they do not equal). It won't compare the content of both arrays.
In c you could use the strcmp function from string.h, it returns 0 if they are equal
#include <string.h>
if( !strcmp( charTime, buf ))
In an expression using == the names of char arrays decay into char* pointing to the start of their respective arrays. The comparison is then perform in terms of the values of the pointers themselves and not the actual contents of the arrays.
== will only return true for two pointers pointing to the same location and false otherwise, even if they are pointing to two arrays with identical contents.
What you need is the standard library function strcmp. This expression evaluates as true if the arrays contain the same contents (up to the terminating null character which must be present in both arrays fro strcmp to work safely).
strcmp(charTime, buf) == 0
Check them in a for loop. Get the ASCII numbers for each char once they change they're not equal.
You are checking the identity charTime and buf. To check the equality, loop over each character in one array and compare them with the related character in the other array.