2

So, I am starting to familiarize with C, and at this point I'm trying to understand pointers. I got the following code from here, but I cannot comprehend, how does one subtract a character array from a pointer.

#include<stdio.h>
#include<string.h>
#include<conio.h>

main() 
{   
char s[30], t[20];   
char *found; 

/* Entering the main string */   
puts("Enter the first string: ");   
gets(s);

/* Entering the string whose position or index to be displayed */   
puts("Enter the string to be searched: ");   
gets(t);

/*Searching string t in string s */   
found=strstr(s,t);   
if(found)
    printf("Second String is found in the First String at %d position.\n",found-s);    
else
    printf("-1");   
getch(); 
}

Isn't the pointer only the address of a given variable/constant? When the subtraction happens the character array automatically assumes that since the operation happens with a pointer is subtracts its address? I am a bit confused here.

Thanks in advance.

Marco Bonelli
  • 63,369
  • 21
  • 118
  • 128
David Serb
  • 31
  • 7
  • First of all, never *ever* use `gets`. It's [a dangerous function](https://stackoverflow.com/questions/1694036/why-is-the-gets-function-so-dangerous-that-it-should-not-be-used) that have even been removed from the C specification many years ago. Use e.g. [fgets`](https://en.cppreference.com/w/c/io/fgets) instead. – Some programmer dude Feb 06 '20 at 13:02
  • 1
    Because array variable holds in memory first array element address. And when subtracting two addresses in memory you get number of positions in between of those two – Agnius Vasiliauskas Feb 06 '20 at 13:05
  • I read about the usage of gets, and don't plan on using it, but I wanted to show the example unchanged from the source – David Serb Feb 06 '20 at 13:09
  • I get it now, thank you, have not thought about that :) – David Serb Feb 06 '20 at 13:09
  • Note that the value of `found - s` is of type `ptrdiff_t`. The proper format specifier for a `ptrdiff_t` is `%td`, and not just `%d`. See https://stackoverflow.com/questions/7954439/c-which-character-should-be-used-for-ptrdiff-t-in-printf Using the wrong format specifier is a form of [undefined behavior](https://en.wikipedia.org/wiki/Undefined_behavior). – Andrew Henle Feb 06 '20 at 13:13
  • @AgniusVasiliauskas: Array variables do not hold in memory the first array element address. With an array object, the compiler knows the address, and, when the object is used in an expression where it is automatically converted to the address of its first element, the compiler constructs that address as necessary (for example, by adding an offset to some base address). It does not generally load it from memory. – Eric Postpischil Feb 06 '20 at 13:14
  • Avoid `conio.h`. It's a non standard header to an ancient compiler. – klutt Feb 06 '20 at 13:14
  • As a new user, please take the [tour] and read [ask]. Concerning your question, `conio.h` shows that whatever resource you are learning from is probably bad. That header is both obsolete, non-standard and non-portable. Maybe you should try to find a good tutorial first. Further, considering the topic of your question, I don't even know what you would expect as result from such an operation. – Ulrich Eckhardt Feb 06 '20 at 13:34
  • Your current source of learning is teaching you 30 years outdated knowledge. You really should replace your current book/teacher with something more up to date. – Lundin Feb 06 '20 at 14:08

4 Answers4

5

Assuming you're wondering about the expression found-s, then what's happening is that you subtract two pointers.

Arrays naturally decay to pointers to their first element. That means plain s is equal to &s[0], which is what's happening here: found-s is equal to found - (&s[0]).

And the subtraction works because found is pointing to an element inside the array s, so the pointers are related (which is a requirement for pointer subtraction). The result is the difference (in elements) between the two pointers.

Marco Bonelli
  • 63,369
  • 21
  • 118
  • 128
Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
3

I cannot comprehend, how does one subtract a character array from a pointer.

Technically, one cannot. But that does not make the code presented invalid.

Isn't the pointer only the address of a given variable/constant?

More or less. A pointer is an address. A valid one is the address of some object or function.

When the subtraction happens the character array automatically assumes that since the operation happens with a pointer is subtracts its address?

Close. With a only a couple of exceptions, values of array type are converted to pointers to the first array element wherever they appear in an expression. Specifically:

Except when it is the operand of the sizeof operator, the _Alignof operator, or the unary & operator, or is a string literal used to initialize an array, an expression that has type ''array of type'' is converted to an expression with type ''pointer to type'' that points to the initial element of the array object and is not an lvalue.

(C2011, 6.3.2.1/3)

Note that the indexing operator, [], is not among the exceptions. Indexing is a pointer operation. Function-call expressions likewise are not exceptions, so you cannot actually pass an array to a function, no matter how much it looks like you are doing so -- instead, you end up passing the corresponding pointer. And most directly to the point, the difference operator, -, is not an exception, so the code in question expresses a difference between two pointers, not between an array and a pointer.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • C 2011 is canceled. The current standard is C 2018, and “the `_Alignof_` operator” text is removed from that passage because it was never possible for an array object to be the operand of `_Alignof`. (`_Alignof` only accepts type operands, not objects.) – Eric Postpischil Feb 06 '20 at 13:16
  • I feel like I more or less fully understand pointers. I wrote some code to make sure I get it, and it is clear. Thank you, did not think it is so simple as asking on this forum :) – David Serb Feb 06 '20 at 13:20
2

The C compiler knows the types you're using and their size so when you do pointer arithmetic, the C compiler can do some intelligent things for you.

For instance, if int *a = 0x10 then a + 1 will give 0x14 not 0x11. The compiler knows that the size of an int is 4 so when you add 1 to the address of an int pointer it gives you the address that the next int sized object that would be.

Likewise when you subtract 2 pointers (i.e. found - s the compiler isn't giving you the number of bytes between the 2 addresses, it's giving you the number of objects between them which in the case of char (size 1) is the same thing.

Michael
  • 810
  • 6
  • 18
1

A character array may not be subtracted from a pointer but one pointer can be subtracted from another pointer if the both pointers point to elements of the same array or one past the last element of the array.

From the C Standard (6.5.6 Additive operators)

9 When two pointers are subtracted, both shall point to elements of the same array object, or one past the last element of the array object; the result is the difference of the subscripts of the two array elements. The size of the result is implementation-defined, and its type (a signed integer type) is ptrdiff_t defined in the header. If the result is not representable in an object of that type, the behavior is undefined. In other words, if the expressions P and Q point to, respectively, the i-th and j-th elements of an array object, the expression (P)-(Q) has the value i−j provided the value fits in an object of type ptrdiff_t. Moreover, if the expression P points either to an element of an array object or one past the last element of an array object, and the expression Q points to the last element of the same array object, the expression ((Q)+1)-(P) has the same value as ((Q)-(P))+1 and as -((P)-((Q)+1)), and has the value zero if the expression P points one past the last element of the array object, even though the expression (Q)+1 does not point to an element of the array object.106

So a question arises: what is s in the expression found-s?

The C Standard answers (6.3.2.1 Lvalues, arrays, and function designators)

3 Except when it is the operand of the sizeof operator or the unary & operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue. If the array object has register storage class, the behavior is undefined.

So in the above pointed expression s is converted to pointer to its first element and in fact the expression can be equivalently rewritten for clarity like

found - &s[0]

But of course knowing this implicit conversion of array designators to pointers it is simpler to write

found - s

The result of the expression is the number of elements of the array between two pointers.

Sometimes beginners make the following error knowing nothing about such a conversion. They write for example

char s[] = "Hello";

if ( s == "Hello" )
{
    // ...do something
} 

However in the condition of the if statement the stored strings are not compared. There are compared addresses of the first element of the array s and of the first element of the string literal. As the array and the literal occupy different extents of memory the result of the condition evaluates to false.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335