0

Here goes my code:

#include <iostream>
using namespace std;
int countX(char*, char);

int main() {
    char msg[] = "there are four a's in this sentence a a";
    //char *ptr = msg; // <----- question 2!
    cout << countX(msg, 'a');
    cin.get();
}

int countX(char* ptr, char x) {
    int c = 0;
    for (; *ptr; ptr++) {
        if (*ptr == x) c++;
    }
    /*
    while(*ptr) {
        if(*ptr==x) c++;
        ptr++;
    }
    */
    return c;
}

I was wondering a few things specifically regarding safe practice and pointers:

  1. My conditional statement in the for-loop ; *ptr ;, is this safe practice? Will it every break if there happens to be something stored in the memory address right next to the last element in the array? Is that even possible? How does it know when to terminate? When is *ptr deemed unacceptable?
  2. (concerning the commented out char *ptr = msg; in the main): I understand a pointer and an array are very similar, however, is there a difference between passing the actual array to countX vs. passing a pointer (which points to the beginning of the array?).
  3. In countX I've provided two different ways to approach the simple problem. Is one considered superior over the other?
  • don't use raw pointer if you really care about safety. this is C++, not C. you can use `std::string` and `std::count_if` – Bryan Chen Mar 21 '14 at 03:56
  • 1
    I'm in the learning phase, so I'm just attempting to understand how stuff works by playing around with it. Not necessarily concerned with safety at this point. Just wondering what's considered good practice, lest I drill bad technique into my head this early on. – Jhomas Tefferson Mar 21 '14 at 04:04
  • Suggestion: at the very beginning, before the first dereferencing of `*ptr`, would you please check that the pointer itself `ptr` is not null? (This check only need to be done once, and before the dereferencing.) Programming in C-style requires more defensiveness than with any other languages. – rwong Mar 21 '14 at 05:39
  • arrays and pointers are not "very similar", they are very different. An array is a block of objects next to each other; a pointer is telling you where another object can be found in memory. Arrays are not "basically a constant pointer" or anything like that. However, it is a common idiom to point to the first element of an array. – M.M Mar 21 '14 at 07:06

3 Answers3

1

Q My conditional statement in the for-loop ; *ptr ;, is this safe practice?

A Yes, most of the time. See below for more details.

Q Will it every (I know you meant ever) break if there happens to be something stored in the memory address right next to the last element in the array?

A Yes.

Q Is that even possible?

A Yes. You can easily access the memory one past the last character of the array and make it something other than the null character.

Q How does it know when to terminate?

A It will terminate when you encounter the terminating null character of a string. If the null character has been replaced by something else, the behavior is going to be unpredictable.

Q When is *ptr deemed unacceptable?

A If the string length is len, it is OK to set ptr in the range msg and msg+len. If ptr points to anything beyond that range, the behavior is undefined. Hence, they should be considered unacceptable in a program.

Q (concerning the commented out char *ptr = msg; in the main): I understand a pointer and an array are very similar, however, is there a difference between passing the actual array to countX vs. passing a pointer (which points to the beginning of the array?).

A No. They are identical.

Q In countX I've provided two different ways to approach the simple problem. Is one considered superior over the other?

A No they are not. It comes down to personal taste. I happen to like to use for loops while I know people that like to use while loops.

R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • Thank you for the reply! Quick question: When you say to keep `ptr` in the range of `msg` and `msg+len`, is that because since `msg` is a constant pointer to the first element, thus indicating start of array? – Jhomas Tefferson Mar 21 '14 at 07:39
0

Q1 : My conditional statement in the for-loop ; *ptr ;, is this safe practice? Will it every break if there happens to be something stored in the memory address right next to the last element in the array? Is that even possible? How does it know when to terminate? When is *ptr deemed unacceptable?

Ans : When used with c-style strings, yes it is a safe practice. Any c-style string necessarily ends with '\0', which is basically 0. The behavior is undefined when the '\0' is not there. So the loop would break at the end of the string. *ptr would never terminate if it is anything other than a c-style string. For example, a c-style "hello" is actually an array containing 'h', 'e', 'l', 'l', 'o', '\0'. So, the loop exists at '\0', never accessing the memory after it. it is possible to access the memory after the last element of an array. For example,

int a[5] = {0,1,2,3,4,5};
int *p = a+5;

p is accessing the element after the last element of the array a.

Q2 :(concerning the commented out char *ptr = msg; in the main): I understand a pointer and an array are very similar, however, is there a difference between passing the actual array to countX vs. passing a pointer (which points to the beginning of the array?).

Ans : Arrays and pointers are not exactly similar. Its just that an array name is nothing but a constant pointer pointing to the first element of the array. Consider the previous example i wrote. In that, a[3], 3[a], *(a+3) and *(p+3), all refer to the same element. Since you are passing by value, the value of the constant pointer msg would just be copied to ptr. So, no, it would make no difference.

Q3 : In countX I've provided two different ways to approach the simple problem. Is one considered superior over the other?

Ans : I am not an expert, but i'd say no.

Also, you probably dont need the cin.get().

  • It is not possible to access the memory after the last element of an array. In your example, `p` points to one-past-the-end, which is OK, but does not access it (which would be undefined behaviour if you tried). – M.M Mar 21 '14 at 07:05
  • 1
    Thank you very much for replying! The array name being a constant pointer to first element is something I haven't thought of before. Also, the `cin.get()` is there to keep the cmdprompt open (until ENTER), so I can catch the result before it closes. Unless there's a better method of doing this that I'm unaware of? Or did you mean for a completely other reason? To Matt McNabb, yeah, I've done some light reading into undefined behaviour, quite new to that as well. Coming to C++ from Java, it's definitely less restrictive, I've found already. – Jhomas Tefferson Mar 21 '14 at 07:16
  • @MattMcNabb - yes, it would access the memory. If you try to get the value displayed, it would display the value in that memory location. It is undefined because we don't know what that value is. For example, if the pointer is of type int as in this case, it would treat the next sizeof(int) bits after the last element as the contents of one integer variable and display them. – Abhishek Bagchi Mar 21 '14 at 08:46
  • It does not access the memory unless you dereference the pointer (and just saying `p = a+5` is not a dereference). If you do dereference the pointer then the behaviour is undefined, because the C++ standard says it is undefined. If it "treats the next N bits" or whatever, that is some specific detail of your implementation that does not apply universally. – M.M Mar 21 '14 at 09:18
  • @OP, it's good that you had not thought of the array name being a "constant pointer to first element" before, because it's not true. What actually happens is that if you use the array name in an expression other than as the operand of `&` or `sizeof`, then it is implicitly converted to a non-`const` pointer to the first element. This pointer is an rvalue. In other words, `a` and `&a[0]` are the same except in those two cases I mentioned. – M.M Mar 21 '14 at 09:20
-2

This is very bad practice.

What you're doing in the for condition is basically if (*ptr), in other words, does the memory pointed to by ptr contain a non-zero value?

So if the memory location after the string contains a non-zero value (maybe from another variable using the space) or a garbage value then your loop could go infinite, or give you an incorrect value. Instead you should run the loop from 0 to the length of your string.

Aakash Jain
  • 1,963
  • 17
  • 29
  • 1
    On the contrary, this is normal and quite acceptable C code under certain conditions. Such code will work fine as long as ptr is valid, and pointing to a null-terminated array. If it isn't then you have big problems. In the C world, this is how things are done. Note that the loop is designed to stop once it reaches the first zero value. In C++ there are safer alternatives, but even then, pointers and iterators are commonly used in ways that can be similarly fragile. – sj0h Mar 21 '14 at 04:32
  • So, char arrays (strings) would be safe in this context? Unless something happens (by some chance) to overwrite the null-terminator in memory? (I assume this is impossible unless done manually?) Also, forgive this question: int/float/other typed arrays, are they null terminated as well? Which leads to are the question are all arrays null terminated? If so, why specify null-terminated arrays? What's an example of an array without the aformentioned termination? – Jhomas Tefferson Mar 21 '14 at 07:12
  • 1
    Only strings are null terminated. Other types of arrays are not. A char array in which you explicitly assigned values for each character and did not put a `\0` at the end would not work in your loop. – Aakash Jain Mar 21 '14 at 08:02