2

As far as I know, you cannot modify a Array variable

How come this code run without any error. Is there anything I am missing out here. (It's not about why there is 'L-VALUE REQUIRED' error , it's about why there isn't.)

    #include<stdio.h>
int strlens(char *s);
void main(){
    char s[]="get me length of this string ";

    // s++ ; this would give 'L-VALUE REQUIRED ERROR'

    printf("%d",strlens(s));    
}
int strlens(char s[]){
    int i;
    for(i=0; *s!='\0';++i, ++s) ; //++s:  there is NO 'L-VALUE REQUIRED ERROR'
    return i;


}
manifold
  • 437
  • 6
  • 23
  • `s` is not an array name in `strlens`, it's a pointer. Arrays and pointers are distinct types. – StoryTeller - Unslander Monica Dec 11 '17 at 07:22
  • *s = value at s.. s = address pointer. so, ++s increments the address. – Vj- Dec 11 '17 at 07:23
  • my question is not about why there is L value required, it's about why there is not – manifold Dec 11 '17 at 07:28
  • @viru - The duplicate has an answer for that. Read beyond the original question. – StoryTeller - Unslander Monica Dec 11 '17 at 07:28
  • Array arguments behave as pointers. They have space on the stack and they hold the address to the array. This is not the case with array names. – Yashas Dec 11 '17 at 07:29
  • @Yashas could you please elaborate on that. – manifold Dec 11 '17 at 07:34
  • 1
    When you do `char s[]`, the `s` is a name (compile time symbol; has no run-time significance). It is not allocated any space and hence does not store an address. If you want to do pointer arithmetic, you need to store the address somewhere, don't you? In case of the function argument, the address of your original character array was pushed onto the stack and your function parameter `s` refers to that. Therefore, the function parameter `s` has space allocated for it where it stores the address. You can now do pointer arithmetic as there is something to which you can do arithmetic on. – Yashas Dec 11 '17 at 07:38
  • 2
    @Yashas: "*Array arguments behave as pointers*": they do not just "*behave as pointers*", they ***are*** pointers. – alk Dec 11 '17 at 08:27
  • Nitpicking: "*the address of your original character array was pushed onto the stack*" should read "the address of your original character array's **1st element** was pushed onto the stack". – alk Dec 11 '17 at 08:29
  • 1
    The error message is misleading. `s` is a lvalue in `main`. It is not a **modifiable** lvalue though. – Antti Haapala -- Слава Україні Dec 11 '17 at 08:47
  • 2
    Just to state this explicitly: `int strlens(char s[]);` **is 100% equivalent** to `int strlens(char * s);` It is interchangeable and the compiler ought to create the *exact* some code for both. – alk Dec 11 '17 at 08:55

3 Answers3

4

A quirk of the C language that is well-known to seasoned C programmers, but trips up new C coders to no end, is that arrays are "pass by reference". Generally speaking, an array name used in most expressions will "decay" to the address of its first element. Functions carry that to an extreme case, where the array syntax in the function parameter is actually an alias for the pointer type itself.

This is described in paragraph 7 of c11's §6.7.6.3 Function declarators:

A declaration of a parameter as ‘‘array of type’’ shall be adjusted to ‘‘qualified pointer to type’’, where the type qualifiers (if any) are those specified within the [ and ] of the array type derivation.


Historically, this quirk was an attempt to maintain behavioral compatibility to C's predecessors, B and BCPL, and efficient structure layout. C's predecessor had a semantic for arrays in that its physical layout was actually a pointer that got dynamically allocated and initialized at runtime. When passed to a procedure, the pointer semantic was a natural adoption. Dennis Ritchie invented the notion of allowing the array syntax represent the actual address of the array, and then maintained the pointer semantic when passed to a function. Thus, the inventor of the C language considered this quirk a novel solution to a real world problem (semantic compatibility).

References: The Development of the C Language

jxh
  • 69,070
  • 8
  • 110
  • 193
  • 1
    *arrays are "pass by reference"* -- you mean the correct thing of course and the standard quote is the relevant one, still I don't like to put it that way -- there's just no "pass by reference" in C, but pointers allow the programmer to kind of "simulate" it. I would therefore better state that arrays just *can't be passed* at all and the rules for expression evaluation and type adjustment (together often called "arrays *decay* to pointers", which isn't in the standard either) help to "easily" pass a pointer instead. –  Dec 11 '17 at 08:24
  • I think the more precise terms would be pass by pointer/by address. – MikeMB Dec 11 '17 at 08:53
  • @FelixPalmen: The quotes are meant to impart that I am not using the term literally. However, the notion of "pointers are references" was imparted to me from the Ritchie's paper *The Development of the C Language*. *Decay* was a term I picked up as I was becoming proficient in C. I probably saw it in the C-faq. It should have been in quotes too, and I have made that edit. – jxh Dec 11 '17 at 08:53
  • Agree with @FelixPalmen Arrays are not pass by reference. If they were, it would imply that changing the "array reference" in the callee would change the array in the caller. i.e. the array would actually be a different location in memory. The correct point is that arrays are not passed at all. – JeremyP Dec 11 '17 at 09:05
  • @JeremyP: That interpretation (*the array would actually be a different location in memory*) is not the usual interpretation of what changing a function argument passed by reference would mean. You are conflating the pointer argument as if the pointer object itself is what is passed by reference. Dennis Ritchie was attempting to simulate pass by reference for arrays to avoid stack burden. – jxh Dec 11 '17 at 09:09
  • No. My interpretation of pass by reference is correct. Changing a thing passed if it is pass by reference changes the thing in the caller as well as the callee. This is why we say that C does not have pass by reference. It is you who is conflating pass by reference and passing pointers by value. – JeremyP Dec 11 '17 at 09:13
  • @JeremyP: If a pointer was passed by reference, it could point to a different thing after the function call. That is because the pointer object itself got a different value. If an array object gets a different value, it just means array contents changed, not that the array became a totally different array in a different memory location. – jxh Dec 11 '17 at 09:16
  • "If a pointer was passed by reference, it could point to a different thing after the function call." Yes. Exactly. "If an array object gets a different value, it just means array contents changed" No. The array would be a different array. Changing the content of an array is not the same as changing the array. Fortunately, these subtleties don't matter in C because you can't pass an array at all. – JeremyP Dec 11 '17 at 09:19
  • @JeremyP: I don't believe you are interpreting an array reference the same way that most people would. Consider that "by reference" in C is usually emulated by a pointer to an object. You are allowed to define a function parameter to be a pointer to an array. This does not mean you are allowed to change the array that had its address passed in to refer to a totally different array object. Just as passing in the address of an `int` variable to a function does not allow the function to make the variable refer to a entirely different `int` object. – jxh Dec 11 '17 at 09:29
  • @JeremyP: Feel free to [chat](https://chat.stackoverflow.com/rooms/160914/discussion-between-jxh-and-jeremyp). – jxh Dec 11 '17 at 09:40
2

This line

char s[]="get me length of this string ";

defines a character array. s is not a pointer, it evaluates to an address (when fed to a pointer for instance, or when accessing a value like s[i] equivalent to *(s+i)), or represents the space occupied by the array (eg in sizeof(s))

But in a function signature like this

int strlens(char s[]){

char s[] is equivalent to char *s, and you can treat s like a pointer.

Déjà vu
  • 28,223
  • 6
  • 72
  • 100
2

char arr[] = "asds"

Here, arr is just a name. It refers to a memory location but is not a pointer. The compiler substitutes the address directly wherever arr is used. It is not a pointer because unlike pointers, it does not have any space allocated to store an address. It is a mere compile time symbol. Hence, at run-time there is nothing on which you can do pointer arithmetic on. If you had to increment something, that something should exist at run-time.

More details:

Basically, the literal "asds" is stored in your executable and the compiler knows where exactly it is (well, the compiler is placing it in the executable, so it should know?).

The identifier arr is just a name to that location. As in, arr is not a pointer, i.e: it does not exist in memory storing an address.


void func(char arr[])

In case of a function argument, the arr does exist in memory at run-time because the arguments are pushed onto the call stack before making the function call. Since arrays are passed by reference, the address of the first element of the actual parameter is pushed onto the call stack.

Therefore, arr is allocated some space on the stack where it stores the address to the first element of your actual array.

Now you have a pointer. Hence you can increment (or do any pointer arithmetic on it).

Yashas
  • 1,154
  • 1
  • 12
  • 34