Why do we need a pointer here - c++

I'm new to C++, and while going through pointers I don't quite understand why I need to use *p in the while loop condition check below.
It is a very simple function that counts occurrences 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.
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;
}
Why do I need
while (*p != NULL)
Since p is already a pointer, I thought
while (p != NULL)
should be enough, but the program crashes.

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.

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.

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;
}

Related

Aren't pointers supposed to point to nullptr when an array ends?

Why does this code not work?, I used the function from "A Tour of C++" and it tells me that pointers point to nullptr when an array ends briefly explained. I tried to implement it and it doesn't show anything. Thanks in advance.
#include <iostream>
int count_x(char* p, char x)
{
int count = 0;
while (p)
{
if (*p == x){++count;}
p++;
}
return count;
}
int main()
{
char my_string[] {"hello world"};
char* my_string_ptr {my_string};
std::cout << "There are " << count_x(my_string_ptr,'a') << " a in the string\n";
return 0;
}```
No, pointer at the end of an array are not null. You probably want:
while (*p)
which is the same as
while (*p != '\0')
and
while (*p != 0)
which are testing for the null character.
p stores an address value in the computer memory which is deferenced by astricks (*) like *p.
In your code,
p++;
is incrementing the current address to which the p is pointing currently. When you reach the end of the string (at the last null character), p will be the address of the null character not the null itself, so
while(p)
will be true and following p++ will increment it to next memory location (which technically does not belong to the allocated string), and hence p will return non-zero address this while loop will keep on running and you will get a segmentation fault.
As the jeffrey has mentioned in his answer too, use *p to test the while condition so when p reaches the null, it can derefer it.
Your original code crashed (or caused segmentation fault). The reason is that in the while loop, you did not correctly specify the condition to exit this while loop. Therefore, the while loop will continue to go past the end of the string, which causes segmentation fault (i.e, your app will crash).
One correct way to exit the while loop is to compare each character of the string to see if that character equals the end of string character that is '\0'.
When you encounter this end of string character that is '\0', you should exit the while loop.
I have fixed one line of your code, and your function is now working fine as shown below:
int count_x(char* p, char x)
{
int count = 0;
while (*p != '\0') // I fixed your code here
{
if (*p == x){++count;}
p++;
}
return count;
}
Again, please note that the code above runs well, and produces the correct result as I have verified it. Please let me know if you run into any issue. Cheers.

What does while(p) mean? (p is a pointer to the first element of a C style string)

#include <iostream>
int main(void) {
char* c = "Hello World!";
char* p = c;
while (p && *p) {
std::cout << *p << std::endl;
++p;
}
return 0;
}
Look at the above.
It is a short code example in an exercise of C++.
I can understand while(*p) which means loop until the last character('\0') is reached.
But I can't understand while(p).
What does while(p) mean? (p is a pointer to the first element of a C style string)
Check that the pointer itselfs is not null - which may be of use if used in a function and p is an argument.
In this example it is useless since p can never be null since.
The while (p) part makes sure that the value of p is nonzero, and the while (*p) checks to make sure that the dereferenced value of p is nonzero.
Therefore, if the string itself (the pointer to it) p is nonzero, and it's current character *p is not '\0', the while loop block will execute.

Why can't I call my function recursively?

I'm trying to compile this code in order to reverse a string:
void reverse(char *str, int n)
{
if (n==0 || n==1) {
return; //acts as quit
} else {
char i = str[0]; //1st position of string
char j = str[n-1]; //Last position of string
char temp = str[i];
str[i] = str[j]; //Swap
str[j] = temp;
reverse(str[i+1],n-1); // <-- this line
}
}
#include <iostream>
int main()
{
char *word = "hello";
int n = sizeof word;
reverse(word, n);
std::cout << word << std::endl;
return 0;
}
The compiler reports an error where I call reverse() recursively:
invalid conversion from char to char* at reverse(str[i+1], n-1).
Why?
Any advice on other issues in my code is also welcome.
str[i+1] is a character, not a pointer to a character; hence the error message.
When you enter the function, str points to the character you're going to swap with the n:th character away from str.
What you need to do in the recursion is to increment the pointer so it points to the next character.
You also need to decrease n by two, because it should be a distance from str + 1, not from str.
(This is easy to get wrong; see the edit history of this answer for an example.)
You're also using the characters in the strings as indexes into the strings when swapping.
(If you had the input "ab", you would do char temp = str['a']; str['a'] = str['b']; str['b'] = temp;. This is obviously not correct.)
str[0] is not the position of the first character, it is the first character.
Use std::swap if you're allowed to, otherwise see below.
More issues: you shouldn't use sizeof word, as that is either 4 or 8 depending your target architecture - it's equivalent to sizeof(char*).
You should use strlen to find out how long a string is.
Further, you should get a warning for
char *word = "hello";
as that particular conversion is dangerous - "hello" is a const array and modifying it is undefined.
(It would be safe if you never modified the array, but you are, so it isn't.)
Copy it into a non-const array instead:
char word[] = "hello";
and increase the warning level of your compiler.
Here's a fixed version:
void reverse(char *str, int n)
{
if(n <= 1) // Play it safe even with negative n
{
return;
}
else
{
// You could replace this with std::swap(str[0], str[n-1])
char temp = str[0]; //1st character in the string
str[0] = str[n-1]; //Swap
str[n-1] = temp;
// n - 2 is one step closer to str + 1 than n is to str.
reverse(str + 1, n - 2);
}
}
int main()
{
char word[] = "hello";
// sizeof would actually work here, but it's fragile so I prefer strlen.
reverse(word, strlen(word));
std::cout << word << std::endl;
}
I'm going to dissect your code, as if you'd posted over on Code Review. You did ask for other observations, after all...
Firstly,
char *word = "hello";
Your compiler should warn you that pointing a char* at a literal string is undefined behaviour (if not, make sure that you have actually enabled a good set of warnings. Many compilers emit very few warnings by default, for historical reasons). You need to ensure that you have a writable string; for that you can use a char[]:
char word[] = "hello";
The next line
int n = sizeof word;
has now changed meaning, but is still wrong. In your original code, it was the size of a pointer to char, which is unlikely to be the same as the length of the word "hello". With the change to char[], it's now the size of an array of 6 characters, i.e. 6. The sixth character is the NUL that ends the string literal. Instead of the sizeof operator, you probably want to use the strlen() function.
Moving on to reverse():
You read characters from positions in the string, and then use those characters to index it. That's not what you want, and GCC warns against indexing using plain char as it may be signed or unsigned. You just want to index in one place, and your i and j are unnecessary.
Finally, the question you asked. str[i+1] is the character at position i+1, but your function wants a pointer to character, which is simply str+i+1. Or, since we worked out we don't want i in there, just str+1.
Note also that you'll need to subtract 2 from n, not 1, as it will be used as a count of characters from str+1. If you only subtract 1, you'll always be swapping with the last character, and you'll achieve a 'roll' rather than a 'reverse'.
Here's a working version:
void reverse(char *str, int n)
{
if (n < 2)
// end of recursion
return; //acts as quit
char temp = str[0];
str[0] = str[n-1]; //Swap
str[n-1] = temp;
reverse(str+1,n-2);
}
#include <iostream>
#include <cstring>
int main()
{
char word[] = "hello";
int n = std::strlen(word);
reverse(word, n);
std::cout << word << std::endl;
}
We can make further changes. For example, we could use std::swap to express the switching more clearly. And we could pass a pair of pointers instead of a pointer and a length:
#include <utility> // assuming C++11 - else <algorithm>
void reverse(char *str, char *end)
{
if (end <= str)
// end of recursion
return;
std::swap(*str, *end);
reverse(str+1, end-1);
}
and invoke it with reverse(word, word+n-1).
Finally (as I'm not going to mention std::reverse()), here's the idiomatic iterative version:
void reverse(char *str, char *end)
{
while (str < end)
std::swap(*str++, *end--);
}
use like this :
reverse(&str[i+1],n-1);
pass address of the (i+1)th position not value.

Pointer syntax and incrementation

#include <iostream>
#include <cstdlib>
using namespace std;
void reverse(char* str){
char *end = str;
char tmp;
if(str){
cout << "hello" << endl;
while(*end){
cout << end << endl;
++end;
}
--end;
while (str < end){
tmp = *str;
*str++ = *end;
*end-- = tmp;
}
}
}
int main(){
char str[] = "helloyouarefunny";
string input = str;
reverse(str);
for(int i = 0; i < input.length(); i++) {
cout << str[i];
}
}
Is if(str){} equivalent to if(str == NULL){}?
What does while(*end){} mean and what is it exactly doing? I think I have a general understanding that the while loop will continue to be executed as long as it does not "see" a '\0'. But I am not sure what is exactly going on with this line of code.
Given that if(str){} is an equivalent statement to if(str == NULL){}, what would you pass into a function to make str = NULL? For example, in my main(){}, I tried to do char str[] = NULL, thereby, attempting to pass a NULL so that it wouldn't go inside the code if(str == NULL){}. But I get an error saying I cannot make this declaration char str[] = NULL. So my question is why am I getting this error and what can I pass through the reverse() function in order to make the code inside of if(str){} not execute? I hope this question made sense.
And the code ++end is doing pointer arithmetic correct? So every time it is incremented, the address is moving to the address right next to it?
I'm a little confused while(str < end){}. What is the difference between just str and *str? I understand that cout << str << endl; has to do with overloading the operator << and therefore prints the entire string that is passed through the argument. But why, when I cout << *end << endl;, it only prints the character at that memory address? So my question is, what's the difference between the two? Is it just dereferencing when i do *str? I might actually be asking more than that question in this question. I hope I don't confuse you guys >_<.
Is if(str){} equivalent to if(str == NULL){}?
No, if(str){} is equivalent to if(str != NULL){}
What does while(*end){} mean and what is it exactly doing?
Since the type of of end is char*, while(*end){} is equivalent to while (*end != '\0'). The loop is executed for all the characters of the input string. When the end of the string is reached, the loop stops.
Given that if(str){} is an equivalent statement to if(str == NULL){}
That is not correct. I did not read rest of the paragraph since you start out with an incorrect statement.
And the code ++end is doing pointer arithmetic correct? So every time it is incremented, the address is moving to the address right next to it?
Sort of. The value of end is incremented. It points to the next object that it used to point to before the operation.
I'm a little confused while(str < end){}
In the previous while loop, end was incremented starting from str until it reached the end of the string. In this loop, end is decremented until it reaches the start of the string. When end reaches str, the conditional of the while statement evaluates to false and the loop breaks.
Update
Regarding
what would you pass into a function to make str = NULL?
You could simply call
reverse(NULL);
I tried to do char str[] = NULL;
str is an array of characters. It can be initialized using couple of ways:
// This is what you have done.
char str[] = "helloyouarefunny";
// Another, more tedious way:
char str[] = {'h','e','l','l','o','y','o','u','a','r','e','f','u','n','n','y', '\0'};
Notice the presence of an explicitly specified null character in the second method.
You cannot initialize a variable that is of type array of chars to to NULL. The language does not allow that. You can initialize a pointer to NULL but not an array.
char* s1 = NULL; // OK
reverse(s1); // Call the function
s1 = malloc(10); // Allocate memory for the pointer.
strcpy(s1, "test") // Put some content in the allocated memory
reverse(s1); // Call the function, this time with some content.
These are pretty standard C programming idioms.
No, in fact if (str) ... is equivalent to if (str != NULL) ...
C character strings are null terminated, meaning that "Hello" is represented in memory as the character array {'H', 'e', 'l', 'l', 'o', '\0'}. As with pointers, the 0 or NULL value is considered false in a logical expression. Thus while (*end) ... will execute the body of the while loop so long as end has not reached the null character.
N/A
Correct - this advances to the next character in the string, or to the null terminator.
This is the reverse algorithm. After the first loop, end points to one past the end of the string and str points to the beginning. Now we work these two pointers toward each other, swapping characters.
1/2) In C and C++, whatever is in the if or while is evaluated as a boolean. 0 is evaluated to false while any other value is evaluated to true. Given that NULL is equivalent to 0, if(str) and if(str != NULL) do the same things.
Likewise, while(*end) will only loop so long as the value end is pointing to does not evaluate to 0.
3) If you pass a char pointer to this function, it could be the null pointer (char *str = 0), so you're checking to make sure str is not null.
4) Yes, the pointer is then pointing to the next location in memory until eventually you find the null at the end of the string.
5) Perhaps your confusion is based around the fact that the code is missing parenthesis, the loop should look like:
while (str < end){
tmp = *str;
*(str++) = *end;
*(end--) = tmp;
}
So that the two pointers will continue to make there way towards eachother until crossing paths (at which point, str will no longer be less than end)

Counting Bytes Of Char S

I have a homework, It is:
Write the code of function below, this function should count number of bytes inside of s till it is not '\0'.
The function:
unsigned len(const char* s);
Really I do not know what this homework mean, can anyone write this homework's code please?
Further more can anyone please explain what does "Const char* s" mean? If you can explain with some examples it would be perfect.
Here is a code which I'm trying to do:
unsigned len(const char* s)
{
int count=0;; int i=0;
while (*(s+i)!=0)
{
count++;
i++;
}
return count;
}
But in the main function I do not know what should I write, BTW I have written this:
const char k='m';
const char* s=&k;
cout << len(s) << endl;
The result always is 4! really I do not know what should I do for this question, if I can store only one character in const char, so the result should be the same always. What this question is looking for exactly?
The homework means you should write a function that behaves like this:
int main() {
char s[] = {'a','b','c','\0'};
unsigned s_length = len(s);
// s_length will be equal to 3 ('a','b','c', not counting '\0')
}
I think it's unlikely that anyone will do you homework for you here.
Presumably your class has covered function parameters, pointers, and arrays if you're being asked to do this. So I guess you're asking about const. const char* s means that s points to a const char, which means you're not allowed to modify the char. That is, the following is illegal:
unsigned len(const char *s) {
*s = 'a'; // error, modifying a const char.
}
Here are the basic things you need to know about pointers to write the function. First, in this case the pointer is pointing at an element in an array. That is:
char A[] = {'a','b','c','\0'};
char const *s = &A[0]; // s = the address of A[0];
The pointer points to, or references, a char. To get that char you dereference the pointer:
char c = *s;
// c is now equal to A[0]
Because s points at an element of an array, you can add to and subtract from the pointer to access other elements of the array:
const char *t = s+1; // t points to the element after the one s points to.
char d = *t; // d equals A[1] (because s points to A[0])
You can also use the array index operator:
char c = s[0]; // c == A[0]
c = s[1]; // c == A[1]
c = s[2]; // c == A[2]
What would you used to look at each element of the array sequentially, with an increasing index?
Your proposed solution looks like it should work correctly. The reason you're getting a result of 4 is just coincidence. You could be getting any results at all. The problem with the way you're calling the function:
const char k='m';
const char* s=&k;
cout << len(s) << endl;
is that there's no '\0' guaranteed to be at the end. You need to make an array where one of the elements is 0:
const char k[] = { 1,2,3,0};
const char* s = &k[0];
cout << len(s) << '\n'; // prints 3
char m[] = { 'a', 'b', 'c', 'd', '\0', 'e', 'f'};
cout << len(m) << '\n'; // prints 4
char const *j = "Hello"; // automatically inserts a '\0' at the end
cout << len(j) << '\n'; // prints 5
In C (and by extension C++), strings can be represented as a sequence of characters terminated by a null character. So, the string "abc" would be represented as
'a', 'b', 'c', '\0'
This means, you can get the length of a C string by counting each character until you encounter a null. So if you have a null terminated const char* string, you can find out the length of that string by looping over the string and incrementing a counter variable until you find the '\0' character.
it means you have a string like hello world Every string terminates with a \0. That means it looks like this: hello world\0
Now step over the char array (char* s) until you find \0.
Update:
\0 is in fact only one single character of value 0x00. \ is used to tell visualize that this is meant instead of the number 0 in a string.
Example:
0abc\0 -> string starting with number 0 and is terminated with 0x0.
EDIT
char * indicates the type of the variable s. It is a pointer to a character array. const means that this character array is readonly and can't be changed.
Do you actually mean "count the characters till you find a '\0'"?
If so, you could implement it like this:
for each character
if it is not 0
increment x (where x is variable holding number of characters found)
otherwise
stop looking
return x
I am not going to write your homework as well :P, but let me give you some hint: it's called "pointer arithmetic". So, a pointer is a thing exactly just as it names says: a pointer to a memory "cell". As you know all variables in the memory are stored in "cells", that you can refer by an address. A C string is stored in continuous cells in the memory, so for example "abc" would look like something like (the '\0' is added by the compiler when you define a string literal with quotes):
+----+----+----+----+
|'a' |'b' |'c' |'\0'|
+----+----+----+----+
^
s
and you also get the address of the first char. Now, to get the address of 'b', you can simple add one to s like this: (s + 1). To get what is actually in the cell where s points to, you should use the * operator:
*s = 'a' or *(s + 1) = 'b'. This is called pointer arithmetic.
Note: in this case adding one to the pointer shifts to the next cell, because char is one byte long. If you define a pointer to bigger structure (long int for example of 4 bytes) adding one will move to the to the position in the memory where your next structure would begin (in case of long int it will move +4 bytes).
Now that should be enough help to finish your hw.
OK , I have found my answer, just check if I'm true:
#include <iostream>
using namespace std;
unsigned len(const char*);
int main()
{
const char* s = "Hello";
cout << len(s) << endl;
return 0;
}
unsigned len(const char* s)
{
int count=0;; int i=0;
while (*(s+i)!=0)
{
count++;
i++;
}
return count;
}
So it is showing that I have set "Hello" into const char* s; So for const char* variables I should use strings like "Hello" with the sign ("). Is that True?