I'm new to C and while going through pointers I don't quite understand why do I need to use *p in the while loop condition check below.
It is a very simple function that counts occurences of the character x in the array with an example invocation as in the below main function. We assume here that p will point to an array of chars. Purely for demonstration. Thanks!
int count(char* p, char x) {
int count = 0;
while (*p != NULL) { // why *p requried here if p is already a pointer?
if (x == *p) count ;
p ;
}
return count;
}
int main(){
char a[5] = {'a','a','c','a',NULL};
char* p = a;
std::cout << count(p, 'a') << std::endl;
}
I.e why do I need
while (*p != NULL)
Since p is already a pointer I thought
while (p != NULL)
should be enough, but program crashes.
CodePudding user response:
Incrementing the pointer will make it point to the next element in the character array. Incrementing the pointer will never make it equal to a nullpointer or NULL
.
c-strings are nul-terminated. The end of the string is marked with an element with value \0
. In main this is the last element of the array and the loop in the function will stop when it reaches that last element.
p
is the pointer to the element. *p
is the element.
Using NULL
for that condition is misleading. NULL
should not be used in C anymore. A null pointer is nullptr
and the terminator in strings is '\0'
. The code works nevertheless because NULL
just happens to equal 0
and '\0'
. Tough, it was meant to be used for pointers, not for char
.
The code can be written like this:
int count(char* p, char x) {
int count = 0;
while (*p != '\0') { // why *p requried here if p is already a pointer?
if (x == *p) count ;
p ;
}
return count;
}
int main(){
char a[5] = {'a','a','c','a','\0'};
std::cout << count(a, 'a') << std::endl;
}
Or better, use std::string
and std::count
:
#include <string>
#include <algorith>
int main() {
std::string s{"aaca"};
std::cout << std::count(s.begin(),s.end(),'a');
}
Note that string literals automatically include the terminator. So "aaca"
is a const char[5]
, an array of 5 characters, and the last one is '\0'
. With std::string
the details are a little hairy, but s[4]
is also '\0'
. Note that this is in contrast to other containers, where container[container.size()]
is out-of-bounds and wrong.
CodePudding user response:
p
is a pointer to char
. So if you check the value of p
it will be an address to that char
(the first char
in a string or array). So that address will be non-zero whether you are pointing to the first character or the last character.
In C or C strings are traditionally null terminated, meaning that the end of a string is marked by the null-terminator which is a single char with the value 0. To check the value of the char
that the pointer p
is pointing to, you need to de-reference it. De-referencing is done by prepending a *
to the expression. In this case we extract the value that p
is pointing to and not the address that p
points to.
You are basically having an array of char
, and as an example it might look like this in memory:
Address | ASCII value | Value |
---|---|---|
1000 | 97 (0x61) | a |
1001 | 97 (0x61) | a |
1002 | 99 (0x63) | c |
1003 | 97 (0x61) | a |
1004 | 0 (0x00) | NULL |
To begin with will point to the first char, that is address 1000, so the value of p
is 1000, and the value of *p
is 97 or 'a'. As you increment p
it will change to 1001, 1002, etc. until it gets to 1004 where the value of p
is 1004 and the value of *p
will be 0.
Had you written while (p != NULL)
instead of *p
you would essentially have checked whether 1004 != 0
which would be true, and you would continue past the end of the string.
CodePudding user response:
I know a lot of (older) tutorials start with (naked) pointers and "C" style arrays but they are really not the first things you should use. If possible in C try to write solutions not depending in pointers. For holding text, use std::string.
#include <string> // stop using char* for text
#include <algorithm> // has the count_if method
#include <iostream>
int count_matching_characters(const std::string& string, char character_to_match)
{
int count{ 0 };
// range based for loop, looping over al characters in the string
for (const char c : string)
{
if (c == character_to_match) count ;
}
return count;
// or using a lambda function and algorithm
/*
return std::count_if(string.begin(), string.end(), [&](const char c)
{
return (character_to_match == c);
});
**/
}
int main()
{
int count = count_matching_characters("hello world", 'l');
std::cout << count;
return 0;
}