Calling by reference without explicitly declaring function parameters as pointers? - c

I'm reading K&R and have a problem here. I don't know how this function is changing the value of the calling variable. Shouldn't this be call by value, not call by reference since to[] and from[] aren't explicity declared as pointers? The value of foo is changed to "Testing".
#include <stdio.h>
void copy(char to[], char from[]) {
int i = 0;
while ((to[i] = from[i]) != '\0') i++;
}
int main() {
char foo[] = "";
char bar[] = "Testing";
copy(foo, bar);
printf("%s\n", foo);
return 0;
}
Also, why isn't this arr_alter function changing the value of test_arr? It seems that looping through each element in the array changes the value of the calling variable yet this does not.
#include <stdio.h>
void arr_alter(char arr[]) {
arr = "Changed";
}
int main() {
char test_arr[] = "Testing";
arr_alter(test_arr);
printf("%s\n", test_arr);
return 0;
}
In short, why is function #1 treating the arguments as pointers while function #2 is not?
I'm a bit confused, all help would be greatly appreciated.

Arrays are passed as pointers, by value.
You can take
void copy(char to[], char from[])
as equivalent to
void copy(char * to, char * from)
The difference between the two cases is that the first case dereferences the pointer with the array element operator [].
The second case does not do this, it overwrites the passed pointer. But the pointer was passed by value, so neither the array content nor the passed pointer of the caller change.

First of all the first program has undefined behaviour.
Array foo is defined as having only one element (the terminating zero of the "empty" string literal)
char foo[] = "";
However you are going to copy string "Testing" in the array.
copy(foo, bar);
This results in overwritting the memory that does not belong to array foo.
Array foo should be enough large to be able to accomodate string "Testing".
For example
char foo[8];
According to the C Standard (6.7.6.3 Function declarators (including prototypes))
7 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....
On the other (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.
Thus in this function declaration
void copy(char to[], char from[]);
parameters to and from are adjusted to pointers. So this function declaration is equivalent to the following function declaration
void copy(char *to, char *from);
and the both declare the same one function.
You may write in the program the both declarations. For example
#include <stdio.h>
void copy(char to[], char from[]);
void copy(char *to, char *from);
void copy(char to[], char from[]) {
int i = 0;
while ((to[i] = from[i]) != '\0') i++;
}
//...
but the definition of the function shall be only one.
And in this function call
copy(foo, bar);
according to the second quote from the Standard arrays foo and bar are converted to pointers to their first elements.
As for the function from the second program then function parameters are its local variables. A function deals with copies of its arguments. So any changes of a copy of an argument do not influence on the argument itself.
You can imagine the definition of function arr_alter and its call the following way
arr_alter(test_arr);
//...
void arr_alter( /*char arr[] */) {
char *arr = test_arr;
arr = "Changed";
}
After exiting the function its local variable arr will be destroyed. Variable test_arr will not be changed and moreover arrays have no the assignment operator. You can not reassign an array such a way.

char to[] and char from[] is the equivalent to assigning a char ptr to the first element of the array.
Say you had char s[10]= "hello";
print(s);
Is the same as
print(&s[0]);
The address of the first slot in the array.

Related

How is a string declared as a parameter to a function in c?

I wanted to know in what ways a string can be passed to a function and what is the corresponding syntax of the function declaration.
In C, a string is a sequence of character values including a zero-valued terminator - the string "hello" is represented as the sequence {'h', 'e', 'l','l', 'o', 0}.
Strings (including string literals) are stored in arrays of character type:
char str[] = "hello";
Except when it is the operand of the sizeof or unary & operators, or is a string literal used to initialize a character array in a declaration (like above), an expression of type "N-element array of T" will be converted ("decay") to an expression of type "pointer to T" and the value of the expression will be the address of the first element of the array.
So if you call a function like
foo( str );
what the function actually receives is a char *, not an array of char, so the prototype will be
void foo( char * ); // declaration
void foo( char *str ) { ... } // definition
In the context of a function parameter declaration, T a[N] and T a[] are adjusted to T *a, so you could also write the prototype as
void foo( char str[] )
or
void foo( char str[N] )
but they'll both be interpreted as
void foo( char *str )
In C there is no type called string, it is an array of char. You can pass it to the function by passing the address of the first element and the size of the array or you will depend on the terminating character '\n'.
To define a string
char str[] = "A String";
the above line is stored in memory as:
pass it to a function
To pass the array the function prototype must be as:
returnType funName(char * str);
note that returnType can be any type
Function Call will be like:
funName(&str[0]); or funName(str);
Manipulation inside the function (suppose you want to print the string character by character)
returnType funName(char * str) {
uint8_t loopIndex = 0;
while(str[loopIndex] != '\n') {
printf("%c", str[loopIndex]);
}
return whatEver;
}
note that you can pass the size of the array to the function as a parameter
In C, strings are nothing but an array of characters. Below is the example
void myfun(char *str) { // collecting in a pointer
printf("%s", str);
}
int main() {
char str[] = "Independance" ;
myfun(str); // Passing address of an array
return 0;
}
In C, strings aren't passed by value to a function. They are passed by reference through a pointer to char.
Related:
What's the difference between passing by reference vs. passing by value?
Function declaration:
void foo (char *p_s);
p_s is a pointer to char.
If you don't want to modify the string passed by reference you can define p_s as const char * p_s. If you don't want the pointer to be changed to point to anywhere else except the beginning of the string in the caller, you can define p_s as char * const p_s. If you want both you can declare p_s as char const * const p_s.
Function call:
const char s[] = "Hello World!";
foo(s);

Are array names in function arguments treated differently than arrays declared locally (auto)

Please read the comments in the program below :
#include<stdio.h>
void test(char c[])
{
c=c+2; //why does this work ?
c--;
printf("%c",*c);
}
int main()
{
char ch[5]={'p','o','u','r'};
//ch = ch+2; //this is definitely not allowed on array names as they are not pointers
test(ch);
return 0;
}
OUTPUT
o
You should keep in mind that the name of the array "decays" to a pointer to its first element. This means that test(ch); is equivalent to test(&ch[0]);.
Also, void test(char c[]) is nothing but void test(char* c), a pointer to a character. Pointers can be incremented or decremented which is why c = c + 2 and c-- compiles just fine.
Array designators are immutable lvalues. That is you may not change an array designator such a way that ir would define another array.
Consider array designators as named memory extents.
As for your example then this function declaration
void test(char c[]);
is adjusted by the compiler the following way
void test(char *c);
that is a parameter having an array type is adjusted by the compiler to pointer. Thus for example these function declarations
void test(char c[100]);
void test(char c[10]);
void test(char c[1]);
void test(char c[]);
are equivalent and declare this one function
void test(char *c);
You nay include all these declarations in your program though they will be redundant.
For example
#include <stdio.h>
void test(char c[100]);
void test(char c[10]);
void test(char c[1]);
void test(char c[]);
void test( char *c)
{
c=c+2;
c--;
printf("%c",*c);
}
int main( void )
{
char ch[5]={'p','o','u','r'};
test(ch);
}
To make it evident consider the following program
#include <stdio.h>
void test( char c[] )
{
printf( "sizeof( c ) = %zu\n", sizeof( c ) );
}
int main( void )
{
char ch[5]={'p','o','u','r'};
test( ch );
printf( "sizeof( ch ) = %zu\n", sizeof( ch ) );
}
Its output is
sizeof( c ) = 8
sizeof( ch ) = 5
That is within the function sizeof( c ) is equal to the size of a pointer (in used system it is equal to 8). While in main sizeof( ch ) is the size of the array.
When you pass an array to such a function then the array designator is implicitly converted to pointer to its first element. So these calls
test( ch );
test( &ch[0] );
are equivalent.
This means that within the function you deal with a pointer and you can change the value of the pointer using the pointer arithmetic.
When an array is passed as function argument (among other cases), it decays to a pointer to the first element, and the function parameter which receives it is local to the function scope.
Quoting C11, chapter §6.3.2.1
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. [...]
So, in your case, inside void test(char c[]) function call, c is just another pointer, which points to the first element of the array. Normal pointer arithmetic can be performed on that pointer.
In other words,
void test(char c[]) { //....
is same as
void test(char *c) { //....
So, you case is something similar to
int main(void) //correcting the definition
{
char ch[5]={'p','o','u','r'};
//ch = ch+2; //this is definitely not allowed on array names as they are not pointers
char *c = &ch[0]; // this is what happens when you pass the array as function argument.
c = c + 2; // see, this is possible.
test(ch);
return 0;
}
In declarations of function parameters, an array declaration is automatically adjusted to be a pointer declaration, per C 2018 6.7.6.3 7:
A declaration of a parameter as “array of type” shall be adjusted to “qualified pointer to type”,…
Thus void test(char c[]) is effectively void test(char *c).
In main, ch is an array, because it was declared with char ch[5]…, which is a normal declaration that is not adjusted. In test, c is a pointer.
When main calls test with test(ch), the argument ch is an expression. In an expression, an array is automatically converted to a pointer in most cases because C 2018 6.3.2 3 says:
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…
Thus, when an array is passed to a function with a parameter declared as an array, the array is converted to a pointer and is passed for a parameter that was adjusted to be a pointer.
Note that only an outer array is adjusted. If the function parameter declaration is int x[3][4], it is adjusted to be int (*x)[4], a pointer to an array of 4 int. Only the array that is the parameter (the array of 3 arrays of 4 int above) is adjusted; other types within its composition are not adjusted.
Aside
The C standard is not entirely clear about the effects of the adjustment. Using Apple LLVM 10.0.1 with clang-1001.0.46.4, the following program prints “Hello, world.”:
#include <stdio.h>
static void foo(int a[printf("Hello, world.\n")]) {}
int main(void) { foo(0); }
This shows the array declaration was not completely adjusted to be a pointer declaration, as the expression specifying the array size was retained but would not be present in a pointer declaration.

C function change string using pointer

I'm trying to make a function that changes a char array from the main function, that's what I'm trying to do:
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>
void change(char *a);
int main()
{
char a[] = "hello";
printf("\na = %s", a);
change(a);
printf("%\na = %s", a);
getch();
}
void change(char *a)
{
a = "goodbye";
}
Several problems with this code, but first we need to take a step back and talk about how arrays are handled in C.
Except when it is the operand of the sizeof or unary & operators, or is a string literal used to initialize another array in a declaration, an expression of type "N-element array of T" will be converted ("decay") to an expression of type "pointer to T", and the value of the expression will be the address of the first element of the array.
In the declaration
char a[] = "hello";
"hello" is a string literal, which has type "6-element array of char" (5 characters plus the 0 terminator). Since it is being used to initialize the array a in a declaration, the rule above doesn't apply; instead, the size of a is set to be the same as the size of the literal (6), and the contents of the string literal are copied to the array.
When you call change from main as
change(a);
the expression a has type "6-element array of char". Since it is neither a string literal nor the operand of the sizeof or unary & operators, that expression will be converted to type "pointer to char", and the value of the expression will be the address of the first element of the aray. Hence why the change function is declared as
void change(char *a);
In this context, a is simply a pointer. When you write
a = "goodbye";
the string literal "goodbye" is not being used in an initializer, and it's not the operand of the sizeof or unary & operators, so the expression is converted to type "pointer to char", and the value of the expression is the address of the first character. So what happens here is that you're copying the address of the string literal "goodbye" to a. This overwrites the value in a, but this a is not the same object in memory as the array a in main, so any changes to it are not reflected in main.
If you want to update the contents of an array, you will need to use the library functions strcpy/strncpy (for 0-terminated strings) or memcpy (for everything else), or update each element explicitly (a[0]='g'; a[1]='o'; a[2]='o';, etc).
To update the contents of a, you'd use
strcpy( a, "goodbye" );
Except...
a is only large enough to hold 5 characters plus a 0 terminator; "goodbye" is 7 characters plus the 0 terminator; it's two characters larger than what a is capable of storing. C will happliy let you perform the operation and trash the bytes immediately following a, which may lead to any number of problems (buffer overruns such as this are a classic malware exploit). You have a couple of choices at this juncture:
First, you could declare a to be large enough to handle either string:
#define MAX_LEN 10
...
char a[MAX_LEN] = "hello";
Second, you could limit the size of the string copied to a:
void change( char *a, size_t size )
{
strncpy( a, "goodbye", size - 1 );
a[size - 1] = 0;
}
Note that you will need to pass the number of elements a can store as a separate parameter when you call change; there's no way to tell from a pointer how big the array it points to is:
change( a, sizeof a / sizeof *a ); // although in this case, sizeof a would be
// sufficient.
The main problem is that you are sending a copy of the char pointer a by doing this:
void change(char *a)
{
a = "goodbye";
}
if you want to change a value in another function you should do this:
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>
void change(char **a);
int main()
{
char *a = "hello";
printf("\na = %s", a);
change(&a);
printf("%\na = %s", a);
getch();
}
void change(char **a)
{
*a = "goodbye";
}
I changed the function, now it works, this way:
void change(char *a)
{
strcpy(a, "goodbye");
}

doubts about incrementation of array pointer

Here I'm getting the output 'bcde' but since 'a' is a constant array pointer it should not have been incremented inside fun() right? Then why is it allowed here?
void fun(char a[])
{
a++;
printf("%s",a);
}
void main()
{
char a[]="abcde";
fun(a);
}
When you pass an array to a function in C, the array "decays" to a pointer. In other words, an equivalent function can be declared like this:
void fun(char *a)
Now the code inside the function makes perfect sense: the pointer is incremented, so when the result is passed to printf, the original string is printed starting with the second letter.
When you pass an array name to your function then you are passing pointer to its first element.
void fun(char a[]) // a is not an array of char
is equivalent to
void fun(char *a)
You can modify a inside function because it is not an array name but a pointer to char.
You can't modify a in main as it is declared as array. Array names are non modifiable l-values.
When you are in function arguments, char a[] is treated exactly as if you wrote char *a.
There is no any "constant array pointer"
In this statement
char a[]="abcde";
there is declared a non-const array. You may change it for example as
a[0] = 'A';
When you pass the array to the function as an argument then there is used conversion from the array to a pointer of type char * that points to the first element of the array.
This function declaration
void fun(char a[]);
is equivalent to
void fun(char *a);
The parameter is adjaced to the pointer.
So inside the function you can to change the pointer itself and the object it points to. For example
a++;
*a = 'B';
If you want that the pointer would not be changed in the function you could declare it as
void fun(char * const a);
In this case the compiler would issue an error for statement
a++;
because a is indeed a const pointer.

about pointer array add itself(++)

I don't understand why in the url_split function I can use a++, but in the main function I can't use key_value++, they have the same type...
void url_split(char *src, char **host, char *a[])
{
char const *p1 = "?";
char const *p2 = "&";
*host = strtok(src, p1);
char *str;
while((str = strtok(NULL, p2)))
{
*a = str;
a++;
}
*a = str;
}
int main(int argc, char *argv[])
{
char *host;
char *key_value[100];
char url[] = "http://www.baidu.com/s?wd=linux&cl=3";
url_split(url, &host, key_value);
printf("host = %s\n", host);
while(*key_value)
{
printf("key-value : %s\n", *key_value);
key_value++;
}
return 0;
}
No, they are not actually the same thing: key_value in main is an array that you cannot change (you can change the contents but not the array variable itself).
When you pass it to a function, it becomes a pointer to the first element of that array, which can change (to point to other elements of that array, for example).
It's no different to:
int xyzzy[10]; // xyzzy cannot change
int *plugh = xyzzy; // but plugh can.
This "decay" of arrays to pointers actually happens in the vast majority of cases. From the C11 standard:
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.
They actually don't have the same type:
key_value is of type char *[100], an array of 100 char pointers.
a is actually of type char **, a pointer to a char*, not an array.
When you pass key_value to url_split it decays to a char**, which is why key_value is a valid argument to the function and why you use char** as the type of a function argument that is intended to be an array of char*.
The post-increment operator is incompatible with arrays, an array can't be assigned a new value, but it works perfectly fine for pointers, since they can be assigned a new value. That's why a++ is valid and key_value++ is not.
Here is what is written in Kernighan book about this:
"When an array name is passed to a function, what is passed is the location of the initial element. Within the called function, this argument is a local variable, and so an array name
parameter is a pointer, that is, a variable containing an address."
Which means in main function key_value is an array name, you cannot modify it, since it's just a synonim to the first element in the array. But when passed to function, another pointer is created which points to the same location as first element of array.

Resources