1

I would like some help with pointer of strings and integers. If I intend to change the value of integers defined in the main function, it is necessary to pass the pointer of the variables otherwise nothing will be changed like: (a and b will swap to each other's original value)

 void swapping(int *a,int *b){
    int temp=0;
    temp= *a;
    *a=*b;
    *b=temp;

}

int main(){
    int a=1,b=2;
    swapping(&a,&b);
    printf("%d\n%d\n",a,b );
    return 0;
}

However, when I continue on passing strings(char arrays), operations like this is feasible:

void split(char take1[],char take2[], char str[]){
    int i=0,j=0,yes=0;
    while(str[i]!='\0'){
        if(str[i]=='*'){
            yes=1;
            i++;
        }
        if(yes==0){
            take1[i]=str[i];
        }
        else if (yes!=0){
            take2[j]=str[i];
            j++;
        }
        i++;
    }
}
int main(){
    char taker1[30],taker2[30];
    char str[30]="Hello*world";
    split(taker1,taker2,str);
    printf("%s\n%s\n",taker1,taker2) ;
    return 0;
}

My shallow understanding is because functions that get called are temporarily stored in RAM, so the value reassigned in the function will be removed once the function call is finished. Thus, we need to change the value of pointer in the memory.

But I didn't get why there is no need to pass the pointer of the char arrays as in second example to the function(split()) to alter their values . Could someone please help to see why? Thanks!

  • 3
    *"But I didn't get why there is no need to pass the pointer of the char arrays"* -- If you pass an array, it decays into a pointer to the first element. So it is actually passing a pointer. And the `char take1[]` parameter is just another way of writing `char *take1` – Blaze Feb 13 '20 at 09:37
  • For any array or pointer `p` and index `i`, the expression `p[i]` is exactly equal to `*(p + i)`. In your `swapping` function when you use e.g. `*a` that's really the same as `*(a + 0)` which is equal to `a[0]`. – Some programmer dude Feb 13 '20 at 09:38
  • 1
    And as mentioned, arrays *decay* to pointers to their first element. So your call `split(taker1,taker2,str)` is really the same as `split(&taker1[0], &taker2[0], &str[0])` – Some programmer dude Feb 13 '20 at 09:40
  • ahhhh many thanks to Blaze and Some programmer dude!! I get it. – NOOB_developer Feb 13 '20 at 09:42
  • @Peter123 As the others have rightfully pointed out, **array variables** are intrinsical **pointers** to the memory address of the first element of the array, also known as the base address. But it is important to notice that terms *array variables* and *arrays* are different. An array is the actual contiguous block of memory storing multiple elements of the same type. And an array variable just stores the base address of that contiguous block. Refer to my answer below for better understanding. – Akash Das Feb 13 '20 at 10:22

3 Answers3

2

(OP) But I didn't get why there is no need to pass the pointer of the char arrays as in second example to the function(split()) to alter their values .

With many operations1, arrays are converted to the pointer of the first element. That happened with

split(taker1,taker2,str);

Let's dig deeper.


The C standard library defines string

A string is a contiguous sequence of characters terminated by and including the first null character. C17dr § 7.1.1 1

char array str below contains a string.

char str[30]="Hello*world";

char arrays taker1, taker2 are uninitialized. They do not certainly contain a string.

char taker1[30],taker2[30];

(OP) I continue on passing strings(char arrays),

Not quite. Below, char array taker1 is converted to the address of the first element when passed to the function. Like-wise for taker2, str

split(taker1, taker2, str);

split() receives 3 pointers, even though it may look like arrays.

void split(char take1[],char take2[], char str[]){
// same as 
void split(char *take1, char *take2, char *str) {

The body of split() then uses these pointers to manipulate data. Recall these pointers point to main's str[], taker1[], taker2[]. When splt() is done, printf("%s\n%s\n", taker1, taker2) ; shows the effect.


1 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. C17dr

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
0

char take[] is essentially the same as char *take. So you actually passing the pointer.

In char taker[30] case for example taker itself is a pointer and taker[n] is equivalent to *(taker + n).

Eraklon
  • 4,206
  • 2
  • 13
  • 29
  • 2
    Not totally wrong, but you should insist that a pointer and an array are different animals. What happens is that an array **decays** to a pointer to its first element when used – Serge Ballesta Feb 13 '20 at 11:07
0

EXPLANATION

This is because, in C, an array declaration decays to a pointer internally (Refer to the citation at the end for details). When you declare char a[], it is the same as declaring char* a. In both of these cases, a stores the memory address of the first element of the array. But, in the case of variables like plain integers or characters, such as int x = 10;, the variable x will actually store the value 10.

When you declare an array such as

char a[10];

the object designated by the expression a is an array (i.e., a contiguous block of memory large enough to hold 10 character values a.k.a string), and the type of the expression a is an "array of 10 character elements", or char[10]. The expression a is implicitly converted to char *, and its value is the address of the first element.

Thus, when you pass an array variable to a function, you are actually passing the memory address (or base address) of the array. And since you have written your function declaration as:

void split(char take1[],char take2[], char str[])

It is the same as writing:

void split(char *take1,char *take2, char *str)

And, in your function call which is:

split(taker1,taker2,str);

taker1, taker2 and str actually contain base addresses of the respective character arrays (i.e. string). So you don't explicitly have to mention the address_of operator (&) along with the array variables in the function call.

The code you have posted can also be written as follows:

void split(char *take1,char *take2, char *str){
    int i=0,j=0,yes=0;
    while(*(str+i) != '\0'){
        if(*(str+i) == '*'){
            yes=1;
            i++;
        }
        if(yes==0){
            *(take1+i) = *(str+i);
        }
        else if (yes!=0){
            *(take2+i) = *(str+i);
            j++;
        }
        i++;
    }
}
int main(){
    char taker1[30], taker2[30];
    char str[30] = "Hello*world";
    split(taker1, taker2, str);
    printf("%s\n%s\n", taker1, taker2) ;
    return 0;
}

Notice the interchanged array operator([]) and and dereference operator(*). Hint: Writing arr[5] is the same as *(arr + 5).

LONG STORY SHORT:

  • In C, arrays are passed by reference. Normal variables are passed by value.
  • Array variables can be treated as pointers.
  • You ought to normally skip the & with array variables in function calls.

BONUS

The aforementioned reason is also why we don't use & in scanf() for string variables (with %s format specifier), i.e.,

char str[10];
scanf("%s", str);

But in the case of integers or other primaries:

int num;
scanf("%d", &num);

Also, you will get a better understanding of the concepts involved after going through dynamic memory allocation in C.

CITATION

Here's the exact language from the C standard (n1256):

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.

Akash Das
  • 163
  • 15
  • 3
    An array declaration is not a pointer internally. An array *parameter* declaration is a pointer (not internally, it just is one). – user253751 Feb 13 '20 at 12:31
  • @chux-ReinstateMonica An array in C *usually* **decays** to a pointer internally. I acknowledge what you mean is right. But that's a discussion for another question - https://stackoverflow.com/questions/1641957/is-an-array-name-a-pointer. – Akash Das Feb 14 '20 at 07:23
  • @chux-ReinstateMonica For your counterexample regarding `sizeof a`, please refer to the documentation I have cited in my answer in the last part. It clearly states that it happens for cases **other than `sizeof`** - http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf – Akash Das Feb 14 '20 at 07:28
  • @chux-ReinstateMonica I have amended that. – Akash Das Feb 17 '20 at 06:18