2

Here is the code I'm having trouble to understand:

char* myPtr = "example";
myPtr[1] = 'x';

How am I allowed to use myPtr[1]? Why can I choose positions like a do on arrays? myPtr is not even an array.

Obs. I know about lookup table, literal pooling and string literals, my concern is just how this even compile. I don't use pointers that much.

Can anyone help?

kometen
  • 6,536
  • 6
  • 41
  • 51
  • 2
    You actually aren't allowed to do this in modern C++, where character literals are `const char *`s. Any self-respective, modern C++ compiler will refuse to compile this code. But, assuming we're talking about `const char *`, an array is really just a pointer to the first element of the array, so arrays and pointers are really like fraternal twins. – Sam Varshavchik Apr 21 '16 at 01:25
  • That was helpful. Thanks. :p – Luiz Heringer Apr 21 '16 at 01:31
  • `operator[]` is actually a pointer operation, not an array operation. That it works (or appears to work) on arrays is a consequence of array to pointer decay. – Benjamin Lindley Apr 21 '16 at 01:32
  • in C the name of an array is a synonym for the location (i.e. address in memory) of the initial element of the array (ref: The C programming Laguage by Ritchie et al pg 83) – Grant Shannon Apr 21 '16 at 01:36
  • 2
    Pointers are ***not*** arrays. Read section 6 of the [comp.lang.c FAQ](http://www.c-faq.com/). – Keith Thompson Apr 21 '16 at 01:41
  • You have every right to be confused. C's pointer semantics are completely nonsensical. Try "1 [myPtr] = 'x' ;" for example. – user3344003 Apr 21 '16 at 01:41
  • @user3344003: C's pointer semantics are entirely consistent, but have some counterintuitive consequences. – Keith Thompson Apr 21 '16 at 01:42
  • @SamVarshavchik: No, an array is not a pointer to its own first element. An expression of array type is, in most contexts, *implicitly converted* to a pointer to its initial element. – Keith Thompson Apr 21 '16 at 01:43
  • @gps: An array name is not a synonym for the address of its initial element. It's implicitly converted to a pointer in most contexts, but `sizeof array_object` yields the size of the entire array, not the size of a pointer, and `&array_object` yields the address of the entire array (same location as `&arr_object[0]`, but it has a different type). – Keith Thompson Apr 21 '16 at 01:48
  • 1
    Possible duplicate of [C: differences between char pointer and array](http://stackoverflow.com/questions/1335786/c-differences-between-char-pointer-and-array) – o11c Apr 21 '16 at 01:51
  • You may want to choose C or C++. They are different languages. – Shoe Apr 21 '16 at 02:06

6 Answers6

11

Apparently you made an assumption that applicability of [] operator to something necessarily implies that that "something" is an array. This is not true. The built-in [] operator has no direct relation to arrays. The [] is just a shorthand for a combination of * and + operators: by definition a[b] means *(a + b), where one operand is required to be a pointer and another is required to be an integer.

Moreover, when you apply the [] operator to an actual array, that array gets implicitly converted to a pointer type first, and only then the resultant pointer can act as an operand of [] operator. This actually means the opposite of what you supposedly assumed initially: operator [] never works with arrays. By the time we get to the [] the array has already decayed to a pointer.

As a related side-note, this latter detail manifests itself in one obscure peculiarity of the first C language standard. In C89/90 the array-to-pointer conversion was not allowed for rvalue arrays, which also prevented the [] operator from working with such arrays

struct S { int a[10]; };

struct S foo(void) { struct S s = { 0 }; return s; }

int main() 
{
  foo().a[5]; 
  /* ERROR: cannot convert array to pointer, and therefore cannot use [] */

  return 0;
}

C99 expanded the applicability of that conversion thus making the above code valid.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • "*The built-in `[]` operator has absolutely no direct relation to arrays whatsoever."* Perhaps, but it has a very strong *indirect* relation, in that the pointer must point to an element of an array object. – Keith Thompson Apr 21 '16 at 01:46
  • 1
    @Keith Thompson: Yes, but it can also be used with pointers to standalone objects (not arrays), since for the purpose of pointer arithmetic standalone objects act as arrays of size 1. And claiming that any standalone object can be seen as an array of size 1 *for all means and purposes* is probably too much (even though it is true). – AnT stands with Russia Apr 21 '16 at 01:47
  • Yes, again, it effectively has to be an array object. – Keith Thompson Apr 21 '16 at 01:48
1

Pointers hold the address of memory location of variables of specific data types they are assigned to hold. As others have pointed out its counter-intuitive approach take a bit of learning curve to understand.

Note that the string "example" itself is immutable however, the compiler doesn't prevent the manipulation of the pointer variable, whose new value is changed to address of string 'x' (this is not same as the address of x in 'example'),

char* myPtr = "example";
myPtr[1] = 'x';

Since myPtr is referencing immutable data when the program runs it will crash, though it compiles without issues.

From C perspective, here, you are dereferencing a mutable variable. By default in C, the char pointer is defined as mutable, unless specifically stated as immutable through keyword const, in which case the binding becomes inseparable and hence you cannot assign any other memory address to the pointer variable after defining it.

Lets say your code looked like this,

const char *ptr ="example";
ptr[1] = 'x';

Now the compilation will fail and you cannot modify the value as this pointer variable is immutable.

You should use char pointer only to access the individual character in a string of characters.

If you want to do string manipulations then I suggest you declare an int to store each character's ASCII values from the standard input output like mentioned here,

#include<stdio.h>
int main()
{
    int countBlank=0,countTab=0,countNewLine=0,c;
    while((c=getchar())!=EOF)
    {
        if(c==' ')
            ++countBlank;
        else if(c=='\t')
            ++countTab;
        else if(c=='\n')
            ++countNewLine;
        putchar(c);
    }
    printf("Blanks = %d\nTabs = %d\nNew Lines = %d",countBlank,countTab,countNewLine);
}

See how the integer takes ASCII values in order to get and print individual characters using getchar() and putchar().

A special thanks to Keith Thompson here learnt some useful things today.

Vignesh Vedavyas
  • 369
  • 2
  • 10
  • String literals (not character literals) in C++ are of type `const char[N]`, not `const char*`, where `N` is the length of the literal plus 1 for the terminating `'\0'`. – Keith Thompson Apr 21 '16 at 01:50
  • @KeithThompson There was a C tag in the question. so I go by my const char * – Vignesh Vedavyas Apr 21 '16 at 02:20
  • 1
    In C, string literals are of type `char[N]`. In C++, they're of type `const char[N]`. In both cases, `N` is the length plus 1. Try printing the value of `sizeof "hello, world"`; it's the size of the array, not the size of a pointer. – Keith Thompson Apr 21 '16 at 02:22
  • @Keith I intended to point out that const char * -the type of string arrays or objects of C and C++ respectively prevents the asker from modifying by using myPtr[1] = 'x'; If you feel the need to correct then perhaps you could start from here, http://stackoverflow.com/questions/4949254/const-char-const-versus-const-char – Vignesh Vedavyas Apr 21 '16 at 02:37
  • String literals (not "character literals" as you wrote) are of array type, not of pointer type. Arrays are not pointers. Also, "declare an int to point to each character's ASCII values" doesn't make sense; an `int` doesn't point to anything. – Keith Thompson Apr 21 '16 at 02:40
  • alright I poorly worded it. int a; a='a'; printf("%d",a); gets me 97 the ASCII of 'a'. – Vignesh Vedavyas Apr 21 '16 at 02:45
  • 1
    Either a pointer object or an array object can be stored on the stack (strictly speaking: in an object with automatic storage duration), on the heap (strictly speaking: in an object with allocated storage duration), or in static data. The distinction you make in your first paragraph doesn't really exist. – Keith Thompson Apr 21 '16 at 15:35
1

It compiles according to §5.2.1/1 [expr.sub] of the C++ standard:

A postfix expression followed by an expression in square brackets is a postfix expression. One of the expressions shall have the type “array of T” or “pointer to T” and the other shall have unscoped enumeration or integral type. The result is of type “T”. The type “T” shall be a completely-defined object type.

The expression E1[E2] is identical (by definition) to *((E1)+(E2)), except that in the case of an array operand, the result is an lvalue if that operand is an lvalue and an xvalue otherwise.

Since "example" has type char const[8] it may decay to char const* (it used to decay to char* as well, but it's mostly a relict of the past) which makes it a pointer.

At which point the expression myPtr[1] becomes *(myPtr + 1) which is well defined.

Community
  • 1
  • 1
Shoe
  • 74,840
  • 36
  • 166
  • 272
0

The most important thing to remember is this:

Arrays are not pointers.

But there are several language rules in both C and C++ that can make it seem as if they're the same thing. There are contexts in which an expression of array type or an expression of pointer type is legal. In those contexts, the expression of array type is implicitly converted to yield a pointer to the array's initial element.

char an_array[] = "hello";
const char *a_pointer = "goodbye";

an_array is an array object, of type char[6]. The string literal "hello" is used to initialize it.

a_pointer is a pointer object, of type const char*. You need the const because the string literal used to initialize it is read-only.

When an expression of array type (usually the name of an array object) appears in an expression, it is usually implicitly converted to a pointer to its initial (0th) element. So, for example, we can write:

char *ptr = an_array;

an_array is an array expression; it's implicitly converted to a char* pointer. The above is exactly equivalent to:

char *ptr = &(an_array[0]); // parentheses just for emphasis

There are 3 contexts in which an array expression is not converted to a pointer value:

  1. When it's the operand of the sizeof operator. sizeof an_array yields the size of the array, not the size of a pointer.

  2. When it's the operand of the unary & operator. &an_array yields the address of the entire array object, not the address of some (nonexistent) char* pointer object. It's of type "pointer to array of 6 chars", or char (*)[6].

  3. When it's a string literal used as an initializer for an array object. In the example above:
    char an_array[] = "hello";
    the contents of the string literal "hello" are copied into an_array; it doesn't decay to a pointer.

Finally, there's one more language rule that can make it seem as if arrays were "really" pointer: a parameter defined with an array type is adjusted so that it's really of pointer type. You can define a function like:

void func(char param[10]);

and it really means:

void func(char *param);

The 10 is silently ignored.

The [] indexing operator requires two operands, a pointer and an integer. The pointer must point to an element of an array object. (A standalone object is treated as a 1-element array.) The expression

arr[i]

is by definition equivalent to

*(arr + i)

Adding an integer to a pointer value yields a new pointer that's advanced i elements forward in the array.

Section 6 of the comp.lang.c FAQ has an excellent explanation of all this stuff. (It applies to C++ as well as to C; the two languages have very similar rules in this area.)

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
0

In C++, your code generates a warning during compile:

{
  //char* myPtr = "example";  // ISO C++ forbids converting a string 
                              // constant to ‘char*’ [-Wpedantic]

  // instead you should use the following form
  char myPtr[] = "example";  // a c-style null terminated string

  // the myPtr symbol is also treated as a char*, and not a const char*

  myPtr[1] = 'k';  // still works,  

  std::cout << myPtr << std::endl;  // output is 'ekample'
}

On the other hand, std::string is much more flexible, and has many more features:

 {
   std::string myPtr = "example";

   myPtr[1] = 'k';  // works the same

   // then, to print the corresponding null terminated c-style string
   std::cout << myPtr.c_str() << std::endl;

   //  ".c_str()" is useful to create input to system calls requiring
   //   null terminated c-style strings
 }
2785528
  • 5,438
  • 2
  • 18
  • 20
-1

The semantics of abc[x] is "Add x*sizeof(type)" to abc where abc is any memory pointer. Arrays variable behave like memory pointers and they just point to beginning of the memory location allocated to array.

Hence adding x to array or pointer variable both will point to memory which is same as variable pointing to + x*sizeof(type which array contains or pointer points to, e.g. in case of int pointers or int array it's 4)

Array variables are not same as pointer as said in comment by Keith as array declaration will create fix sized memory block and any arithmetic on that will use size of array not the element types in that array.

user2224280
  • 29
  • 1
  • 7
  • Nice. Good explanation. Thanks a lot. – Luiz Heringer Apr 21 '16 at 01:36
  • "*Arrays variable behave like memory pointers and they just point to beginning of the memory location allocated to array.*". No. Arrays and pointers are entirely distinct things. Please take a look at my answer and at section 6 of the [comp.lang.c FAQ](http://www.c-faq.com/). – Keith Thompson Apr 21 '16 at 20:01
  • Do you understand difference between **behave like** and **being distinct things** ? – user2224280 Apr 21 '16 at 22:17
  • Yes, I do. Arrays don't behave like pointers. An array expression is, in most contexts, implicitly converted to a pointer to the array's initial element. The resulting pointer behaves like a pointer. – Keith Thompson Apr 21 '16 at 23:54
  • And what am I saying **behave like** or **are same** ? – user2224280 Apr 22 '16 at 00:16