I know that the correct way to compare "strings" in C is by using strcmp, but now I tried comparing some character arrays with the == operator, and got some strange results.
Take a look at the following code:
int main()
{
char *s1 = "Andreas";
char *s2 = "Andreas";
char s3[] = "Andreas";
char s4[] = "Andreas";
char *s5 = "Hello";
printf("%d\n", s1 == s2); //1
printf("%d\n", s3 == s4); //0
printf("%d\n", s1 == s5); //0
}
The first printf correctly prints a 1, which signals that they are not equal. But can someone explain to me why, when comparing the character arrays, the == is returning a 0 ?
Can someone please explain to me why the first printf is returning a 1 (ie, they are equal) and the character arrays are returning a 0 ?
The == is comparing the memory address.
It's likely that your compiler is making s1 and s2 point to the same static data to save space.
ie. The "Andreas" in the first two lines of code is stored in your executable data. The C standard says these strings are constant and so has optomized the two pointers to point to the same storage.
The char[] lines create a variable by copying the data into the variable and so are stored at different address on the stack during execution.
Uh... when == prints a 1, it means they are equal. It's different from strcmp, which returns the relative order of the strings.
You are comparing addresses and not the strings. The first two are constant and will only be created once.
int main()
{
char *s1 = "Andreas";
char *s2 = "Andreas";
char s3[] = "Andreas";
char s4[] = "Andreas";
char *s5 = "Hello";
printf("%d\n", s1 == s2); //1
printf("%p == %p\n", s1, s2);
printf("%d\n", s3 == s4); //0
printf("%p != %p\n", s3, s4);
printf("%d\n", s1 == s5); //0
printf("%p != %p\n", s1, s5);
}
Output on my computer, but you get the idea:
1
0x1fd8 == 0x1fd8
0
0xbffff8fc != 0xbffff8f4
0
0x1fd8 != 0x1fe0
Wait a sec... 1 means true, 0 means false. So your explanation is partially backwards. As for why the first two strings seem to be equal: The compiler built that constant string (s1/2) just once.
s1 == s2 means "(char*) == (char*)" or that the addresses are the same.
Same thing for s3 == s4. That's the "arrays decay into pointers" at work.
And you have the meaning of the result of the comparison wrong:
0 == 0; /* true; 1 */
42 == 0; /* false; 0 */
"foo" == "bar"; /* false (the addresses are different); 0 */
All the values from s1 through s5 aren't char themselves, they're pointers to char. So what you're comparing is the memory addresses of each string, rather than the strings themselves.
If you display the addresses thus, you can see what the comparison operators are actually working on:
#include <stdio.h>
int main() {
char *s1 = "Andreas";
char *s2 = "Andreas";
char s3[] = "Andreas";
char s4[] = "Andreas";
char *s5 = "Hello";
printf("%p\n", s1); // 0x80484d0
printf("%p\n", s2); // 0x80484d0
printf("%p\n", s3); // 0xbfef9280
printf("%p\n", s4); // 0xbfef9278
printf("%p\n", s5); // 0x80484d8
}
Exactly where the strings are allocated in memory is implementation specific. In this case, the s1 and s2 are pointing to the same static memory block, but I wouldn't expect that behaviour to be portable.
You can't compare strings, but you can compare pointers.
Related
I want to know what is compared when we have s1 > s2 and what is the result (for example when we compare chars their ASCII corresponding codes are compared). I read that with this operation are compared the first chars of s1 and s2, but if I try to compare with s1[0] > s2[0] the result is different, then it cannot be that.
Comparing with == means checking if the pointers point to the same object, so this:
char s1[] = "foo";
char s2[] = "foo";
if(s1 == s2)
puts("Same object");
else
puts("Different object");
would print "Different object".
< and > makes absolutely no sense unless the pointers are pointing to the same object. For instance, you could do like this:
char s[] = "asdfasdf";
char *p1 = &s[3], *p2 = &s[6];
if(p1 < p2)
// Code
If you have character arrays like for example
char s1[] = "Hello";
char s2[] = "Hello";
then in an if statement like for example this
if ( s1 == s2 ) { /*,,,*/ }
the character arrays are implicitly converted to pointers to their first elements.
So the above statement is equivalent to
if ( &s1[0] == &s2[0] ) { /*,,,*/ }
As the arrays occupy different extents of memory then the result of such a comparison will be always equal to logical false.
If you want to compare strings stored in the arrays you need to use the standard string function strcmp declared in the header <string.h>.
For example
#include <string.h>
#include <stdio.h>
//...
if ( strcmp( s1, s2 ) == 0 )
{
puts( "The strings are equal." );
}
else if ( strcmp( s1, s2 ) < 0 )
{
puts( "The first string is less than the second string." );
}
else
{
puts( "The first string is greater than the second string." );
}
You could use the following macro that might be considered a slight abuse of preprocessor.
#define STROP(a, OP, b) (strcmp(a, b) OP 0)
Examplary usage:
STROP(s1, >=, s2) // expands to `strcmp(s1,s2) >= 0`
STROP(s1, ==, s2) // expands to `strcmp(s1, s2) == 0`
This question already has answers here:
Addresses of two char pointers to different string literals are same
(10 answers)
String Literal address across translation units [duplicate]
(2 answers)
Is comparing the same string literals always true? [duplicate]
(2 answers)
Closed 2 years ago.
I'm studying strings and pointers in C.
From what I have studied, I understand that if I write:
char * s = "hello";
I am creating a 6 character / 6 byte array and making char * s point to this newly created array In the example above I create two strings a and b:
char a [20];
char b [20];
and then two pointers s and s2 pointing to a and b:
char * s = a;
char * s2 = b;
If I write:
if (s == s2)
I get printed False on screen, because, ACCORDING TO MY LOGIC, I am comparing if email
pointed to by s is equal to what s2 is pointing to. So far I have understood everything.
Now the part that I didn't understand. If instead of creating two strings and assigning addresses to
pointers, I wrote:
char * s = "hello";
char * s2 = "hello";
if (s == s2)
I am printed True.
I don't understand why. This second conditional statement shouldn't compare whether to use
is pointed by s equal to what s2 is pointing to?
I expected False to be printed again. Help !!
#include <stdio.h>
#include <stdlib.h>
int main()
{
{
char a[20] = "hello";
char b[20] = "hello";
char *s = a;
char *s2 = b;
if(s == s2)//address of s == address of s2 ? False
{
printf("True\n");
}
else
{
printf("False\n");
}
}
//if i try to do :
char *s = "hello";
char *s2 = "hello";
if(s == s2)//the result is True, but i don't understood because.
{
printf("True\n");
}
else
{
printf("False\n");
}
return 0;
}
i feel kinda lost, since we started learning about pointers i kinda cant follow and i know its really important subject in C.
anyway!
so i got to make a recursive function, that will get 2 pointers:
1) pointer to index [0].
2) pointer 2 to the middle of the string.
now.. i gotta check if the first part from 0 to middle is equal from middle to end. like..... ADAMADAM.
before i transfer the string i changed entire lower letters to capitals to avoid case sensitivity... so i got something like this.. but its refusing to work.
also using constant is prohibited...
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define TRUE 1
#define FALSE 0
#define SS 81
int CheckString(char *,int *);
int main() {
char str[SS];
int length,len,strcheck;
printf("Please enter a string:\n");
fgets(str,SS,stdin);
len=(strlen(str) - 1);
if((len>0)&&(str[len]=='\n')) // replacing '\n' key to '\0'
str[len]='\0';
length=len/2;
strcheck=CheckString(str,&length);
if (strcheck==FALSE)
printf("FALSE.\n");
else
printf("TRUE.\n");
return 0;
}
// function
int CheckString(char *str, int *i) {
if (*str != '\0')
if (*str == str[*i])
return CheckString(str+1,i++);
else
return FALSE;
return TRUE;
}
so i guess i got some problem with the pointers
It seems you mean the following
#include <stdio.h>
#include <string.h>
int CheckString(const char *s, size_t *i)
{
return s[*i] == '\0' || *s == s[*i] && CheckString(s + 1, i);
}
int main( void )
{
char *s = "ADAMADAM";
size_t i = strlen(s) / 2;
int result = CheckString(s, &i);
printf("%s\n", result ? "true" : "false");
return 0;
}
The program output
true
Note: maybe you should calculate the value for the second argument the following way
size_t i = ( strlen(s) + 1 ) / 2;
Think about this.
The outer condition in the loop inside CheckString() should be checking for *(str + *i) != '\0', or equivalently, for str[*i] != '\0'. Also, you do not need to increment *i, and certainly not i, since that is a pointer. The value *i is the distance between the characters being checked in the two halves of the string.
The modified function could look like:
int CheckString(char *str, int *i) {
if (str[*i] != '\0') {
if (*str == str[*i]) {
return CheckString(str+1,i);
} else {
return FALSE;
}
}
return TRUE;
}
The problem specification says (more or less):
I've got to make a recursive function that will get 2 pointers:
pointer 1 to index [0].
pointer 2 to the middle of the string.
I've got to check if the first part from 0 to middle is equal to the second part from middle to end, like: ADAMADAM.
As an exercise in recursion, this is fine; as a way of implementing the functionality, recursion is overkill (iteration is fine).
There is confusion (ambiguity) about the interface to the function — the wording of the question seems to suggest two char * values, but the code uses a pointer to an integer as the second argument. That's singularly peculiar. An integer value could make sense, but a pointer to an integer does not.
We need to define the conditions carefully. Taking the example string given (char str1[] = "ADAMADAM";), the two pointers might be char *p1 = &str1[0]; char *p2 = &str1[0] + strlen(str1) / 2; — meaning p1 points to the first A and p2 to the third A. Consider an alternative string: char str2[] = "MADAMADAM";; The equivalent formula would leave p1 pointing at the first M and p2 pointing at the second M.
Assuming p1 and p2 are incremented in lock-step, then:
The strings are different if, at any point before *p2 equals '\0', *p1 != *p2.
If *p2 equals '\0', then the strings are the same.
By definition, p1 and p2 point to the same array, so pointer differences are legitimate.
Further, p1 must be less than p2 to be useful; p1 equal to p2 means the strings are identical trivially.
There is a strong argument that the 'middle of the string' criterion means that either p2[p2 - p1] == '\0' or p2[p2 - p1 + 1] == '\0' (for even and odd string lengths respectively). That is, the distance between the two pointers indicates where the end of the string must be. It means that using p1 = &str[0] and p2 = &str[2] (on either of the sample strings) should fail because the end of string isn't in the right place. And if the string was "AMAMAMAM", using &str[0] and &str[2] should fail because the end of string isn't in the right place; ditto &str[0] and &str[6].
However, this 'strong argument' is also a design decision. It would be feasible to simply demand that the substring from p2 to EOS (end of string) is the same as the string from p1 for the same length. In that case, using &str[0] with either &str[2] or &str[6] (or, indeed, with the normal &str[4]) on "AMAMAMAM" would work fine.
Using some of these observations leads to this code. If you're really under instructions not to use const, simply remove the const qualifiers where they appear. The code will work the same.
#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
static bool identical_halfstrings(const char *p1, const char *p2)
{
assert(p1 <= p2);
assert(strlen(p1) >= strlen(p2) + (p2 - p1));
if (*p2 == '\0')
return true;
if (*p1 != *p2)
return false;
return identical_halfstrings(p1+1, p2+1);
}
int main(void)
{
const char *strings[] =
{
"ADAMADAM",
"MADAMADAM",
"nonsense",
};
enum { NUM_STRINGS = sizeof(strings) / sizeof(strings[0]) };
for (int i = 0; i < NUM_STRINGS; i++)
{
const char *p1 = strings[i];
const char *p2 = strings[i] + strlen(strings[i]) / 2;
printf("[%s] ([%s]) = %s\n", p1, p2,
identical_halfstrings(p1, p2) ? "TRUE" : "FALSE");
}
return 0;
}
The second assertion ensures that p1 and p2 are pointing to the same string — that there isn't a null byte between the locations pointed at by p1 and p2.
Test case output:
[ADAMADAM] ([ADAM]) = TRUE
[MADAMADAM] ([MADAM]) = TRUE
[nonsense] ([ense]) = FALSE
Just for the record, an iterative version of the same function is:
static bool identical_halfstrings(const char *p1, const char *p2)
{
assert(p1 <= p2);
assert(strlen(p1) >= strlen(p2) + (p2 - p1));
while (*p2 != '\0')
{
if (*p1++ != *p2++)
return false;
}
return true;
}
It produces the same output for the sample data.
I am trying to cast s2 to make the test pass.
I store printable characters together with unsigned char values into s3.
s2 is a test string result meant to verify printable char are loaded properly in s3.
#include <stdio.h>
#include <string.h>
#define test_string_len 2
union {
char unsigned us[test_string_len];
char s1[test_string_len];
} result;
main() {
char *s2;
s2= "ab";
char unsigned s3[test_string_len];
s3[0] = 'a';
s3[1] = 'b';
s3[2] = '\0';
memcpy (result.us, s3, test_string_len);
if ( result.s1 == s2) {
printf("Pass\n");
}
printf("s2 = %s\n", s2 );
printf("s3 = %s\n", s3 );
printf("result.s1 = %s\n", s3 );
printf("result.us = %s\n", result.us );
getchar();
}
It's not possible to compare strings (or other arrays) with == in C. This is because an array such as int myarray[8]; can essentially be thought of as a pointer called myarray storing the address of the first element. In other words, == would compare the starting address of the array, rather than the contents of the actual array items being pointed to.
Instead, you must either use the strcmp or memcmp functions, or use a for() loop to cycle through each index and check that the values in each array match.
You need to change your code as follows.
if(!strcmp( result.s1,s2)) {
printf("Pass\n");
}
I'm trying to self-study C using C Primer Plus from Stephen Prata and one of the end-of-chapter exercises is to "Write a function that replaces the contents of a string with the string reversed.". This is a chapter on character strings with a good dose of pointers. I'm trying to use pointers as much as possible so I can better understand, but I'm stuck.
My problem is that when I print the value of the return pointer in main, it is garbled.
When I use gdb(just learning how to use that too), I can see that the memory address returned from my function is the same address that was used in the function and it's getting assigned to my pointer in main okay as far as I can tell.
I've tried so many things, what am I missing? FWIW I have not learned about malloc yet in the book, though I see it referenced on various www pages I've frequented trying to better understand C.
$ cc -o exercise8 exercise8.c && ./exercise8
This is s1 before: abcd
This is s2 in function: dcba
This is s3 after: d`!
/* A function that replaces the contents of a string with the string reversed. */
#include <stdio.h>
#include <string.h>
char *str_rev(char * string);
int main(void)
{
char * s1 = "abcd";
char * s3;
printf("This is s1 before: %s\n", s1);
s3 = str_rev(s1);
printf("This is s3 after: %s\n", s3);
}
char *str_rev(char * string)
{
char ar3[5];
char * s2;
int len = 0;
s2 = ar3;
len = (strlen(string) - 1);
string = string + len;
while ( len >= 0 )
{
*s2 = *string;
len--;
string--;
s2++;
}
s2++;
*s2 = 0;
s2 = s2 - 5;
printf("This is s2 in function: %s\n", s2);
return s2;
}
$ gdb exercise8
GNU gdb (GDB) 7.1-ubuntu
Reading symbols from exercise8...done.
(gdb) break 12
Breakpoint 1 at 0x804844a: file exercise8.c, line 12.
(gdb) break 40
Breakpoint 2 at 0x80484d9: file exercise8.c, line 40.
(gdb) run
Starting program: exercise8
This is s1 before: abcd // My original string.
This is s2 in function: dcba // Good, my reversed string while in the function.
Breakpoint 2, str_rev (string=0xbffff043 "dcba") at exercise8.c:40
40 return s2;
(gdb) print s2
$1 = 0xbffff043 "dcba" // Location of pointer s2.
(gdb) continue
Continuing.
Breakpoint 1, main () at exercise8.c:12
12 printf("This is s3 after: %s\n", s3);
(gdb) print s3
$2 = 0xbffff043 "dcba" // Back in main same pointer as s2 from function.
(gdb) step
This is s3 after: d`Q // Line 12 executed. Output garbled.
14 }
(gdb)
You're returning a pointer to a local variable, (automatic variable to speak in ISO standard terms,) which is allocated on the stack, as soon as you return from your function that memory is released leaving you with a dangling pointer pointing to memory that may or may not still contain the string you put there, it depends entirely on circumstances. You should provide the output buffer as a function argument, or allocate it with malloc, or in C++ with new.
edit; added some example code
void reverse(const char* s1, char* s2) {
const int l = strlen(s1);
const char* p = s1 + l - 1;
do {
*s2++ = *p;
} while (p-- != s1);
*s2 = 0;
}
int main() {
// some code here
char s1[5] = "abcd";
char s2[5] = "";
reverse(s1, s2);
// some more code here
return 0;
}
or
char* reverse(const char* s) {
const int l = strlen(s);
char* rs = malloc(l+1);
const char* p = s + l - 1;
do {
*rs++ = *p;
} while (p-- != s);
*rs = 0;
return rs - l;
}
int main() {
// some code here
char s1[5] = "abcd";
char* s2 = reverse(s1);
// some more code here
free(s2);
return 0;
}
char ar3[5];
char * s2 = ar3;
The above code will make s2 points to a character string on the stack. This ar3 variable will be deleted once your function finishes.
You should output to some variable that you have pre-allocated. Modify it as follow
int main(void)
{
char * s1 = "abcd";
char s3[5];
printf("This is s1 before: %s\n", s1);
str_rev(s1, s3);
printf("This is s3 after: %s\n", s3);
}
void str_rev(char * string, char * s2)
{
........
// don't return
// Also assign the last character with the NULL terminator
ar2[strlen(string)] = '\0';
}
Of course, once you get to the chapter regarding malloc, you can allocate the necessary memory for s3 depending on the length of s1. Until then, read on and have fun.
The problem description sounds like you can just reverse the string in place. Keep it simple.
void reverse_range(char *first, char *last) // [first, last)
{
for (; first != last && first != --last; ++first)
{
char temp = *first; *first = *last; *last = temp;
}
}
void reverse(char *str)
{
reverse_range(str, str + strlen(str));
}
int main()
{
char text[] = "0123456789";
printf("before: %s\n", text);
reverse(text);
printf("after : %s\n", text);
}
s2 points to your local ar3 array. Therefore when str_rev returns, it is no longer valid to look at the memory where ar3 was via s2. Now s2 is called a dangling pointer, and this is one of the huge pains in learning to use C pointers correctly.
For a simple solution that doesn't use malloc and meets the exercise requirement of "replaces the contents of a string", try copying the result into the function argument pointer (the original string; but be careful since your current code has since changed the pointer string). You know this pointer points at memory with enough characters and isn't local to your function.
Part of the problem is that you can't actually "replace the contents of a string with the string reversed" when you are dealing with string literals, ie. strings delcared in the form char * s1 = "abcd";
Without using literals, I made a relatively easy to understand recursive example:
/* A function that replaces the contents of a string with the string reversed. */
#include <stdio.h>
#include <string.h>
void str_rev(char * string);
int main(void)
{
char s1[] = "abc";
char s2[] = "even";
char s3[] = "quodd";
printf("This is s1 before: %s\n", s1);
str_rev(s1);
printf("This is s1 after: %s\n", s1);
printf("This is s2 before: %s\n", s2);
str_rev(s2);
printf("This is s2 after: %s\n", s2);
printf("This is s3 before: %s\n", s3);
str_rev(s3);
printf("This is s3 after: %s\n", s3);
return 0;
}
void str_rev(char * string) {
//Store the first char of the string locally
char firstChar = string[0];
//Store the last char of the string locally
int lastCharPos = strlen(string)-1;
char lastChar = string[lastCharPos];
//Shorten the string (temporarily)
string[lastCharPos] = '\0';
if (string[1] != '\0') {
//Call on the now shortened string, eg.
//"abc" becomes "b"
//"even" becomes "ve"
//"quodd" becomes "uod"
str_rev(string+1);
}
//Swap the first and last characters
string[0] = lastChar;
string[lastCharPos] = firstChar;
}
On my system the output is as follows:
This is s1 before: abc
This is s1 after: cba
This is s2 before: even
This is s2 after: neve
This is s3 before: quodd
This is s3 after: ddouq
#include <stdio.h>
#include <string.h>
void my_strrev(char* begin){
char temp;
char* end;
end = begin + strlen(begin)-1;
while(end>begin){
temp = *end;
*end = *begin;
*begin = temp;
end--;
begin++;
}
}
main(){
char string[]= "foobar";
my_strrev(string);
printf("%s", string);
}